提交 9f48e8c6 编写于 作者: R realonezhang

Merge branch 'feature/autotest-reanlone-20220414' into feature/autotest-reanlone-3.6.10

# Conflicts:
#	Android/config.gradle
#	Android/dokit/src/main/res/layout/dk_item_connect_address.xml
#	Android/dokit_module.json
......@@ -10,10 +10,10 @@ import android.widget.SeekBar;
import android.widget.TextView;
import com.didichuxing.doraemondemo.R;
import com.didichuxing.doraemondemo.dokit.SimpleDokitView;
import com.didichuxing.doraemondemo.dokit.SimpleDoKitView;
import com.didichuxing.doraemonkit.kit.lbs.route.FloatGpsRouteMockCache;
public class FloatGpsMockRouteKitView extends SimpleDokitView {
public class FloatGpsMockRouteKitView extends SimpleDoKitView {
public static final String TAG = "FloatGpsMockRoutKitView";
private View mRootView;
private static int sMockSpeed = 10;
......
......@@ -8,7 +8,7 @@ import android.widget.Switch;
import android.widget.TextView;
import com.didichuxing.doraemondemo.R;
import com.didichuxing.doraemondemo.dokit.SimpleDokitView;
import com.didichuxing.doraemondemo.dokit.SimpleDoKitView;
import com.didichuxing.doraemonkit.kit.lbs.common.LocInfo;
import com.didichuxing.doraemonkit.kit.lbs.manual.FloatGpsMockCache;
import com.didichuxing.doraemonkit.kit.lbs.preset.FloatGpsPresetMockCache;
......@@ -17,7 +17,7 @@ import com.google.android.flexbox.FlexboxLayout;
import java.util.ArrayList;
public class FloatGpsPresetMockKitView extends SimpleDokitView {
public class FloatGpsPresetMockKitView extends SimpleDoKitView {
public static final String TAG = "FloatGpsPresetMockKitView";
private View mRootView;
......
......@@ -6,6 +6,9 @@
<dist:module dist:instant="true" />
<uses-permission
android:name="android.permission.READ_FRAME_BUFFER"
tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" /> <!-- tencent Android Q新增权限,允许应用在后台发起定位,如应用target为Q,请添加此权限 -->
......@@ -62,6 +65,12 @@
android:enabled="true"
android:foregroundServiceType="mediaProjection"
tools:targetApi="q" />
<service
android:name=".test.ScreenRecordingService"
android:enabled="true"
android:foregroundServiceType="mediaProjection" />
</application>
</manifest>
......@@ -5,7 +5,7 @@ import android.view.View
import android.widget.CompoundButton
import com.didichuxing.doraemondemo.R
import com.didichuxing.doraemonkit.DoKit
import com.didichuxing.doraemonkit.kit.core.AbsDokitFragment
import com.didichuxing.doraemonkit.kit.core.AbsDoKitFragment
/**
* @Author: changzuozhen
......@@ -13,7 +13,7 @@ import com.didichuxing.doraemonkit.kit.core.AbsDokitFragment
* 切换全屏与否只需要调整继承关系即可
* @see CustomDokitFragment
*
* @see TestSimpleDokitFloatView
* @see TestSimpleDoKitFloatView
*
*
* 悬浮窗,支持折叠
......@@ -24,11 +24,11 @@ import com.didichuxing.doraemonkit.kit.core.AbsDokitFragment
*
* 全屏页面
*
* @see com.didichuxing.doraemonkit.kit.core.AbsDokitFragment
* @see com.didichuxing.doraemonkit.kit.core.AbsDoKitFragment
* 启动工具函数
*
*/
class CustomDokitFragment : AbsDokitFragment() {
class CustomDokitFragment : AbsDoKitFragment() {
override fun onViewCreated(rootView: View?) {
super.onViewCreated(view)
ViewSetupHelper.setupButton(rootView, R.id.test1, "TestSimpleDokitFragment") { v: View? ->
......@@ -63,4 +63,4 @@ class CustomDokitFragment : AbsDokitFragment() {
override fun initTitle(): String {
return "我是自定义页面"
}
}
\ No newline at end of file
}
......@@ -8,8 +8,8 @@ import android.widget.FrameLayout
import android.widget.TextView
import com.didichuxing.doraemondemo.R
import com.didichuxing.doraemonkit.DoKit
import com.didichuxing.doraemonkit.kit.core.AbsDokitView
import com.didichuxing.doraemonkit.kit.core.DokitViewLayoutParams
import com.didichuxing.doraemonkit.kit.core.AbsDoKitView
import com.didichuxing.doraemonkit.kit.core.DoKitViewLayoutParams
/**
* ================================================
......@@ -20,7 +20,7 @@ import com.didichuxing.doraemonkit.kit.core.DokitViewLayoutParams
* 修订历史:
* ================================================
*/
class DemoDokitView : AbsDokitView() {
class DemoDoKitView : AbsDoKitView() {
override fun onCreate(context: Context) {}
override fun onCreateView(context: Context, rootView: FrameLayout): View {
return LayoutInflater.from(context).inflate(R.layout.dk_demo, rootView, false)
......@@ -30,18 +30,18 @@ class DemoDokitView : AbsDokitView() {
val tvClose = findViewById<TextView>(R.id.tv_close)
tvClose?.setOnClickListener {
DoKit.removeFloating(DemoDokitView::class)
DoKit.removeFloating(DemoDoKitView::class)
}
}
override fun initDokitViewLayoutParams(params: DokitViewLayoutParams) {
params.width = DokitViewLayoutParams.WRAP_CONTENT
params.height = DokitViewLayoutParams.WRAP_CONTENT
override fun initDokitViewLayoutParams(params: DoKitViewLayoutParams) {
params.width = DoKitViewLayoutParams.WRAP_CONTENT
params.height = DoKitViewLayoutParams.WRAP_CONTENT
params.gravity = Gravity.TOP or Gravity.LEFT
params.x = 200
params.y = 200
}
}
\ No newline at end of file
}
......@@ -25,11 +25,11 @@ class DemoKit : AbstractKit() {
get() = R.mipmap.dk_sys_info
override fun onClickWithReturn(activity: Activity): Boolean {
DoKit.launchFloating(DemoDokitView::class.java)
DoKit.launchFloating(DemoDoKitView::class.java)
return true
}
override fun onAppInit(context: Context?) {
}
}
\ No newline at end of file
}
......@@ -15,18 +15,18 @@ import android.widget.TextView;
import com.blankj.utilcode.util.ConvertUtils;
import com.didichuxing.doraemondemo.R;
import com.didichuxing.doraemonkit.DoKit;
import com.didichuxing.doraemonkit.kit.core.AbsDokitView;
import com.didichuxing.doraemonkit.kit.core.DokitViewLayoutParams;
import com.didichuxing.doraemonkit.kit.core.AbsDoKitView;
import com.didichuxing.doraemonkit.kit.core.DoKitViewLayoutParams;
/**
* @Author: changzuozhen
* @Date: 2020-12-22
* <p>
* 悬浮窗,支持折叠
* @see SimpleDokitView
* @see SimpleDoKitView
* 启动工具函数
*/
public abstract class SimpleDokitView extends AbsDokitView {
public abstract class SimpleDoKitView extends AbsDoKitView {
private static final String TAG = "SimpleBaseFloatPage";
int mWidth;
int mHeight;
......@@ -92,9 +92,9 @@ public abstract class SimpleDokitView extends AbsDokitView {
@Override
public void initDokitViewLayoutParams(DokitViewLayoutParams params) {
params.width = DokitViewLayoutParams.WRAP_CONTENT;
params.height = DokitViewLayoutParams.WRAP_CONTENT;
public void initDokitViewLayoutParams(DoKitViewLayoutParams params) {
params.width = DoKitViewLayoutParams.WRAP_CONTENT;
params.height = DoKitViewLayoutParams.WRAP_CONTENT;
params.gravity = Gravity.TOP | Gravity.LEFT;
params.x = 200;
params.y = 200;
......@@ -115,4 +115,4 @@ public abstract class SimpleDokitView extends AbsDokitView {
}
}
\ No newline at end of file
}
......@@ -13,18 +13,18 @@ import com.didichuxing.doraemonkit.DoKit
* @Date: 2020-12-22
* 切换全屏与否只需要调整继承关系即可
* @see CustomDokitFragment
* @see TestSimpleDokitFloatView
* @see TestSimpleDoKitFloatView
*
* 悬浮窗,支持折叠
* @see com.didichuxing.doraemonkit.kit.core.SimpleDokitView
* 启动工具函数
*
* 全屏页面
* @see com.didichuxing.doraemonkit.kit.core.AbsDokitFragment
* @see com.didichuxing.doraemonkit.kit.core.AbsDoKitFragment
* 启动工具函数
*
*/
class TestSimpleDokitFloatView : SimpleDokitView() {
class TestSimpleDoKitFloatView : SimpleDoKitView() {
override fun getLayoutId(): Int {
return R.layout.layout_demo_custom
}
......@@ -48,4 +48,4 @@ class TestSimpleDokitFloatView : SimpleDokitView() {
}
}
\ No newline at end of file
}
......@@ -23,10 +23,10 @@ class TestSimpleDokitFloatViewKit : AbstractKit() {
}
override fun onClickWithReturn(activity: Activity): Boolean {
DoKit.launchFloating(TestSimpleDokitFloatView::class.java)
DoKit.launchFloating(TestSimpleDoKitFloatView::class.java)
return true
}
override fun onAppInit(context: Context?) {
}
}
\ No newline at end of file
}
......@@ -13,16 +13,29 @@ import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager.widget.ViewPager
import com.blankj.utilcode.util.ToastUtils
import com.didichuxing.doraemondemo.R
import com.didichuxing.doraemondemo.test.ScreenRecordingTest
import com.didichuxing.doraemonkit.DoKit
import com.didichuxing.doraemonkit.constant.BundleKey
import com.didichuxing.doraemonkit.kit.fileexplorer.ImageDetailFragment
import com.didichuxing.doraemonkit.kit.test.report.ScreenShotManager
import java.io.File
/**
* 一机多控Demo Activity
*/
class MCActivity : AppCompatActivity() {
val TAG = "MCActivity"
companion object {
private const val TAG = "MCActivity"
}
lateinit var mAdapter: RVAdapter
private val screenShotManager = ScreenShotManager("test/kk")
private val screenRecordingTest = ScreenRecordingTest()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_mc)
......@@ -35,13 +48,26 @@ class MCActivity : AppCompatActivity() {
startActivity(Intent(this, WebViewActivity::class.java))
}
findViewById<Button>(R.id.testPage).setOnClickListener {
startScreenShot()
}
findViewById<Button>(R.id.screenPage).setOnClickListener {
screenRecordingTest.start(this)
}
findViewById<SlideBar>(R.id.unlock_bar).setOnUnlockListener(object :
SlideBar.OnUnlockListener {
override fun onUnlock(view: View?) {
DoKit.sendCustomEvent(
"un_lock",
view,
mapOf("unlock" to "custom unlock")
mapOf(
"unlock" to "custom unlock",
"testRecording" to "true"
)
)
}
......@@ -49,10 +75,12 @@ class MCActivity : AppCompatActivity() {
DoKit.sendCustomEvent(
"lock_process",
view,
mapOf("progress" to "$leftMargin")
mapOf(
"progress" to "$leftMargin",
"testRecording" to "false"
)
)
}
})
val spinner = findViewById<Spinner>(R.id.spinner)
......@@ -107,6 +135,17 @@ class MCActivity : AppCompatActivity() {
}
private fun startScreenShot() {
val map = screenShotManager.screenshotBitmap()
val fileName = screenShotManager.createNextFileName()
screenShotManager.saveBitmap(map, fileName)
val bundle = Bundle()
bundle.putSerializable(BundleKey.FILE_KEY, File(screenShotManager.getScreenFile(fileName)))
DoKit.launchFullScreen(ImageDetailFragment::class.java, this, bundle, false)
}
private fun initData() {
val rvDatas = mutableListOf<String>()
......@@ -161,4 +200,8 @@ class MCActivity : AppCompatActivity() {
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
screenRecordingTest.onActivityResult(requestCode, resultCode, data)
}
}
package com.didichuxing.doraemondemo.test;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.PixelFormat;
import android.hardware.HardwareBuffer;
import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
import android.media.Image;
import android.media.ImageReader;
import android.media.projection.MediaProjection;
import android.media.projection.MediaProjectionManager;
import android.os.Build;
import android.os.IBinder;
import android.util.DisplayMetrics;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.core.app.NotificationCompat;
import com.didichuxing.doraemondemo.R;
import com.didichuxing.doraemondemo.mc.MCActivity;
import com.didichuxing.doraemondemo.test.screen.ScreenRecordingDoKitView;
import com.didichuxing.doraemonkit.DoKit;
import com.didichuxing.doraemonkit.kit.test.report.ScreenShotManager;
import com.didichuxing.doraemonkit.util.DoKitExecutorUtil;
import java.nio.ByteBuffer;
/**
* didi Create on 2022/4/25 .
* <p>
* Copyright (c) 2022/4/25 by didiglobal.com.
*
* @author <a href="realonlyone@126.com">zhangjun</a>
* @version 1.0
* @Date 2022/4/25 11:47 上午
* @Description 屏幕录制服务
*/
public class ScreenRecordingService extends Service {
public static MediaProjectionManager mMediaProjectionManager;
public static Activity activity;
public static MediaProjection mMediaProjection;
public static int mResultCode;
public static Intent mResultData;
private static ScreenRecordingService service;
private ImageReader mImageReader;
private ScreenShotManager screenShotManager = new ScreenShotManager("test/sc/");
private boolean enable = true;
public static void stopService() {
if (service != null) {
service.stopSelf();
}
}
@Override
public void onCreate() {
super.onCreate();
if (service != null) {
service.stopSelf();
}
service = this;
}
@Override
public void onDestroy() {
super.onDestroy();
service = null;
enable = false;
DoKit.removeFloating(ScreenRecordingDoKitView.class);
}
@SuppressLint("WrongConstant")
@RequiresApi(api = Build.VERSION_CODES.O)
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
NotificationChannel channel = new NotificationChannel("NOTIFICATION_CHANNEL_ID", "NOTIFICATION_CHANNEL_NAME", NotificationManager.IMPORTANCE_MIN);
channel.setDescription("NOTIFICATION_CHANNEL_DESC");
if (activity == null || activity.getApplication() == null) {
return START_STICKY;
}
NotificationManager mNM = (NotificationManager) getApplication().getSystemService(Context.NOTIFICATION_SERVICE);
if (mNM != null) {
mNM.createNotificationChannel(channel);
}
NotificationCompat.Builder builder = new NotificationCompat.Builder(activity, channel.getId());
// 在API11之后构建Notification的方式
Intent nfIntent = new Intent(activity, MCActivity.class);
builder.setContentIntent(PendingIntent.getActivity(activity, 0, nfIntent, 0))
.setLargeIcon(BitmapFactory.decodeResource(activity.getResources(), R.mipmap.ic_launcher))
.setContentTitle("下拉列表中的Title")
.setSmallIcon(R.mipmap.ic_launcher)
.setContentText("要显示的内容")
.setWhen(System.currentTimeMillis());
Notification notification = builder.build(); // 获取构建好的Notification
notification.defaults = Notification.DEFAULT_SOUND; //设置为默认的声音
startForeground(100, notification);
mMediaProjection = mMediaProjectionManager.getMediaProjection(mResultCode, mResultData);
DisplayMetrics metrics = activity.getResources().getDisplayMetrics();
int windowWidth = metrics.widthPixels;
int windowHeight = metrics.heightPixels;
float mScreenDensity = metrics.density;
ImageReader mImageReader = null; //ImageFormat.RGB_565
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
mImageReader = ImageReader.newInstance(windowWidth, windowHeight, PixelFormat.RGBA_8888, 2, HardwareBuffer.USAGE_CPU_WRITE_OFTEN);
} else {
mImageReader = ImageReader.newInstance(windowWidth, windowHeight, PixelFormat.RGBA_8888, 2);
}
VirtualDisplay mVirtualDisplay = mMediaProjection.createVirtualDisplay("screen-mirror",
windowWidth, windowHeight, (int) mScreenDensity, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
mImageReader.getSurface(), null, null);
this.mImageReader = mImageReader;
DoKitExecutorUtil.execute(mRunnable);
return super.onStartCommand(intent, flags, startId);
}
private Runnable mRunnable = new Runnable() {
@Override
public void run() {
while (enable) {
try {
Thread.sleep(100);
acquireLatestImage();
} catch (Exception e) {
e.printStackTrace();
}
}
}
};
public void acquireLatestImage() {
Image image = mImageReader.acquireLatestImage();
int width = image.getWidth();
int height = image.getHeight();
final Image.Plane[] planes = image.getPlanes();
if (planes.length > 0) {
final ByteBuffer buffer = planes[0].getBuffer();
int pixelStride = planes[0].getPixelStride();
int rowStride = planes[0].getRowStride();
int rowPadding = rowStride - pixelStride * width;
Bitmap bitmap = Bitmap.createBitmap(width + rowPadding / pixelStride, height, Bitmap.Config.ARGB_8888);
bitmap.copyPixelsFromBuffer(buffer);
bitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height);
ScreenRecordingDoKitView.Companion.updateScreen(bitmap);
// screenShotManager.saveBitmap(bitmap, screenShotManager.createNextFileName());
}
image.close();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
package com.didichuxing.doraemondemo.test;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.media.projection.MediaProjectionManager;
import androidx.annotation.Nullable;
import com.didichuxing.doraemondemo.test.screen.ScreenRecordingDoKitView;
import com.didichuxing.doraemonkit.DoKit;
/**
* didi Create on 2022/4/25 .
* <p>
* Copyright (c) 2022/4/25 by didiglobal.com.
*
* @author <a href="realonlyone@126.com">zhangjun</a>
* @version 1.0
* @Date 2022/4/25 11:24 上午
* @Description 屏幕录制服务测试支持
*/
public class ScreenRecordingTest {
public static final int REQUEST_MEDIA_PROJECTION = 100;
private MediaProjectionManager mMediaProjectionManager;
private Activity activity;
public void start(Activity activity) {
this.activity = activity;
mMediaProjectionManager = (MediaProjectionManager) activity.getSystemService(Context.MEDIA_PROJECTION_SERVICE);
activity.startActivityForResult(mMediaProjectionManager.createScreenCaptureIntent(), REQUEST_MEDIA_PROJECTION);
}
public void startService() {
Intent nfIntent = new Intent(activity.getApplication(), ScreenRecordingService.class);
activity.startService(nfIntent);
}
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
ScreenRecordingService.activity = activity;
ScreenRecordingService.mMediaProjectionManager = mMediaProjectionManager;
ScreenRecordingService.mResultCode = resultCode;
ScreenRecordingService.mResultData = data;
startService();
DoKit.launchFloating(ScreenRecordingDoKitView.class);
}
}
package com.didichuxing.doraemondemo.test.screen
import android.content.Context
import android.graphics.Bitmap
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.widget.FrameLayout
import android.widget.ImageView
import com.didichuxing.doraemondemo.R
import com.didichuxing.doraemondemo.test.ScreenRecordingService
import com.didichuxing.doraemonkit.kit.core.AbsDoKitView
import com.didichuxing.doraemonkit.kit.core.DoKitViewLayoutParams
import com.didichuxing.doraemonkit.kit.test.widget.FlashImageView
import com.didichuxing.doraemonkit.util.ConvertUtils
import com.didichuxing.doraemonkit.util.ToastUtils
import com.didichuxing.doraemonkit.util.UIUtils
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.plus
/**
* didi Create on 2022/4/14 .
*
* Copyright (c) 2022/4/14 by didiglobal.com.
*
* @author <a href="realonlyone@126.com">zhangjun</a>
* @version 1.0
* @Date 2022/4/14 4:40 下午
* @Description 用一句话说明文件功能
*/
class ScreenRecordingDoKitView : AbsDoKitView() {
companion object {
private val mainScope = MainScope() + CoroutineName(this.toString())
private val myDoKitViews: MutableSet<ScreenRecordingDoKitView> = mutableSetOf()
fun updateScreen(bitmap: Bitmap) {
mainScope.launch {
myDoKitViews.forEach {
it.updateScreen(bitmap)
}
}
}
}
private var mRedDot: FlashImageView? = null
private var mScreenImageView: ImageView? = null
override fun onCreate(context: Context?) {
}
override fun onCreateView(context: Context?, rootView: FrameLayout?): View {
return LayoutInflater.from(context).inflate(R.layout.dk_screen_show_view, rootView, false)
}
override fun onViewCreated(rootView: FrameLayout?) {
myDoKitViews.add(this)
mRedDot = findViewById(R.id.dot)
mScreenImageView = findViewById(R.id.screenAll)
rootView?.setOnClickListener {
}
rootView?.findViewById<ImageView>(R.id.close)?.setOnClickListener {
ScreenRecordingService.stopService()
ToastUtils.showShort("已停止录屏")
}
mRedDot?.startFlash()
}
private fun updateScreen(bitmap: Bitmap) {
mScreenImageView?.let {
it.setImageBitmap(bitmap)
}
}
override fun initDokitViewLayoutParams(params: DoKitViewLayoutParams) {
params.width = UIUtils.getWidthPixels() / 2
params.height = UIUtils.getRealHeightPixels() / 2
params.gravity = Gravity.TOP or Gravity.LEFT
params.x = ConvertUtils.dp2px(25f)
params.y = ConvertUtils.dp2px(25f)
}
override fun onDestroy() {
myDoKitViews.remove(this)
mRedDot?.cancelFlash()
super.onDestroy()
}
}
......@@ -32,14 +32,28 @@
android:id="@+id/nextPage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="DATA PAGE" />
android:text="网络" />
<Button
android:id="@+id/webPage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:text="WEB PAGE" />
android:layout_marginLeft="5dp"
android:text="网页" />
<Button
android:id="@+id/testPage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:text="截图" />
<Button
android:id="@+id/screenPage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:text="录屏" />
</LinearLayout>
......
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#60333333">
<ImageView
android:id="@+id/screenAll"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerInside">
</ImageView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginTop="10dp"
android:layout_marginRight="10dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<com.didichuxing.doraemonkit.kit.test.widget.FlashImageView
android:id="@+id/dot"
android:layout_width="14dp"
android:layout_height="14dp"
android:background="@drawable/dk_autotest_flash_red_bg" />
<Space
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="100" />
<ImageView
android:id="@+id/close"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/dk_dp_10"
android:src="@mipmap/dk_close_icon" />
</LinearLayout>
</FrameLayout>
......@@ -3,7 +3,7 @@ ext {
//0:发布到到本地localRepoURL仓库
//1:发布到滴滴内部仓库 一般不建议使用 如果需要发布到滴滴内网仓库需要将版本号改得比较大 假如版本号跟jcenter上的一致会由于缓存导致没法下载最新的jcenter的线上代码
//2:发布到maven_central远程仓库
archives_type: 1,
archives_type: 0,
//0:依赖dokit本地module运行
//1:依赖dokit远程aar运行
run_type : 0,
......@@ -13,7 +13,7 @@ ext {
//是否使用本地仓库,需要使用绝对路径,仓库地址在 local.properties 中添加 LOCAL_REPOSITORY_URL
use_local : true,
group_id : 'io.github.didi.dokit',
version : '3.6.0.8'
version : '3.6.10'
]
......@@ -56,18 +56,18 @@ ext {
"coroutines-android_v13" : "org.jetbrains.kotlinx:kotlinx-coroutines-android:${android["kotlinx_coroutines_version_v13"]}",
"coroutines-android_v14" : "org.jetbrains.kotlinx:kotlinx-coroutines-android:${android["kotlinx_coroutines_version_v14"]}",
"core-ktx" : "androidx.core:core-ktx:1.3.0",
"activity-ktx" : "androidx.activity:activity-ktx:1.2.3",
"activity-ktx" : "androidx.activity:activity-ktx:1.1.0",
"webkit" : "androidx.webkit:webkit:1.3.0",
"volley" : "com.android.volley:volley:1.1.1",
"fragment" : "androidx.fragment:fragment:1.3.5",
"fragment-ktx" : "androidx.fragment:fragment-ktx:1.3.5",
"fragment" : "androidx.fragment:fragment:1.2.0",
"fragment-ktx" : "androidx.fragment:fragment-ktx:1.2.0",
"drawerlayout" : "androidx.drawerlayout:drawerlayout:1.1.1",
//constraintLayout
"constraintLayout" : 'androidx.constraintlayout:constraintlayout:1.1.3',
//lifecycle
"lifecycle-viewmodel-savedstate": "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.3.1",
"lifecycle-comm" : "androidx.lifecycle:lifecycle-common-java8:2.3.1",
"lifecycle-viewmodel-savedstate": "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.2.0",
"lifecycle-comm" : "androidx.lifecycle:lifecycle-common-java8:2.2.0",
//test
"junit" : "junit:junit:4.12",
//第三方
......@@ -129,8 +129,6 @@ ext {
"jsonviewer" : "com.yuyh.json:jsonviewer:1.0.6",
"room_runtime" : 'androidx.room:room-runtime:2.0.0',
"room_compile" : 'androidx.room:room-compiler:2.0.0',
"um_analytics" : 'com.umeng.umsdk:analytics:8.0.0',
"um_common" : 'com.umeng.umsdk:common:2.0.0',
"didi_http" : 'com.didichuxing.foundation:http:2.1.0.86',
"didi_rpc" : 'com.didichuxing.foundation:rpc:2.1.0.86',
"okgo" : "com.lzy.net:okgo:3.0.4",
......
......@@ -4,7 +4,7 @@
<application>
<activity
android:name="com.didichuxing.doraemonkit.kit.autotest.ui.DokitAutotestActivity"
android:name="com.didichuxing.doraemonkit.kit.autotest.ui.DoKitAutotestActivity"
android:theme="@style/Theme.AppCompat.Light.NoActionBar"
android:windowSoftInputMode="adjustPan|stateHidden|stateUnchanged" />
</application>
......
......@@ -6,7 +6,7 @@ import android.content.Intent
import com.didichuxing.doraemonkit.aop.DokitPluginConfig
import com.didichuxing.doraemonkit.autotest.R
import com.didichuxing.doraemonkit.kit.AbstractKit
import com.didichuxing.doraemonkit.kit.autotest.ui.DokitAutotestActivity
import com.didichuxing.doraemonkit.kit.autotest.ui.DoKitAutotestActivity
import com.didichuxing.doraemonkit.util.DoKitCommUtil
import com.didichuxing.doraemonkit.util.ToastUtils
import com.google.auto.service.AutoService
......@@ -37,7 +37,7 @@ class AutoTestControlKit : AbstractKit() {
ToastUtils.showShort(DoKitCommUtil.getString(R.string.dk_plugin_close_tip))
return false
}
val intent = Intent(activity, DokitAutotestActivity::class.java)
val intent = Intent(activity, DoKitAutotestActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
activity.startActivity(intent)
......
package com.didichuxing.doraemonkit.kit.autotest
import android.app.Activity
import android.graphics.Bitmap
import android.view.View
import com.didichuxing.doraemonkit.DoKit
import com.didichuxing.doraemonkit.kit.autotest.ui.RecordingCaseDoKitView
import com.didichuxing.doraemonkit.kit.connect.ConnectAddress
import com.didichuxing.doraemonkit.kit.connect.data.PackageType
import com.didichuxing.doraemonkit.kit.connect.data.TextPackage
import com.didichuxing.doraemonkit.kit.connect.parser.ByteParser
import com.didichuxing.doraemonkit.kit.connect.parser.JsonParser
import com.didichuxing.doraemonkit.kit.connect.ws.*
import com.didichuxing.doraemonkit.kit.core.DoKitFrameLayout
import com.didichuxing.doraemonkit.kit.test.DoKitTestManager
import com.didichuxing.doraemonkit.kit.test.TestMode
import com.didichuxing.doraemonkit.kit.test.event.*
import com.didichuxing.doraemonkit.kit.test.mock.MockManager
import com.didichuxing.doraemonkit.kit.test.mock.ProxyMockCallback
import com.didichuxing.doraemonkit.kit.test.report.ScreenShotManager
import com.didichuxing.doraemonkit.util.*
import kotlinx.coroutines.*
import okio.ByteString
import java.io.ByteArrayOutputStream
import java.lang.Runnable
/**
* didi Create on 2022/4/6 .
*
* Copyright (c) 2022/4/6 by didiglobal.com.
*
* @author <a href="realonlyone@126.com">zhangjun</a>
* @version 1.0
* @Date 2022/4/6 5:14 下午
* @Description 自动化测试管理
*/
object AutoTestManager {
private val mainScope = MainScope() + CoroutineName(this.toString())
private val uploadScope = CoroutineScope(SupervisorJob() + Dispatchers.IO) + CoroutineName("upload")
private val delayHandler: DelayHandler = DelayHandler()
private val autoTestStateSet: MutableMap<String, AutoTestState> = mutableMapOf()
private var connectAddress: ConnectAddress? = null
private var webSocketClient: WebSocketClient = WebSocketClient()
private var mode: TestMode = TestMode.UNKNOWN
private var screenShotManager: ScreenShotManager = ScreenShotManager("doKit/autotest/screen")
private var diffEventTask: EventScreenShotTask? = null
private var webSocketClientCreate = false
class EventScreenShotTask(private val event: ControlEvent) : Runnable {
override fun run() {
val state = autoTestStateSet.remove(event.eventId)
state?.let {
val message = state.message
val bitmap = screenShotManager.screenshotBitmap()
if (bitmap != null) {
val name = screenShotManager.createNextFileName()
message.params["imageName"] = name
message.params["type"] = "webp"
} else {
message.params["imageName"] = ""
message.params["type"] = ""
}
if (event.eventType == EventType.WSE_TCP_EVENT && event.diffTime < 1000) {
onResponseAutoTestAction(message, bitmap)
} else {
onResponseAutoTestAction(message, bitmap)
}
} ?: run {
val message = AutoTestMessage(command = "action_response", message = "failed")
message.params["eventId"] = event.eventId
message.params["imageName"] = ""
message.params["type"] = ""
onResponseAutoTestAction(message)
}
}
}
fun getMode(): TestMode {
return mode
}
fun startRecord() {
ControlEventManager.resetLastEventDateTime()
ControlEventManager.addOnControlEventInterceptor(eventActionInterceptor)
ControlEventManager.addOnControlEventActionListener(eventActionListener)
MockManager.proxyMockCallback = proxyMockCallback
MockManager.startTest(TestMode.HOST)
DoKitTestManager.startTest(TestMode.HOST)
changeToRecordView()
ToastUtils.showShort("已开始录制")
}
fun stopRecord() {
MockManager.closeTest()
DoKitTestManager.closeTest()
MockManager.proxyMockCallback = null
ControlEventManager.removeOnControlEventActionListener(eventActionListener)
ControlEventManager.removeOnControlEventInterceptor(eventActionInterceptor)
changeToConnectView()
ToastUtils.showShort("已停止录制")
}
fun startAutoTest() {
MockManager.proxyMockCallback = proxyMockCallback
ControlEventManager.addOnControlEventActionProcessListener(actionProcessListener)
MockManager.startTest(TestMode.CLIENT)
DoKitTestManager.startTest(TestMode.CLIENT)
changeToTestView()
ToastUtils.showShort("已开始测试")
}
fun stopAutoTest() {
MockManager.closeTest()
DoKitTestManager.closeTest()
MockManager.proxyMockCallback = null
ControlEventManager.removeOnControlEventActionProcessListener(actionProcessListener)
changeToConnectView()
ToastUtils.showShort("已停止测试")
}
fun startConnect(address: ConnectAddress) {
connectAddress = address
connect()
}
fun stopConnect() {
webSocketClient?.close()
}
fun send(bytes: ByteString): Boolean {
webSocketClient?.let {
it.send(bytes)
return true
}
return false
}
private fun changeToRecordView() {
mode = TestMode.HOST
mainScope.launch {
RecordingCaseDoKitView.changeMode(mode)
}
}
private fun changeToConnectView() {
mode = TestMode.UNKNOWN
mainScope.launch {
RecordingCaseDoKitView.changeMode(mode)
}
}
private fun changeToTestView() {
mode = TestMode.CLIENT
mainScope.launch {
RecordingCaseDoKitView.changeMode(mode)
}
}
private fun onReceiveControl(textPackage: TextPackage) {
val text = textPackage.data
val autoTestMessage = GsonUtils.fromJson<AutoTestMessage>(text, AutoTestMessage::class.java)
when (autoTestMessage.command) {
"startRecord" -> {
startRecord()
val msg = AutoTestMessage(command = "control_response", message = "success")
msg.params["command"] = autoTestMessage.command
onResponseAutoTestMessage(msg)
}
"stopRecord" -> {
stopRecord()
val msg = AutoTestMessage(command = "control_response", message = "success")
msg.params["command"] = autoTestMessage.command
onResponseAutoTestMessage(msg)
}
"startAutoTest" -> {
startAutoTest()
val msg = AutoTestMessage(command = "control_response", message = "success")
msg.params["command"] = autoTestMessage.command
onResponseAutoTestMessage(msg)
}
"stopAutoTest" -> {
onStopAutoTest(autoTestMessage)
}
}
}
private fun onStopAutoTest(autoTestMessage: AutoTestMessage) {
delayHandler.postDelayed(object : Runnable {
override fun run() {
stopAutoTest()
val msg = AutoTestMessage(command = "control_response", message = "success")
msg.params["command"] = autoTestMessage.command
onResponseAutoTestMessage(msg)
}
}, 3000)
}
/**
* 控制消息响应
*/
private fun onResponseAutoTestMessage(autoTestMessage: AutoTestMessage) {
webSocketClient?.let {
it.send(JsonParser.toJson(PackageType.AUTOTEST, autoTestMessage, "auto_test_control"))
}
}
/**
* 自动化测试行为事件响应
*/
private fun onResponseAutoTestAction(autoTestMessage: AutoTestMessage) {
webSocketClient?.let {
it.send(JsonParser.toJson(PackageType.AUTOTEST, autoTestMessage, "action"))
}
}
/**
* 自动化测试行为事件响应
*/
private fun onResponseAutoTestAction(autoTestMessage: AutoTestMessage, bitmap: Bitmap) {
uploadScope.launch {
val stream = ByteArrayOutputStream(2048)
val ok = bitmap.compress(Bitmap.CompressFormat.WEBP, 10, stream)
val bytes = stream.toByteArray()
val textPackage = JsonParser.toTextPackage(PackageType.AUTOTEST, autoTestMessage, "action")
val byteString = ByteParser.toByteString(textPackage, bytes)
webSocketClient.send(byteString)
stream.close()
}
}
private fun isDiffTimeEvent(event: ControlEvent): Boolean {
when (event.eventType) {
EventType.WSE_CUSTOM_EVENT -> {
event.params?.let {
var testRecording: String? = it["testRecording"]
if (testRecording == "false") {
return false
}
}
return true
}
EventType.WSE_TCP_EVENT,
EventType.APP_ON_FOREGROUND,
EventType.APP_ON_BACKGROUND,
EventType.ACTIVITY_BACK_PRESSED -> {
return true
}
EventType.WSE_COMMON_EVENT -> {
event.viewC12c?.let {
when (it.actionType) {
ActionType.ON_LONG_CLICK,
ActionType.ON_SCROLL,
ActionType.ON_INPUT_CHANGE,
ActionType.ON_CLICK -> {
return true
}
else -> {
}
}
}
}
}
return false
}
private fun getDiffTimeByEvent(event: ControlEvent, diffTime: Long): Long {
when (event.eventType) {
EventType.WSE_TCP_EVENT -> {
return diffTime
}
EventType.WSE_COMMON_EVENT -> {
event.viewC12c?.let {
when (it.actionType) {
ActionType.ON_SCROLL,
ActionType.ON_INPUT_CHANGE -> {
return if (diffTime > 100) {
diffTime
} else {
100
}
}
else -> {
}
}
}
}
}
return 1000
}
/**
* 接收到自动化测试事件
*/
private fun onReceiveAction(textPackage: TextPackage) {
val text = textPackage.data
val event = GsonUtils.fromJson<ControlEvent>(text, ControlEvent::class.java)
if (isDiffTimeEvent(event)) {
val diff: Long = if (event.diffTime < 1000) {
getDiffTimeByEvent(event, event.diffTime)
} else {
event.diffTime
}
val eventTask = EventScreenShotTask(event)
diffEventTask = eventTask
delayHandler.postDelayed(eventTask, diff)
}
mainScope.launch {
ControlEventManager.onReceiveControlEventAction(event)
}
}
private fun connect() {
if (connectAddress == null) {
return
}
if (!webSocketClientCreate) {
webSocketClientCreate = true
webSocketClient?.let {
it.addOnWebSocketLoginSuccessListener(object : OnWebSocketLoginSuccessListener {
override fun onWebSocketLoginSuccess() {
mainScope.launch {
DoKit.launchFloating(RecordingCaseDoKitView::class.java)
}
}
})
it.addOnWebSocketTextPackageListener(object : OnWebSocketTextPackageListener {
override fun onReceiveTextPackage(webSocket: OkHttpWebSocketSession, textPackage: TextPackage) {
if (textPackage.type == PackageType.AUTOTEST || textPackage.type == PackageType.BROADCAST) {
when (textPackage.contentType) {
"auto_test_control" -> {
onReceiveControl(textPackage)
}
"action" -> {
onReceiveAction(textPackage)
}
else -> {
}
}
} else if (textPackage.type == PackageType.DATA) {
MockManager.receiveQueryResponse(textPackage)
}
}
})
it.addOnWebSocketCloseListener(object : OnWebSocketCloseListener {
override fun onWebSocketClose() {
mainScope.launch {
DoKit.removeFloating(RecordingCaseDoKitView::class.java)
}
}
})
it.startAutoConnect()
it.connect(connectAddress!!.url)
}
} else {
webSocketClient?.let {
it.startAutoConnect()
it.reConnect(connectAddress!!.url)
}
}
}
private val eventActionInterceptor = object : OnControlEventInterceptor {
override fun onControlEventAction(activity: Activity?, view: View?, controlEvent: ControlEvent): Boolean {
if (view is DoKitFrameLayout) {
return true
}
return false
}
}
private val eventActionListener = object : OnControlEventActionListener {
override fun onControlEventAction(activity: Activity?, view: View?, event: ControlEvent) {
webSocketClient.send(JsonParser.toJson(PackageType.BROADCAST, event, "action"))
}
}
private val proxyMockCallback = object : ProxyMockCallback {
override fun send(data: String) {
webSocketClient.send(data)
}
}
private val actionProcessListener = object : OnControlEventActionProcessListener {
override fun onControlEventProcessSuccess(activity: Activity?, view: View?, controlEvent: ControlEvent) {
val msg = AutoTestMessage(command = "action_response", message = "success")
msg.params["eventId"] = controlEvent.eventId
if (isDiffTimeEvent(controlEvent)) {
val state = AutoTestState(activity, view, controlEvent, msg)
autoTestStateSet[controlEvent.eventId] = state
} else {
onResponseAutoTestAction(msg)
}
}
override fun onControlEventProcessFailed(activity: Activity?, view: View?, controlEvent: ControlEvent, code: Int, message: String) {
val msg = AutoTestMessage(command = "action_response", message = "failed")
msg.params["eventId"] = controlEvent.eventId
msg.params["message"] = message
msg.params["code"] = "" + code
if (isDiffTimeEvent(controlEvent)) {
diffEventTask?.let {
delayHandler.removeCallbacks(it)
}
onResponseAutoTestAction(msg)
} else {
onResponseAutoTestAction(msg)
}
}
}
}
package com.didichuxing.doraemonkit.kit.autotest
/**
* didi Create on 2022/4/6 .
*
* Copyright (c) 2022/4/6 by didiglobal.com.
*
* @author <a href="realonlyone@126.com">zhangjun</a>
* @version 1.0
* @Date 2022/4/6 5:14 下午
* @Description 自动化测试通信消息
*/
data class AutoTestMessage(
val message: String = "",
val command: String = "",
var params: MutableMap<String, String> = mutableMapOf()
)
package com.didichuxing.doraemonkit.kit.autotest
import android.app.Activity
import android.view.View
import com.didichuxing.doraemonkit.kit.test.event.ControlEvent
data class AutoTestState(
val activity: Activity?,
val view: View?,
val controlEvent: ControlEvent,
val message: AutoTestMessage,
val success: Boolean = true
)
package com.didichuxing.doraemonkit.kit.autotest
import android.os.Handler
import android.os.Looper
/**
* didi Create on 2022/4/18 .
*
* Copyright (c) 2022/4/18 by didiglobal.com.
*
* @author <a href="realonlyone@126.com">zhangjun</a>
* @version 1.0
* @Date 2022/4/18 3:11 下午
* @Description 用一句话说明文件功能
*/
class DelayHandler {
private val mainHandler: Handler = Handler(Looper.getMainLooper())
fun postDelayed(runnable: Runnable, delay: Long) {
mainHandler.postDelayed(runnable, delay)
}
fun removeCallbacks(runnable: Runnable) {
mainHandler.removeCallbacks(runnable)
}
}
......@@ -13,6 +13,7 @@ package com.didichuxing.doraemonkit.kit.autotest.ui;
public enum AutotestPage {
CONNECT,
HOME,
RECORD,
CASE_LIST
......
......@@ -6,8 +6,8 @@ import android.text.TextUtils;
import androidx.annotation.Nullable;
import com.didichuxing.doraemonkit.autotest.R;
import com.didichuxing.doraemonkit.kit.core.BaseActivity;
import com.didichuxing.doraemonkit.kit.core.BaseFragment;
import com.didichuxing.doraemonkit.kit.core.NewBaseActivity;
import com.didichuxing.doraemonkit.widget.titlebar.HomeTitleBar;
/**
......@@ -21,7 +21,7 @@ import com.didichuxing.doraemonkit.widget.titlebar.HomeTitleBar;
* @Description 用一句话说明文件功能
*/
public class DokitAutotestActivity extends BaseActivity {
public class DoKitAutotestActivity extends NewBaseActivity {
private HomeTitleBar homeTitleBar;
......@@ -62,11 +62,13 @@ public class DokitAutotestActivity extends BaseActivity {
public void changeFragment(AutotestPage page, boolean push) {
BaseFragment fragment;
switch (page) {
case HOME:
fragment = new DoKitAutotestFragment();
case CONNECT:
fragment = new DoKitAutotestConnectFragment();
break;
case RECORD:
case CASE_LIST:
case HOME:
default:
fragment = new DoKitAutotestFragment();
}
......
package com.didichuxing.doraemonkit.kit.autotest.ui
import android.os.Bundle
import android.view.View
import android.widget.TextView
import com.didichuxing.doraemonkit.DoKit
import com.didichuxing.doraemonkit.autotest.R
import com.didichuxing.doraemonkit.kit.autotest.AutoTestManager
import com.didichuxing.doraemonkit.kit.connect.ConnectAddress
import com.didichuxing.doraemonkit.kit.connect.ConnectAddressStore
import com.didichuxing.doraemonkit.kit.connect.DoKitConnectFragment
import com.didichuxing.doraemonkit.kit.core.BaseFragment
import com.didichuxing.doraemonkit.util.ToastUtils
/**
* didi Create on 2022/4/14 .
*
* Copyright (c) 2022/4/14 by didiglobal.com.
*
* @author <a href="realonlyone@126.com">zhangjun</a>
* @version 1.0
* @Date 2022/4/14 4:40 下午
* @Description 用一句话说明文件功能
*/
class DoKitAutotestConnectFragment : BaseFragment() {
private var urlTextView: TextView? = null
private var address: ConnectAddress? = null
override fun onRequestLayout(): Int {
return R.layout.dk_fragment_autotest_connect
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
urlTextView = view.findViewById<TextView>(R.id.url)
view.findViewById<View>(R.id.connect).setOnClickListener {
starConnect()
}
view.findViewById<View>(R.id.change).setOnClickListener {
startChange()
}
}
override fun onResume() {
super.onResume()
updateUrl()
}
private fun updateUrl() {
val list = ConnectAddressStore.loadAddress()
if (list.size > 0) {
address = list[list.size - 1]
}
address?.let {
urlTextView?.text = "可使用地址:${it.url}"
} ?: run {
urlTextView?.text = "可使用地址:--}"
}
}
private fun starConnect() {
if (address == null) {
ToastUtils.showShort("无可用地址,请添加")
} else {
address?.let {
AutoTestManager.startConnect(it)
finish()
}
}
}
private fun startChange() {
//使用统一的链接管理
DoKit.launchFullScreen(DoKitConnectFragment::class.java, context, null, false)
}
}
......@@ -3,18 +3,24 @@ package com.didichuxing.doraemonkit.kit.autotest.ui
import android.os.Bundle
import android.view.View
import com.didichuxing.doraemonkit.autotest.R
import com.didichuxing.doraemonkit.kit.autotest.AutoTestManager
import com.didichuxing.doraemonkit.kit.core.BaseFragment
import com.didichuxing.doraemonkit.kit.test.TestMode
import com.didichuxing.doraemonkit.kit.test.report.ScreenShotManager
import com.didichuxing.doraemonkit.util.ToastUtils
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/12/10-10:52
* 描 述:一机多控main fragment
* 修订历史:
* ================================================
* didi Create on 2022/4/14 .
*
* Copyright (c) 2022/4/14 by didiglobal.com.
*
* @author <a href="realonlyone@126.com">zhangjun</a>
* @version 1.0
* @Date 2022/4/14 4:40 下午
* @Description 用一句话说明文件功能
*/
class DoKitAutotestFragment : BaseFragment() {
......@@ -26,7 +32,88 @@ class DoKitAutotestFragment : BaseFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
view.findViewById<View>(R.id.connect).setOnClickListener {
starConnect()
}
view.findViewById<View>(R.id.record).setOnClickListener {
startRecording()
}
view.findViewById<View>(R.id.record_stop).setOnClickListener {
stopRecording()
}
view.findViewById<View>(R.id.test).setOnClickListener {
startTest()
}
view.findViewById<View>(R.id.test_stop).setOnClickListener {
stopTest()
}
view.findViewById<View>(R.id.caseList).setOnClickListener {
ToastUtils.showShort("不支持")
}
}
private var screenShotManager: ScreenShotManager = ScreenShotManager("doKit/autotest/screen2")
private fun test() {
val bitmap = screenShotManager.screenshotBitmap(activity)
}
private fun starConnect() {
//使用统一的链接管理
if (activity is DoKitAutotestActivity) {
(activity as DoKitAutotestActivity).pushFragment(AutotestPage.CONNECT)
}
}
private fun startRecording() {
when (AutoTestManager.getMode()) {
TestMode.UNKNOWN -> {
AutoTestManager.startRecord()
}
TestMode.HOST -> {
ToastUtils.showShort("已经在录制中")
}
TestMode.CLIENT -> {
ToastUtils.showShort("在测试中,请先关闭")
}
}
}
private fun stopRecording() {
when (AutoTestManager.getMode()) {
TestMode.HOST -> {
AutoTestManager.stopRecord()
}
else -> {
ToastUtils.showShort("不在录制中")
}
}
}
private fun startTest() {
when (AutoTestManager.getMode()) {
TestMode.UNKNOWN -> {
AutoTestManager.startAutoTest()
}
TestMode.HOST -> {
ToastUtils.showShort("在录制中,请先关闭")
}
TestMode.CLIENT -> {
ToastUtils.showShort("已经在测试中")
}
}
}
private fun stopTest() {
when (AutoTestManager.getMode()) {
TestMode.CLIENT -> {
AutoTestManager.stopAutoTest()
}
else -> {
ToastUtils.showShort("不在测试中")
}
}
}
......
package com.didichuxing.doraemonkit.kit.autotest.ui
import android.content.Context
import android.content.Intent
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.TextView
import com.didichuxing.doraemonkit.DoKit
import com.didichuxing.doraemonkit.autotest.R
import com.didichuxing.doraemonkit.kit.autotest.AutoTestManager
import com.didichuxing.doraemonkit.kit.core.AbsDoKitView
import com.didichuxing.doraemonkit.kit.core.DoKitViewLayoutParams
import com.didichuxing.doraemonkit.kit.test.TestMode
import com.didichuxing.doraemonkit.kit.test.widget.FlashImageView
import com.didichuxing.doraemonkit.kit.test.widget.FlashTextView
import com.didichuxing.doraemonkit.util.ActivityUtils
import com.didichuxing.doraemonkit.util.ConvertUtils
import com.didichuxing.doraemonkit.util.ToastUtils
/**
* didi Create on 2022/4/14 .
*
* Copyright (c) 2022/4/14 by didiglobal.com.
*
* @author <a href="realonlyone@126.com">zhangjun</a>
* @version 1.0
* @Date 2022/4/14 4:40 下午
* @Description 用一句话说明文件功能
*/
class RecordingCaseDoKitView : AbsDoKitView() {
companion object {
val doKitViews: MutableSet<RecordingCaseDoKitView> = mutableSetOf()
fun changeText(text: String) {
doKitViews.forEach {
it.changeText(text)
}
}
fun changeDotColor(id: Int) {
doKitViews.forEach {
it.changeDotColor(id)
}
}
fun changeMode(mode: TestMode) {
doKitViews.forEach {
it.changeMode(mode)
}
}
}
private var mRedDot: FlashImageView? = null
private var mExtend: FlashTextView? = null
private var mText: TextView? = null
override fun onCreate(context: Context?) {
}
override fun onCreateView(context: Context?, rootView: FrameLayout?): View {
return LayoutInflater.from(context).inflate(R.layout.dk_autotest_view_recording_case, rootView, false)
}
override fun onViewCreated(rootView: FrameLayout?) {
mRedDot = findViewById(R.id.dot)
mExtend = findViewById(R.id.tv_extend)
mText = findViewById(R.id.tv_text)
rootView?.setOnClickListener {
if (ActivityUtils.getTopActivity() is DoKitAutotestActivity) {
return@setOnClickListener
}
val intent = Intent(activity, DoKitAutotestActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
activity.startActivity(intent)
}
rootView?.findViewById<ImageView>(R.id.iv_close)?.setOnClickListener {
if (AutoTestManager.getMode() == TestMode.UNKNOWN) {
AutoTestManager.stopConnect()
DoKit.removeFloating(RecordingCaseDoKitView::class)
} else {
ToastUtils.showShort("正在测试或录制,请先停止")
}
}
mRedDot?.startFlash()
mExtend?.startFlash()
changeMode(AutoTestManager.getMode())
doKitViews.add(this)
}
private fun changeText(text: String) {
mText?.text = text
}
private fun changeDotColor(id: Int) {
mRedDot?.setBackgroundResource(id)
}
fun changeMode(mode: TestMode) {
var dotColor = R.drawable.dk_autotest_flash_red_bg
var text = "待链接"
when (mode) {
TestMode.UNKNOWN -> {
dotColor = R.drawable.dk_autotest_flash_red_bg
text = "已链接"
}
TestMode.HOST -> {
dotColor = R.drawable.dk_autotest_flash_green_bg
text = "录制中"
}
TestMode.CLIENT -> {
dotColor = R.drawable.dk_autotest_flash_blue_bg
text = "测试中"
}
}
changeText(text)
changeDotColor(dotColor)
}
override fun initDokitViewLayoutParams(params: DoKitViewLayoutParams) {
params.width = DoKitViewLayoutParams.WRAP_CONTENT
params.height = DoKitViewLayoutParams.WRAP_CONTENT
params.gravity = Gravity.TOP or Gravity.LEFT
params.x = ConvertUtils.dp2px(25f)
params.y = ConvertUtils.dp2px(25f)
}
override fun onDestroy() {
super.onDestroy()
mRedDot?.cancelFlash()
mExtend?.cancelFlash()
doKitViews.remove(this)
}
}
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/dk_color_FFFFFF" />
<corners android:radius="4dp" />
<stroke
android:width="1px"
android:color="@color/dk_color_CCCCCC" />
</shape>
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="7dp" />
<solid android:color="@color/dk_color_0070BB" />
</shape>
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="7dp" />
<solid android:color="@color/dk_color_48BB31" />
</shape>
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="7dp" />
<solid android:color="@color/background_error" />
</shape>
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners
android:radius="3dp"/>
<solid android:color="@color/dk_color_337CC4"/>
</shape>
\ No newline at end of file
android:shape="rectangle">
<corners android:radius="3dp" />
<solid android:color="@color/dk_color_337CC4" />
</shape>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/dk_autotest_dialog_bg"
android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="10dp">
<com.didichuxing.doraemonkit.kit.test.widget.FlashImageView
android:id="@+id/dot"
android:layout_width="14dp"
android:layout_height="14dp"
android:background="@drawable/dk_autotest_flash_red_bg" />
<TextView
android:id="@+id/tv_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:text="已链接"
android:textColor="@color/dk_color_333333"
android:textSize="14sp"
android:textStyle="bold" />
<com.didichuxing.doraemonkit.kit.test.widget.FlashTextView
android:id="@+id/tv_extend"
android:layout_width="20dp"
android:layout_height="wrap_content"
android:textColor="@color/dk_color_333333"
android:textSize="14sp"
android:textStyle="bold"
tools:text="..." />
<ImageView
android:id="@+id/iv_close"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/dk_dp_10"
android:src="@mipmap/dk_close_icon" />
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/dk_color_FFFFFF"
android:orientation="vertical">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="vertical">
<TextView
android:id="@+id/url"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginTop="40dp"
android:layout_marginRight="20dp"
android:text="可使用地址:ws://172.23.164.46:8000/proxy/multicontrol/ILJLQCCF"
android:textColor="@color/dk_color_333333"
android:textSize="18sp"
android:textStyle="bold" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="20dp">
<Switch
android:id="@+id/switch_btn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="是否显示链接状态(悬浮)"
android:textColor="@color/dk_color_333333"
android:textSize="18sp"
android:textStyle="bold" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="40dp"
android:orientation="horizontal">
<Button
android:id="@+id/connect"
android:layout_width="150dp"
android:layout_height="50dp"
android:layout_marginLeft="20dp"
android:layout_marginTop="15dp"
android:background="@drawable/dk_btn_autotest_bg"
android:text="链接服务"
android:textAllCaps="false"
android:textColor="@color/dk_color_FFFFFF"
android:textSize="18sp"
android:textStyle="bold" />
<Button
android:id="@+id/change"
android:layout_width="150dp"
android:layout_height="50dp"
android:layout_marginLeft="20dp"
android:layout_marginTop="15dp"
android:background="@drawable/dk_btn_autotest_bg"
android:text="更换地址"
android:textAllCaps="false"
android:textColor="@color/dk_color_FFFFFF"
android:textSize="18sp"
android:textStyle="bold" />
</LinearLayout>
</LinearLayout>
</ScrollView>
</RelativeLayout>
......@@ -27,7 +27,7 @@
android:orientation="vertical">
<Button
android:id="@+id/tv_connect"
android:id="@+id/connect"
android:layout_width="150dp"
android:layout_height="50dp"
android:layout_marginTop="15dp"
......@@ -39,7 +39,7 @@
android:textStyle="bold" />
<Button
android:id="@+id/tv_host"
android:id="@+id/record"
android:layout_width="150dp"
android:layout_height="50dp"
android:layout_marginTop="15dp"
......@@ -50,8 +50,47 @@
android:textSize="18sp"
android:textStyle="bold" />
<Button
android:id="@+id/record_stop"
android:layout_width="150dp"
android:layout_height="50dp"
android:layout_marginTop="15dp"
android:background="@drawable/dk_btn_autotest_bg"
android:text="停止录制"
android:textAllCaps="false"
android:textColor="@color/dk_color_FFFFFF"
android:textSize="18sp"
android:textStyle="bold" />
<Button
android:id="@+id/test"
android:layout_width="150dp"
android:layout_height="50dp"
android:layout_marginTop="15dp"
android:background="@drawable/dk_btn_autotest_bg"
android:text="开始测试"
android:textAllCaps="false"
android:textColor="@color/dk_color_FFFFFF"
android:textSize="18sp"
android:textStyle="bold" />
<Button
android:id="@+id/test_stop"
android:layout_width="150dp"
android:layout_height="50dp"
android:layout_marginTop="15dp"
android:background="@drawable/dk_btn_autotest_bg"
android:text="停止测试"
android:textAllCaps="false"
android:textColor="@color/dk_color_FFFFFF"
android:textSize="18sp"
android:textStyle="bold" />
<Button
android:id="@+id/tv_client"
android:id="@+id/caseList"
android:layout_width="150dp"
android:layout_height="50dp"
android:layout_marginTop="15dp"
......
package com.didichuxing.doraemonkit.kit.mc
import android.graphics.Bitmap
import com.didichuxing.doraemonkit.kit.connect.data.PackageType
import com.didichuxing.doraemonkit.kit.connect.parser.ByteParser
import com.didichuxing.doraemonkit.kit.connect.parser.JsonParser
import com.didichuxing.doraemonkit.kit.connect.ws.*
import com.didichuxing.doraemonkit.kit.test.event.*
import com.didichuxing.doraemonkit.kit.test.report.AutoTestMessage
import com.didichuxing.doraemonkit.kit.test.report.AutoTestState
import com.didichuxing.doraemonkit.kit.test.report.ScreenShotManager
import com.didichuxing.doraemonkit.util.ActivityUtils
import kotlinx.coroutines.*
import java.io.ByteArrayOutputStream
import java.lang.Runnable
/**
* didi Create on 2022/4/22 .
*
* Copyright (c) 2022/4/22 by didiglobal.com.
*
* @author <a href="realonlyone@126.com">zhangjun</a>
* @version 1.0
* @Date 2022/4/22 12:26 下午
* @Description 用一句话说明文件功能
*/
abstract class AbstractMultiController(private val webSocketClient: WebSocketClient) {
protected val mainScope = MainScope() + CoroutineName(this.toString())
private val uploadScope = CoroutineScope(SupervisorJob() + Dispatchers.IO) + CoroutineName("mc-upload")
private val autoTestStateSet: MutableMap<String, AutoTestState> = mutableMapOf()
private val screenShotManager: ScreenShotManager = ScreenShotManager("doKit/autotest/screen")
private val delayHandler: DelayHandler = DelayHandler()
private var screenShotEventTask: ScreenShotEventTask? = null
private var lastControlEvent: ControlEvent? = null
fun screenShotForEvent(controlEvent: ControlEvent) {
if (controlEvent.eventType == EventType.WSE_TCP_EVENT) {
return
}
lastControlEvent?.let {
screenShotNow(it)
}
lastControlEvent = controlEvent
if (isDiffTimeEvent(controlEvent)) {
val task = ScreenShotEventTask(controlEvent)
screenShotEventTask = task
delayHandler.postDelayed(task, getDiffTimeByEvent(controlEvent, 0))
}
}
private fun getDiffTimeByEvent(event: ControlEvent, diffTime: Long): Long {
when (event.eventType) {
EventType.WSE_COMMON_EVENT -> {
event.viewC12c?.let {
when (it.actionType) {
ActionType.ON_SCROLL,
ActionType.ON_INPUT_CHANGE -> {
return if (diffTime > 100) {
diffTime
} else {
100
}
}
else -> {
}
}
}
}
}
return 1000
}
/**
* 从机事件处理结果
*/
fun onControlEventProcessState(autoTestState: AutoTestState) {
val controlEvent = autoTestState.controlEvent
val message = autoTestState.message
if (isDiffTimeEvent(controlEvent)) {
autoTestStateSet[autoTestState.controlEvent.eventId] = autoTestState
} else {
message.params["imageName"] = ""
message.params["type"] = ""
onResponseAutoTestAction(message)
}
}
private fun buildAutoTestMessage(controlEvent: ControlEvent): AutoTestMessage {
val state: AutoTestState? = autoTestStateSet.remove(controlEvent.eventId)
state?.let {
autoTestStateSet.remove(controlEvent.eventId)
return state.message
}
val message = AutoTestMessage(command = "action_response", message = "success")
message.params["eventId"] = controlEvent.eventId
return message
}
private fun screenShotNow(controlEvent: ControlEvent) {
val message = buildAutoTestMessage(controlEvent)
val bitmap = screenShotManager.screenshotBitmap()
if (bitmap != null) {
val name = screenShotManager.createNextFileName()
message.params["imageName"] = name
message.params["type"] = "webp"
onResponseAutoTestAction(message, bitmap)
} else {
message.params["errorMessage"] = "screenShot error."
message.params["imageName"] = ""
message.params["type"] = ""
onResponseAutoTestAction(message)
}
}
private fun isDiffTimeEvent(controlEvent: ControlEvent): Boolean {
when (controlEvent.eventType) {
EventType.WSE_CUSTOM_EVENT -> {
controlEvent.params?.let {
var testRecording: String? = it["testRecording"]
if (testRecording == "false") {
return false
}
}
return true
}
EventType.APP_ON_FOREGROUND,
EventType.APP_ON_BACKGROUND,
EventType.ACTIVITY_BACK_PRESSED -> {
return true
}
EventType.WSE_COMMON_EVENT -> {
controlEvent.viewC12c?.let {
when (it.actionType) {
ActionType.ON_LONG_CLICK,
ActionType.ON_SCROLL,
ActionType.ON_INPUT_CHANGE,
ActionType.ON_CLICK -> {
return true
}
else -> {
}
}
}
}
}
return false
}
/**
* 自动化测试行为事件响应
*/
private fun onResponseAutoTestAction(autoTestMessage: AutoTestMessage) {
webSocketClient?.let {
it.send(JsonParser.toJson(PackageType.AUTOTEST, autoTestMessage, "action"))
}
}
/**
* 自动化测试行为事件响应
*/
private fun onResponseAutoTestAction(autoTestMessage: AutoTestMessage, bitmap: Bitmap) {
uploadScope.launch {
val stream = ByteArrayOutputStream(2048)
val ok = bitmap.compress(Bitmap.CompressFormat.WEBP, 30, stream)
val bytes = stream.toByteArray()
val textPackage = JsonParser.toTextPackage(PackageType.AUTOTEST, autoTestMessage, "action")
val byteString = ByteParser.toByteString(textPackage, bytes)
webSocketClient.send(byteString)
stream.close()
}
}
inner class ScreenShotEventTask(private val controlEvent: ControlEvent) : Runnable {
private fun isCurrentEvent(): Boolean {
lastControlEvent?.let {
return it.eventId == controlEvent.eventId
}
return false
}
override fun run() {
if (isCurrentEvent()) {
lastControlEvent = null
screenShotNow(controlEvent)
}
}
}
abstract fun start()
abstract fun close()
}
package com.didichuxing.doraemonkit.kit.mc
import android.app.Activity
import android.view.View
import com.didichuxing.doraemonkit.kit.connect.data.TextPackage
import com.didichuxing.doraemonkit.kit.connect.ws.WebSocketClient
import com.didichuxing.doraemonkit.kit.test.DoKitTestManager
import com.didichuxing.doraemonkit.kit.test.TestMode
import com.didichuxing.doraemonkit.kit.test.event.ControlEvent
import com.didichuxing.doraemonkit.kit.test.event.ControlEventManager
import com.didichuxing.doraemonkit.kit.test.event.OnControlEventActionProcessListener
import com.didichuxing.doraemonkit.kit.test.mock.MockManager
import com.didichuxing.doraemonkit.kit.test.report.AutoTestMessage
import com.didichuxing.doraemonkit.kit.test.report.AutoTestState
import com.didichuxing.doraemonkit.util.GsonUtils
import kotlinx.coroutines.launch
/**
* didi Create on 2022/4/22 .
*
* Copyright (c) 2022/4/22 by didiglobal.com.
*
* @author <a href="realonlyone@126.com">zhangjun</a>
* @version 1.0
* @Date 2022/4/22 12:25 下午
* @Description 用一句话说明文件功能
*/
class ClientMultiController(webSocketClient: WebSocketClient) : AbstractMultiController(webSocketClient) {
private val onControlEventActionProcessListener = object : OnControlEventActionProcessListener {
override fun onControlEventProcessSuccess(activity: Activity?, view: View?, controlEvent: ControlEvent) {
val message = AutoTestMessage(command = "action_response", message = "success")
message.params["eventId"] = controlEvent.eventId
val state = AutoTestState(activity, view, controlEvent, message)
onControlEventProcessState(state)
}
override fun onControlEventProcessFailed(activity: Activity?, view: View?, controlEvent: ControlEvent, code: Int, message: String) {
val msg = AutoTestMessage(command = "action_response", message = "failed")
msg.params["eventId"] = controlEvent.eventId
msg.params["message"] = message
msg.params["code"] = "" + code
val state = AutoTestState(activity, view, controlEvent, msg)
onControlEventProcessState(state)
}
}
override fun start() {
ControlEventManager.addOnControlEventActionProcessListener(onControlEventActionProcessListener)
DoKitTestManager.startTest(TestMode.CLIENT)
MockManager.startTest(TestMode.CLIENT)
}
override fun close() {
DoKitTestManager.closeTest()
MockManager.closeTest()
ControlEventManager.removeOnControlEventActionProcessListener(onControlEventActionProcessListener)
}
/**
* 接收到自动化测试事件
*/
fun onReceiveAction(textPackage: TextPackage) {
val text = textPackage.data
val controlEvent = GsonUtils.fromJson<ControlEvent>(text, ControlEvent::class.java)
mainScope.launch {
screenShotForEvent(controlEvent)
ControlEventManager.onReceiveControlEventAction(controlEvent)
}
}
}
package com.didichuxing.doraemonkit.kit.mc
import android.os.Handler
import android.os.Looper
/**
* didi Create on 2022/4/18 .
*
* Copyright (c) 2022/4/18 by didiglobal.com.
*
* @author <a href="realonlyone@126.com">zhangjun</a>
* @version 1.0
* @Date 2022/4/18 3:11 下午
* @Description 用一句话说明文件功能
*/
class DelayHandler {
private val mainHandler: Handler = Handler(Looper.getMainLooper())
fun postDelayed(runnable: Runnable, delay: Long) {
mainHandler.postDelayed(runnable, delay)
}
fun removeCallbacks(runnable: Runnable) {
mainHandler.removeCallbacks(runnable)
}
}
package com.didichuxing.doraemonkit.kit.mc
import android.app.Activity
import android.view.View
import com.didichuxing.doraemonkit.kit.connect.data.PackageType
import com.didichuxing.doraemonkit.kit.connect.parser.JsonParser
import com.didichuxing.doraemonkit.kit.connect.ws.WebSocketClient
import com.didichuxing.doraemonkit.kit.test.DoKitTestManager
import com.didichuxing.doraemonkit.kit.test.TestMode
import com.didichuxing.doraemonkit.kit.test.event.ControlEvent
import com.didichuxing.doraemonkit.kit.test.event.ControlEventManager
import com.didichuxing.doraemonkit.kit.test.event.OnControlEventActionListener
import com.didichuxing.doraemonkit.kit.test.event.OnControlEventInterceptor
import com.didichuxing.doraemonkit.kit.test.mock.MockManager
import com.didichuxing.doraemonkit.mc.R
/**
* didi Create on 2022/4/22 .
*
* Copyright (c) 2022/4/22 by didiglobal.com.
*
* @author <a href="realonlyone@126.com">zhangjun</a>
* @version 1.0
* @Date 2022/4/22 12:25 下午
* @Description 主机控制实现
*/
class HostMultiController(webSocketClient: WebSocketClient) : AbstractMultiController(webSocketClient) {
private val onControlEventInterceptor = object : OnControlEventInterceptor {
override fun onControlEventAction(activity: Activity?, view: View?, controlEvent: ControlEvent): Boolean {
if (view != null && view.id == R.id.dokit_mode_switch_btn) {
return true
}
return false
}
}
private val onControlEventActionListener = object : OnControlEventActionListener {
override fun onControlEventAction(activity: Activity?, view: View?, event: ControlEvent) {
screenShotForEvent(event)
webSocketClient.let {
it.send(JsonParser.toJson(PackageType.BROADCAST, event, "action"))
}
}
}
override fun start() {
ControlEventManager.addOnControlEventInterceptor(onControlEventInterceptor)
ControlEventManager.addOnControlEventActionListener(onControlEventActionListener)
DoKitTestManager.startTest(TestMode.HOST)
MockManager.startTest(TestMode.HOST)
}
override fun close() {
ControlEventManager.removeOnControlEventInterceptor(onControlEventInterceptor)
ControlEventManager.removeOnControlEventActionListener(onControlEventActionListener)
DoKitTestManager.closeTest()
MockManager.closeTest()
}
}
package com.didichuxing.doraemonkit.kit.mc
import com.didichuxing.doraemonkit.kit.core.DoKitManager
import com.didichuxing.doraemonkit.kit.mc.oldui.DoKitMcManager
import com.didichuxing.doraemonkit.kit.mc.ui.adapter.McClientHistory
import com.didichuxing.doraemonkit.util.SPUtils
/**
* didi Create on 2022/4/22 .
*
* Copyright (c) 2022/4/22 by didiglobal.com.
*
* @author <a href="realonlyone@126.com">zhangjun</a>
* @version 1.0
* @Date 2022/4/22 12:25 下午
* @Description 一机多控配置
*/
object MultiControlConfig {
private const val DOKIT_MC_CONNECT_URL = "dokit_mc_connect_url"
private const val NAME_DOKIIT_MC_CONFIGALL = "dokiit-mc-config-all"
private var MC_CONNECT_URL = ""
var sp: SPUtils = SPUtils.getInstance(NAME_DOKIIT_MC_CONFIGALL)
var currentConnectHistory: McClientHistory? = null
set(value) {
val url = value?.url ?: ""
saveMcConnectUrl(url)
field = value
}
fun init() {
loadConfig()
}
fun loadConfig() {
val url = sp.getString(DoKitMcManager.DOKIT_MC_CONNECT_URL)
DoKitManager.MC_CONNECT_URL = url
MC_CONNECT_URL = url
}
fun saveMcConnectUrl(url: String) {
MC_CONNECT_URL = url
DoKitManager.MC_CONNECT_URL = url
sp.put(DOKIT_MC_CONNECT_URL, url)
}
}
......@@ -5,6 +5,7 @@ import android.content.Context
import android.content.Intent
import com.didichuxing.doraemonkit.aop.DokitPluginConfig
import com.didichuxing.doraemonkit.kit.AbstractKit
import com.didichuxing.doraemonkit.kit.mc.oldui.DoKitMcManager
import com.didichuxing.doraemonkit.kit.mc.ui.DoKitMcActivity
import com.didichuxing.doraemonkit.mc.R
import com.didichuxing.doraemonkit.util.DoKitCommUtil
......@@ -38,7 +39,7 @@ class MultiControlKit : AbstractKit() {
}
override fun onAppInit(context: Context?) {
// DexposedBridge.hookAllConstructors(AccessibilityManager::class.java, AllMethodHook())
MultiControlConfig.init()
DoKitMcManager.init()
}
......
package com.didichuxing.doraemonkit.kit.mc
import com.didichuxing.doraemonkit.DoKit
import com.didichuxing.doraemonkit.kit.connect.ConnectAddress
import com.didichuxing.doraemonkit.kit.connect.data.PackageType
import com.didichuxing.doraemonkit.kit.connect.data.TextPackage
import com.didichuxing.doraemonkit.kit.connect.parser.JsonParser
import com.didichuxing.doraemonkit.kit.connect.ws.*
import com.didichuxing.doraemonkit.kit.mc.ui.connect.MultiControlDoKitView
import com.didichuxing.doraemonkit.kit.test.TestMode
import com.didichuxing.doraemonkit.kit.test.mock.MockManager
import com.didichuxing.doraemonkit.kit.test.mock.ProxyMockCallback
import com.didichuxing.doraemonkit.kit.test.report.AutoTestMessage
import com.didichuxing.doraemonkit.util.GsonUtils
import com.didichuxing.doraemonkit.util.ToastUtils
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.plus
/**
* didi Create on 2022/4/22 .
*
* Copyright (c) 2022/4/22 by didiglobal.com.
*
* @author <a href="realonlyone@126.com">zhangjun</a>
* @version 1.0
* @Date 2022/4/22 12:25 下午
* @Description 一机多控管理
*/
object MultiControlManager {
private val mainScope = MainScope() + CoroutineName(this.toString())
private val webSocketClient: WebSocketClient = WebSocketClient()
private var connectAddress: ConnectAddress? = null
private val hostMultiController = HostMultiController(webSocketClient)
private val clientMultiController = ClientMultiController(webSocketClient)
private var webSocketClientCreate = false
private var mode: TestMode = TestMode.UNKNOWN
private val onModeChangeListeners: MutableSet<OnMultiControlModeChangeListener> = mutableSetOf()
fun getMode(): TestMode {
return mode
}
fun startHostMode(connectAddress: ConnectAddress) {
this.connectAddress = connectAddress
mode = TestMode.HOST
connect()
clientMultiController.close()
hostMultiController.start()
onNotifyMultiControlModeChange(mode)
}
private fun startHostMode() {
mode = TestMode.HOST
clientMultiController.close()
hostMultiController.start()
sendChangeHostMode()
onNotifyMultiControlModeChange(mode)
ToastUtils.showShort("主机模式")
}
fun startClientMode(connectAddress: ConnectAddress) {
this.connectAddress = connectAddress
mode = TestMode.CLIENT
connect()
hostMultiController.close()
clientMultiController.start()
onNotifyMultiControlModeChange(mode)
}
private fun startClientMode() {
mode = TestMode.CLIENT
hostMultiController.close()
clientMultiController.start()
onNotifyMultiControlModeChange(mode)
ToastUtils.showShort("从机模式")
}
fun changeMode(testMode: TestMode) {
if (testMode == TestMode.HOST) {
startHostMode()
} else if (testMode == TestMode.CLIENT) {
startClientMode()
}
MultiControlDoKitView.updateConnectMode()
}
fun changeMode() {
if (mode == TestMode.HOST) {
startClientMode()
} else if (mode == TestMode.CLIENT) {
startHostMode()
}
MultiControlDoKitView.updateConnectMode()
}
fun closeWorkMode() {
mode = TestMode.UNKNOWN
clientMultiController.close()
hostMultiController.close()
stopConnect()
onNotifyMultiControlModeChange(mode)
}
private fun sendChangeHostMode() {
webSocketClient.let {
it.send(JsonParser.toJson(PackageType.BROADCAST, "", "mc_host"))
}
}
private fun onReceiveHostModeChange() {
mainScope.launch {
if (mode == TestMode.HOST) {
changeMode(TestMode.CLIENT)
}
}
}
fun addOnMultiControlModeChangeListener(listener: OnMultiControlModeChangeListener) {
onModeChangeListeners.add(listener)
}
fun removeOnMultiControlModeChangeListener(listener: OnMultiControlModeChangeListener) {
onModeChangeListeners.remove(listener)
}
private fun onNotifyMultiControlModeChange(mode: TestMode) {
onModeChangeListeners.forEach {
it.onMultiControlModeChanged(mode)
}
}
private fun onReceiveControl(textPackage: TextPackage) {
val text = textPackage.data
val autoTestMessage = GsonUtils.fromJson<AutoTestMessage>(text, AutoTestMessage::class.java)
when (autoTestMessage.command) {
"startRecord" -> {
val msg = AutoTestMessage(command = "control_response", message = "success")
onResponseAutoTestMessage(msg)
}
}
}
/**
* 控制消息响应
*/
private fun onResponseAutoTestMessage(autoTestMessage: AutoTestMessage) {
webSocketClient?.let {
it.send(JsonParser.toJson(PackageType.AUTOTEST, autoTestMessage, "auto_test_control"))
}
}
/**
* 接收到自动化测试事件
*/
private fun onReceiveAction(textPackage: TextPackage) {
clientMultiController.onReceiveAction(textPackage)
}
private fun stopConnect() {
webSocketClient.close()
}
private fun connect() {
if (connectAddress == null) {
return
}
if (!webSocketClientCreate) {
webSocketClientCreate = true
webSocketClient.let {
it.addOnWebSocketLoginSuccessListener(object : OnWebSocketLoginSuccessListener {
override fun onWebSocketLoginSuccess() {
mainScope.launch {
DoKit.launchFloating(MultiControlDoKitView::class.java)
}
}
})
it.addOnWebSocketTextPackageListener(object : OnWebSocketTextPackageListener {
override fun onReceiveTextPackage(webSocket: OkHttpWebSocketSession, textPackage: TextPackage) {
if (textPackage.type == PackageType.AUTOTEST || textPackage.type == PackageType.BROADCAST) {
when (textPackage.contentType) {
"mc_host" -> {
onReceiveHostModeChange()
}
"auto_test_control" -> {
onReceiveControl(textPackage)
}
"action" -> {
onReceiveAction(textPackage)
}
else -> {
}
}
} else if (textPackage.type == PackageType.DATA) {
MockManager.receiveQueryResponse(textPackage)
}
}
})
it.addOnWebSocketCloseListener(object : OnWebSocketCloseListener {
override fun onWebSocketClose() {
mainScope.launch {
DoKit.removeFloating(MultiControlDoKitView::class.java)
}
}
})
it.addOnWebSocketStatusChangeListener(object : OnWebSocketStatusChangeListener {
override fun onClosed(webSocket: OkHttpWebSocketSession, code: Int, reason: String) {
}
override fun onOpen(webSocket: OkHttpWebSocketSession, response: String) {
}
override fun onFailure(webSocket: OkHttpWebSocketSession, t: Throwable, response: String?) {
ToastUtils.showShort("链接失败:" + t.message)
}
override fun onClosing(webSocket: OkHttpWebSocketSession, code: Int, reason: String) {
}
})
MockManager.proxyMockCallback = object : ProxyMockCallback {
override fun send(data: String) {
webSocketClient.send(data)
}
}
it.startAutoConnect()
it.connect(connectAddress!!.url)
}
} else {
webSocketClient.startAutoConnect()
webSocketClient.reConnect(connectAddress!!.url)
}
}
}
package com.didichuxing.doraemonkit.kit.mc
import com.didichuxing.doraemonkit.kit.test.TestMode
/**
* didi Create on 2022/4/14 .
*
* Copyright (c) 2022/4/14 by didiglobal.com.
*
* @author <a href="realonlyone@126.com">zhangjun</a>
* @version 1.0
* @Date 2022/4/14 10:52 上午
* @Description 用一句话说明文件功能
*/
interface OnMultiControlModeChangeListener {
fun onMultiControlModeChanged(testMode: TestMode)
}
......@@ -16,9 +16,9 @@ import com.google.auto.service.AutoService
* 模块能力相关代码
*/
@AutoService(DokitAbility::class)
class DokitMcAbility : DokitAbility {
class DoKitMcAbility : DokitAbility {
private val TAG = "DokitMcAbility"
private val TAG = "DoKitMcAbility"
override fun init() {
LogHelper.i(TAG, "init()")
}
......@@ -28,7 +28,7 @@ class DokitMcAbility : DokitAbility {
}
override fun getModuleProcessor(): DokitAbility.DokitModuleProcessor {
return DokitMcModuleProcessor()
return DoKitMcModuleProcessor()
}
}
......@@ -4,16 +4,16 @@ import android.view.View
import com.didichuxing.doraemonkit.DoKit
import com.didichuxing.doraemonkit.kit.core.DokitAbility
import com.didichuxing.doraemonkit.kit.test.event.monitor.LifecycleEventMonitor
import com.didichuxing.doraemonkit.kit.mc.DoKitMcManager
import com.didichuxing.doraemonkit.kit.mc.net.DokitMcConnectManager
import com.didichuxing.doraemonkit.kit.mc.oldui.client.ClientDokitView
import com.didichuxing.doraemonkit.kit.mc.oldui.DoKitMcManager
import com.didichuxing.doraemonkit.kit.mc.MultiControlConfig
import com.didichuxing.doraemonkit.kit.mc.oldui.client.ClientDoKitView
import com.didichuxing.doraemonkit.kit.test.mock.http.DoKitMockInterceptor
import com.didichuxing.doraemonkit.kit.mc.oldui.host.HostDokitView
import com.didichuxing.doraemonkit.kit.mc.oldui.record.RecordingDokitView
import com.didichuxing.doraemonkit.kit.mc.oldui.host.HostDoKitView
import com.didichuxing.doraemonkit.kit.mc.oldui.record.RecordingDoKitView
import com.didichuxing.doraemonkit.kit.test.DoKitTestManager
import com.didichuxing.doraemonkit.kit.test.TestMode
import com.didichuxing.doraemonkit.kit.test.event.monitor.CustomEventMonitor
import com.didichuxing.doraemonkit.kit.test.mock.http.DoKitProxyMockInterceptor
import com.didichuxing.doraemonkit.kit.test.util.XposedHookUtils
import com.didichuxing.doraemonkit.kit.test.utils.XposedHookUtil
import com.didichuxing.doraemonkit.util.LogHelper
import com.didichuxing.doraemonkit.util.SPUtils
......@@ -26,7 +26,7 @@ import com.didichuxing.doraemonkit.util.SPUtils
* 修订历史:
* ================================================
*/
class DokitMcModuleProcessor : DokitAbility.DokitModuleProcessor {
class DoKitMcModuleProcessor : DokitAbility.DokitModuleProcessor {
override fun values(): Map<String, Any> {
return mapOf(
......@@ -41,18 +41,18 @@ class DokitMcModuleProcessor : DokitAbility.DokitModuleProcessor {
actions?.let {
when (actions["action"]) {
"launch_host_view" -> {
DoKit.launchFloating(HostDokitView::class)
DoKit.launchFloating(HostDoKitView::class)
}
"launch_client_view" -> {
DoKit.launchFloating(ClientDokitView::class)
DoKit.launchFloating(ClientDoKitView::class)
}
"launch_recoding_view" -> {
if (DoKitMcManager.IS_MC_RECODING ||
SPUtils.getInstance()
.getBoolean(DoKitMcManager.MC_CASE_RECODING_KEY, false)
) {
DoKit.launchFloating(RecordingDokitView::class)
DoKit.launchFloating(RecordingDoKitView::class)
DoKitMcManager.IS_MC_RECODING = true
DoKitMcManager.MC_CASE_ID =
SPUtils.getInstance().getString(DoKitMcManager.MC_CASE_ID_KEY)
......@@ -71,18 +71,18 @@ class DokitMcModuleProcessor : DokitAbility.DokitModuleProcessor {
return mapOf(Pair("mode", mode))
}
"mc_custom_event" -> {
DoKitMcManager.sendCustomEvent(
CustomEventMonitor.onCustomEvent(
actions["eventType"] as String,
actions["view"] as View?,
actions["param"] as Map<String, String>?
)
}
"global_hook" -> {
XposedHookUtils.globalHook()
XposedHookUtil.globalHook()
}
"dokit_mc_connect_url" -> {
val map = mutableMapOf<String, String>()
val history = DokitMcConnectManager.currentConnectHistory
val history = MultiControlConfig.currentConnectHistory
map["url"] = history?.url ?: ""
}
......
......@@ -4,7 +4,7 @@ import androidx.appcompat.app.AlertDialog
import com.didichuxing.doraemonkit.DoKit
import com.didichuxing.doraemonkit.extension.doKitGlobalScope
import com.didichuxing.doraemonkit.kit.test.mock.data.HostInfo
import com.didichuxing.doraemonkit.kit.mc.oldui.client.ClientDokitView
import com.didichuxing.doraemonkit.kit.mc.oldui.client.ClientDoKitView
import com.didichuxing.doraemonkit.kit.test.event.ControlEvent
import com.didichuxing.doraemonkit.kit.test.event.EventType
import com.didichuxing.doraemonkit.kit.mc.utils.WSPackageUtils
......@@ -101,7 +101,7 @@ object DoKitMcClient {
doKitGlobalScope.launch {
DoKitMcClient.close()
}
DoKit.removeFloating(ClientDokitView::class)
DoKit.removeFloating(ClientDoKitView::class)
if (ActivityUtils.getTopActivity() != null) {
AlertDialog.Builder(ActivityUtils.getTopActivity())
.setTitle("一机多控")
......
package com.didichuxing.doraemonkit.kit.mc.net
import android.text.TextUtils
import com.didichuxing.doraemonkit.kit.connect.data.LoginData
import com.didichuxing.doraemonkit.kit.connect.data.PackageType
import com.didichuxing.doraemonkit.kit.connect.data.TextPackage
import com.didichuxing.doraemonkit.kit.core.DoKitManager
import com.didichuxing.doraemonkit.kit.test.event.ControlEvent
import com.didichuxing.doraemonkit.kit.test.event.EventType
import com.didichuxing.doraemonkit.kit.mc.utils.WSPackageUtils
import com.didichuxing.doraemonkit.kit.test.mock.MockManager
import com.didichuxing.doraemonkit.util.*
import io.ktor.client.*
import io.ktor.client.engine.cio.*
import io.ktor.client.engine.okhttp.*
import io.ktor.client.features.websocket.*
import io.ktor.client.request.*
import io.ktor.http.*
import io.ktor.http.cio.websocket.*
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.consumeEach
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/11/13-15:38
* 描 述:
* 修订历史:
* ================================================
*/
object DoKitMcConnectClient {
const val TAG = "DoKitWsConnectServer"
private var clientWebSocketSession: DefaultClientWebSocketSession? = null
const val CONNECT_SUCCEED = 200
const val CONNECT_FAILED = 0
var authLoginData: LoginData? = null
private val client: HttpClient by lazy {
try {
Class.forName("io.ktor.client.engine.okhttp.OkHttp")
LogHelper.i(TAG, "client engine is OkHttp")
return@lazy HttpClient(OkHttp) {
install(WebSockets)
}
} catch (e: ClassNotFoundException) {
LogHelper.i(TAG, "client engine is CIO")
return@lazy HttpClient(CIO) {
install(WebSockets)
}
}
}
fun connect(host: String, port: Int, path: String, connectSerial: String, callBack: suspend (Int, String?) -> Unit) {
CoroutineScope(Dispatchers.IO).launch {
try {
val client = client.ws(
method = HttpMethod.Get,
host = host,
port = port,
path = path,
request = {
headers {
header(
"deviceModel",
"${DeviceUtils.getManufacturer()}-${DeviceUtils.getModel()}"
)
}
}
) {
DokitMcConnectManager.connectMode = ConnectMode.LOGIN
clientWebSocketSession = this
onConnectSuccess(connectSerial)
DokitMcConnectManager.connectMode = ConnectMode.CONNECT
clientWebSocketSession?.let {
CoroutineScope(it.coroutineContext).launch {
}
}
incoming.consumeEach {
try {
onHandleMessage(it, this, callBack)
} catch (e: Exception) {
e.printStackTrace()
}
}
}
} catch (e: Exception) {
e.printStackTrace()
LogHelper.e(TAG, "client connect error===>${e.message}")
callBack(CONNECT_FAILED, e.message)
}
}
}
private fun onConnectSuccess(connectSerial: String) {
login(connectSerial)
}
private suspend fun onHandleMessage(it: Frame, session: DefaultClientWebSocketSession, callBack: suspend (Int, String?) -> Unit) {
when (it) {
is Frame.Text -> {
val packageText = it.readText()
LogHelper.json(TAG, packageText)
val text = GsonUtils.fromJson<TextPackage>(
packageText,
TextPackage::class.java
)
if (text?.type != null) {
when (text.type) {
PackageType.LOGIN -> {
DokitMcConnectManager.connectMode = ConnectMode.CONNECT
val data = text.data
try {
val loginData = GsonUtils.fromJson<LoginData>(data, LoginData::class.java)
authLoginData = loginData
callBack(CONNECT_SUCCEED, data)
} catch (e: Exception) {
e.printStackTrace()
LogHelper.e(TAG, "client handle error===>${e.message}")
}
}
PackageType.NOTIFY -> {
LogHelper.e(TAG, "client handle notify===>${text.data}")
}
PackageType.BROADCAST -> {
if (TextUtils.equals(text.contentType, "mc_host")) {
DokitMcConnectManager.changeClientMode()
} else {
val serverInfo = text.data
try {
process(serverInfo)
} catch (e: Exception) {
e.printStackTrace()
LogHelper.e(TAG, "client handle error===>${e.message}")
}
}
}
PackageType.DATA -> {
MockManager.receiveQueryResponse(text)
}
}
}
}
is Frame.Binary -> {
LogHelper.i(TAG, "Binary data=${it}")
}
is Frame.Close -> {
LogHelper.i(TAG, "Close data=${it}")
}
is Frame.Ping -> {
LogHelper.i(TAG, "Ping data=${it}")
}
is Frame.Pong -> {
LogHelper.i(TAG, "Pong data=${it}")
}
else -> {
LogHelper.e(TAG, "type error===>${it}")
}
}
}
private suspend fun process(serverInfo: String) {
val wsEvent =
GsonUtils.fromJson<ControlEvent>(
serverInfo,
ControlEvent::class.java
)
//连接成功的返回信息
if (wsEvent.eventType == EventType.WSE_CONNECTED) {
}
//断开连接
if (wsEvent.eventType == EventType.WSE_CLOSE) {
}
WSEventProcessor.process(wsEvent)
}
fun close() {
try {
clientWebSocketSession?.let {
CoroutineScope(it.coroutineContext).launch {
clientWebSocketSession?.close()
}
}
DokitMcConnectManager.connectMode = ConnectMode.CLOSE
} catch (e: Exception) {
e.printStackTrace()
}
}
fun send(wsEvent: ControlEvent) {
clientWebSocketSession?.let {
CoroutineScope(it.coroutineContext).launch {
if (it.isActive) {
val wsPackage = WSPackageUtils.toPackageJson(wsEvent)
it.outgoing.send(Frame.Text(wsPackage))
}
}
}
}
fun sendDataProxy(proxyPackage: String) {
clientWebSocketSession?.let {
CoroutineScope(it.coroutineContext).launch {
if (it.isActive) {
it.outgoing.send(Frame.Text(proxyPackage))
}
}
}
}
fun sendChangeHostMode() {
clientWebSocketSession?.let {
CoroutineScope(it.coroutineContext).launch {
if (it.isActive) {
val wsPackage = WSPackageUtils.toModePackageJson(PackageType.BROADCAST, "mc_host")
it.outgoing.send(Frame.Text(wsPackage))
}
}
}
}
fun getConnectSerial(): String {
authLoginData?.let {
return it.connectSerial
}
return ""
}
private fun login(connectSerial: String) {
clientWebSocketSession?.let {
CoroutineScope(it.coroutineContext).launch {
if (it.isActive) {
val pi = DokitDeviceUtils.getPackageInfo(Utils.getApp())
val name = "${DeviceUtils.getManufacturer()}-${DeviceUtils.getModel()}(${DeviceUtils.getSDKVersionName()})"
val loginData = LoginData(
name,
"android",
"${DeviceUtils.getSDKVersionName()}",
"${UIUtils.getWidthPixels()} x ${UIUtils.getRealHeightPixels()}",
"${DeviceUtils.getManufacturer()}-${DeviceUtils.getModel()}",
"${DoKitManager.IP_ADDRESS_BY_WIFI}",
connectSerial,
"${pi.packageName}",
"${pi.versionName}"
)
val data = GsonUtils.toJson(loginData)
it.outgoing.send(Frame.Text(WSPackageUtils.toPackageJson(PackageType.LOGIN, data)))
}
}
}
}
fun session(): DefaultClientWebSocketSession? {
return clientWebSocketSession;
}
}
package com.didichuxing.doraemonkit.kit.mc.net
import com.didichuxing.doraemonkit.kit.mc.DoKitMcManager
import com.didichuxing.doraemonkit.kit.mc.oldui.DoKitMcManager
import com.didichuxing.doraemonkit.kit.mc.ui.adapter.McClientHistory
import com.didichuxing.doraemonkit.kit.mc.ui.connect.ConnectDokitView
import com.didichuxing.doraemonkit.kit.test.TestMode
import com.didichuxing.doraemonkit.util.ToastUtils
object DokitMcConnectManager {
......@@ -12,6 +9,7 @@ object DokitMcConnectManager {
var connectMode: ConnectMode = ConnectMode.CLOSE
var itemHistory: McClientHistory? = null
var currentConnectHistory: McClientHistory? = null
set(value) {
val url = value?.url ?: ""
......@@ -21,28 +19,4 @@ object DokitMcConnectManager {
var currentClientHistory: McClientHistory? = null
var dokitFloatViews = mutableListOf<ConnectDokitView>()
fun changeHostMode() {
DoKitMcManager.CONNECT_MODE = TestMode.HOST
DoKitMcManager.startHostMode()
updateConnectModeDokitView()
DoKitMcConnectClient.sendChangeHostMode()
ToastUtils.showShort("主机模式")
}
fun changeClientMode() {
DoKitMcManager.CONNECT_MODE = TestMode.CLIENT
DoKitMcManager.startClientMode()
updateConnectModeDokitView()
ToastUtils.showShort("从机模式")
}
private fun updateConnectModeDokitView() {
dokitFloatViews.forEach {
it.updateConnectMode()
}
}
}
package com.didichuxing.doraemonkit.kit.mc
package com.didichuxing.doraemonkit.kit.mc.oldui
import android.app.Activity
import android.view.View
import com.didichuxing.doraemonkit.kit.core.DoKitManager
import com.didichuxing.doraemonkit.kit.test.event.OnControlEventActionListener
import com.didichuxing.doraemonkit.kit.test.event.monitor.CustomEventMonitor
import com.didichuxing.doraemonkit.kit.test.TestMode
import com.didichuxing.doraemonkit.kit.test.mock.data.HostInfo
import com.didichuxing.doraemonkit.kit.test.mock.MockManager
import com.didichuxing.doraemonkit.kit.test.mock.ProxyMockCallback
import com.didichuxing.doraemonkit.kit.mc.net.DoKitMcConnectClient
import com.didichuxing.doraemonkit.kit.test.DoKitTestManager
import com.didichuxing.doraemonkit.kit.test.event.ControlEvent
import com.didichuxing.doraemonkit.kit.test.event.ControlEventManager
import com.didichuxing.doraemonkit.mc.R
import com.didichuxing.doraemonkit.util.SPUtils
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/12/9-17:42
* 描 述:
* 修订历史:
* ================================================
* didi Create on 2022/4/22 .
*
* Copyright (c) 2022/4/22 by didiglobal.com.
*
* @author <a href="realonlyone@126.com">zhangjun</a>
* @version 1.0
* @Date 2022/4/22 12:25 下午
* @Description 用一句话说明文件功能
*/
object DoKitMcManager {
/**
* 主机
*/
const val MULTI_CONTROL_MODE_SERVER = 100
/**
* 从机
*/
const val MULTI_CONTROL_MODE_CLIENT = 200
const val MC_CASE_ID_KEY = "MC_CASE_ID"
const val MC_CASE_RECODING_KEY = "MC_CASE_RECODING"
const val DOKIT_MC_CONNECT_URL = "dokit_mc_connect_url"
......@@ -49,7 +29,6 @@ object DoKitMcManager {
*/
var IS_MC_RECODING = false
/**
* 主机信息
*/
......@@ -64,24 +43,14 @@ object DoKitMcManager {
var sp: SPUtils = SPUtils.getInstance(NAME_DOKIIT_MC_CONFIGALL)
private var mode: TestMode = TestMode.UNKNOWN
fun getMode(): TestMode {
return mode
}
fun init() {
loadConfig()
ControlEventManager.addOnControlEventActionListener(object : OnControlEventActionListener {
override fun onControlEventAction(activity: Activity?, view: View?, event: ControlEvent) {
if (view != null && view.id == R.id.dokit_mode_switch_btn) {
return
}
DoKitMcConnectClient.send(event)
}
})
MockManager.proxyMockCallback = object : ProxyMockCallback {
override fun send(data: String) {
DoKitMcConnectClient.sendDataProxy(data)
}
}
}
fun loadConfig() {
......@@ -95,35 +64,4 @@ object DoKitMcManager {
}
/**
* 发送自定义事件
* @return view
* @return eventType 事件类型
* @return param 自定义参数
*/
fun sendCustomEvent(eventType: String, view: View? = null, param: Map<String, String>? = null) {
if (!DoKitTestManager.isHostMode()) {
return
}
if (DoKitManager.MC_CLIENT_PROCESSOR == null) {
return
}
CustomEventMonitor.onCustomEvent(eventType, view, param)
}
fun startHostMode() {
DoKitTestManager.startTest(TestMode.HOST)
}
fun startClientMode() {
DoKitTestManager.startTest(TestMode.CLIENT)
}
fun closeWorkMode() {
DoKitTestManager.closeTest()
}
}
......@@ -5,8 +5,8 @@ import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.widget.FrameLayout
import com.didichuxing.doraemonkit.kit.core.AbsDokitView
import com.didichuxing.doraemonkit.kit.core.DokitViewLayoutParams
import com.didichuxing.doraemonkit.kit.core.AbsDoKitView
import com.didichuxing.doraemonkit.kit.core.DoKitViewLayoutParams
import com.didichuxing.doraemonkit.kit.mc.ui.McPages
import com.didichuxing.doraemonkit.kit.mc.utils.McPageUtils
import com.didichuxing.doraemonkit.mc.R
......@@ -21,7 +21,7 @@ import com.didichuxing.doraemonkit.util.ConvertUtils
* 修订历史:
* ================================================
*/
class ClientDokitView : AbsDokitView() {
class ClientDoKitView : AbsDoKitView() {
override fun onCreate(context: Context?) {
}
......@@ -36,7 +36,7 @@ class ClientDokitView : AbsDokitView() {
}
}
override fun initDokitViewLayoutParams(params: DokitViewLayoutParams) {
override fun initDokitViewLayoutParams(params: DoKitViewLayoutParams) {
params.width = ConvertUtils.dp2px(70.0f)
params.height = ConvertUtils.dp2px(70.0f)
params.gravity = Gravity.TOP or Gravity.LEFT
......
......@@ -7,7 +7,7 @@ import androidx.lifecycle.lifecycleScope
import com.didichuxing.doraemonkit.DoKit
import com.didichuxing.doraemonkit.kit.test.TestMode
import com.didichuxing.doraemonkit.kit.core.BaseFragment
import com.didichuxing.doraemonkit.kit.mc.DoKitMcManager
import com.didichuxing.doraemonkit.kit.mc.oldui.DoKitMcManager
import com.didichuxing.doraemonkit.kit.mc.ui.DoKitMcActivity
import com.didichuxing.doraemonkit.kit.mc.ui.adapter.McClientHistory
import com.didichuxing.doraemonkit.kit.mc.net.DoKitMcClient
......@@ -42,9 +42,8 @@ class DoKitMcClientFragment : BaseFragment() {
findViewById<View>(R.id.btn_close).setOnClickListener {
lifecycleScope.launch {
DoKitMcManager.WS_MODE = TestMode.UNKNOWN
DoKit.removeFloating(ClientDokitView::class)
DoKit.removeFloating(ClientDoKitView::class)
DoKitMcClient.close()
DoKitMcManager.closeWorkMode()
if (activity is DoKitMcActivity) {
(activity as DoKitMcActivity).onBackPressed()
}
......
......@@ -15,7 +15,7 @@ import com.didichuxing.doraemonkit.DoKit
import com.didichuxing.doraemonkit.kit.test.TestMode
import com.didichuxing.doraemonkit.extension.isTrueWithCor
import com.didichuxing.doraemonkit.kit.core.BaseFragment
import com.didichuxing.doraemonkit.kit.mc.DoKitMcManager
import com.didichuxing.doraemonkit.kit.mc.oldui.DoKitMcManager
import com.didichuxing.doraemonkit.kit.mc.net.DoKitMcClient
import com.didichuxing.doraemonkit.kit.mc.net.DokitMcConnectManager
import com.didichuxing.doraemonkit.kit.mc.ui.*
......@@ -211,7 +211,7 @@ class DoKitMcClientHistoryFragment : BaseFragment() {
DokitMcConnectManager.currentClientHistory = clientHistory
updateHistoryView()
//启动悬浮窗
DoKit.launchFloating(ClientDokitView::class)
DoKit.launchFloating(ClientDoKitView::class)
}
DoKitMcClient.CONNECT_FAIL -> {
DoKitTestManager.closeTest()
......
......@@ -12,7 +12,7 @@ import com.didichuxing.doraemonkit.kit.test.TestMode
import com.didichuxing.doraemonkit.kit.core.BaseFragment
import com.didichuxing.doraemonkit.kit.core.DoKitManager
import com.didichuxing.doraemonkit.kit.mc.ui.DoKitMcActivity
import com.didichuxing.doraemonkit.kit.mc.DoKitMcManager
import com.didichuxing.doraemonkit.kit.mc.oldui.DoKitMcManager
import com.didichuxing.doraemonkit.kit.mc.net.DoKitMcHostServer
import com.didichuxing.doraemonkit.kit.mc.utils.CodeUtils
import com.didichuxing.doraemonkit.mc.R
......@@ -49,7 +49,7 @@ class DoKitMcHostFragment : BaseFragment() {
lifecycleScope.launch(exceptionHandler) {
DoKitMcHostServer.stop {
DoKitMcManager.WS_MODE = TestMode.UNKNOWN
DoKit.removeFloating(HostDokitView::class)
DoKit.removeFloating(HostDoKitView::class)
if (activity is DoKitMcActivity) {
(activity as DoKitMcActivity).onBackPressed()
}
......@@ -68,8 +68,7 @@ class DoKitMcHostFragment : BaseFragment() {
DoKitMcHostServer.start {
DoKitMcManager.WS_MODE = TestMode.HOST
//启动悬浮窗
DoKit.launchFloating(HostDokitView::class)
DoKitMcManager.startHostMode()
DoKit.launchFloating(HostDoKitView::class)
}
}
}
......
......@@ -5,8 +5,8 @@ import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.widget.FrameLayout
import com.didichuxing.doraemonkit.kit.core.AbsDokitView
import com.didichuxing.doraemonkit.kit.core.DokitViewLayoutParams
import com.didichuxing.doraemonkit.kit.core.AbsDoKitView
import com.didichuxing.doraemonkit.kit.core.DoKitViewLayoutParams
import com.didichuxing.doraemonkit.kit.mc.ui.McPages
import com.didichuxing.doraemonkit.kit.mc.utils.McPageUtils
import com.didichuxing.doraemonkit.mc.R
......@@ -21,7 +21,7 @@ import com.didichuxing.doraemonkit.util.ConvertUtils
* 修订历史:
* ================================================
*/
class HostDokitView : AbsDokitView() {
class HostDoKitView : AbsDoKitView() {
override fun onCreate(context: Context?) {
}
......@@ -36,7 +36,7 @@ class HostDokitView : AbsDokitView() {
}
}
override fun initDokitViewLayoutParams(params: DokitViewLayoutParams) {
override fun initDokitViewLayoutParams(params: DoKitViewLayoutParams) {
params.width = ConvertUtils.dp2px(70.0f)
params.height = ConvertUtils.dp2px(70.0f)
params.gravity = Gravity.TOP or Gravity.LEFT
......
......@@ -13,13 +13,13 @@ import com.didichuxing.doraemonkit.util.ToastUtils
import com.didichuxing.doraemonkit.kit.core.BaseFragment
import com.didichuxing.doraemonkit.kit.test.mock.http.HttpMockServer
import com.didichuxing.doraemonkit.kit.test.mock.http.HttpMockServer.RESPONSE_OK
import com.didichuxing.doraemonkit.kit.mc.DoKitMcManager
import com.didichuxing.doraemonkit.kit.mc.oldui.DoKitMcManager
import com.didichuxing.doraemonkit.kit.mc.ui.DoKitMcActivity
import com.didichuxing.doraemonkit.kit.mc.ui.adapter.McCaseInfoDialogProvider
import com.didichuxing.doraemonkit.kit.mc.ui.McPages
import com.didichuxing.doraemonkit.kit.test.mock.data.McCaseInfo
import com.didichuxing.doraemonkit.kit.test.mock.data.McConfigInfo
import com.didichuxing.doraemonkit.kit.mc.oldui.record.RecordingDokitView
import com.didichuxing.doraemonkit.kit.mc.oldui.record.RecordingDoKitView
import com.didichuxing.doraemonkit.kit.test.mock.data.CaseInfo
import com.didichuxing.doraemonkit.kit.mc.utils.McCaseUtils
import com.didichuxing.doraemonkit.kit.test.DoKitTestManager
......@@ -140,7 +140,7 @@ class DoKitMcMainFragment : BaseFragment() {
lifecycleScope.launch(mExceptionHandler) {
val result = HttpMockServer.mockStop<Any>(mcCaseInfoDialog())
if (result.code == RESPONSE_OK) {
DoKit.removeFloating(RecordingDokitView::class)
DoKit.removeFloating(RecordingDoKitView::class)
SPUtils.getInstance().put(DoKitMcManager.MC_CASE_RECODING_KEY, false)
DoKitTestManager.closeTest()
DoKitMcManager.IS_MC_RECODING = false
......@@ -203,7 +203,7 @@ class DoKitMcMainFragment : BaseFragment() {
private fun saveRecodingStatus(configInfo: McCaseInfo?) {
configInfo?.let {
DoKitMcManager.MC_CASE_ID = it.caseId
DoKit.launchFloating(RecordingDokitView::class.java)
DoKit.launchFloating(RecordingDoKitView::class.java)
DoKitTestManager.startTest(TestMode.HOST)
SPUtils.getInstance().put(DoKitMcManager.MC_CASE_ID_KEY, DoKitMcManager.MC_CASE_ID)
SPUtils.getInstance().put(DoKitMcManager.MC_CASE_RECODING_KEY, true)
......
......@@ -9,9 +9,9 @@ import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.TextView
import com.didichuxing.doraemonkit.DoKit
import com.didichuxing.doraemonkit.kit.core.AbsDokitView
import com.didichuxing.doraemonkit.kit.core.DokitViewLayoutParams
import com.didichuxing.doraemonkit.kit.mc.DoKitMcManager
import com.didichuxing.doraemonkit.kit.core.AbsDoKitView
import com.didichuxing.doraemonkit.kit.core.DoKitViewLayoutParams
import com.didichuxing.doraemonkit.kit.mc.oldui.DoKitMcManager
import com.didichuxing.doraemonkit.kit.mc.ui.DoKitMcActivity
import com.didichuxing.doraemonkit.kit.test.DoKitTestManager
import com.didichuxing.doraemonkit.mc.R
......@@ -36,7 +36,7 @@ import kotlinx.coroutines.launch
* 修订历史:
* ================================================
*/
class RecordingDokitView : AbsDokitView() {
class RecordingDoKitView : AbsDoKitView() {
private var mRedDot: View? = null
private var mExtend: TextView? = null
private lateinit var mDotFlow: Flow<Int>
......@@ -77,7 +77,7 @@ class RecordingDokitView : AbsDokitView() {
}
rootView?.findViewById<ImageView>(R.id.iv_close)?.setOnClickListener {
//TODO 需要补充取消录制接口
DoKit.removeFloating(RecordingDokitView::class)
DoKit.removeFloating(RecordingDoKitView::class)
SPUtils.getInstance().put(DoKitMcManager.MC_CASE_RECODING_KEY, false)
DoKitTestManager.closeTest()
DoKitMcManager.IS_MC_RECODING = false
......@@ -116,9 +116,9 @@ class RecordingDokitView : AbsDokitView() {
}
override fun initDokitViewLayoutParams(params: DokitViewLayoutParams) {
params.width = DokitViewLayoutParams.WRAP_CONTENT
params.height = DokitViewLayoutParams.WRAP_CONTENT
override fun initDokitViewLayoutParams(params: DoKitViewLayoutParams) {
params.width = DoKitViewLayoutParams.WRAP_CONTENT
params.height = DoKitViewLayoutParams.WRAP_CONTENT
params.gravity = Gravity.TOP or Gravity.LEFT
params.x = ConvertUtils.dp2px(25f)
params.y = ConvertUtils.dp2px(25f)
......
......@@ -6,8 +6,8 @@ import android.view.View;
import android.widget.FrameLayout;
import com.didichuxing.doraemonkit.R;
import com.didichuxing.doraemonkit.kit.core.AbsDokitView;
import com.didichuxing.doraemonkit.kit.core.DokitViewLayoutParams;
import com.didichuxing.doraemonkit.kit.core.AbsDoKitView;
import com.didichuxing.doraemonkit.kit.core.DoKitViewLayoutParams;
import com.didichuxing.doraemonkit.kit.viewcheck.LayoutBorderView;
import com.didichuxing.doraemonkit.model.ViewInfo;
......@@ -15,7 +15,7 @@ import com.didichuxing.doraemonkit.model.ViewInfo;
* Created by jintai on 2019/09/26.
* 在相应的界面上绘制指定View的边框
*/
public class BorderDoKitView extends AbsDokitView {
public class BorderDoKitView extends AbsDoKitView {
private LayoutBorderView mLayoutBorderView = null;
@Override
......@@ -36,10 +36,10 @@ public class BorderDoKitView extends AbsDokitView {
@Override
public void initDokitViewLayoutParams(DokitViewLayoutParams params) {
params.flags = DokitViewLayoutParams.FLAG_NOT_FOCUSABLE_AND_NOT_TOUCHABLE;
params.width = DokitViewLayoutParams.MATCH_PARENT;
params.height = DokitViewLayoutParams.MATCH_PARENT;
public void initDokitViewLayoutParams(DoKitViewLayoutParams params) {
params.flags = DoKitViewLayoutParams.FLAG_NOT_FOCUSABLE_AND_NOT_TOUCHABLE;
params.width = DoKitViewLayoutParams.MATCH_PARENT;
params.height = DoKitViewLayoutParams.MATCH_PARENT;
}
......
......@@ -2,12 +2,11 @@ package com.didichuxing.doraemonkit.kit.mc.ui
import android.os.Bundle
import android.text.TextUtils
import com.didichuxing.doraemonkit.kit.core.BaseActivity
import com.didichuxing.doraemonkit.kit.core.BaseFragment
import com.didichuxing.doraemonkit.kit.core.NewBaseActivity
import com.didichuxing.doraemonkit.kit.mc.oldui.client.DoKitMcClientFragment
import com.didichuxing.doraemonkit.kit.mc.oldui.client.DoKitMcClientHistoryFragment
import com.didichuxing.doraemonkit.kit.mc.ui.connect.DoKitMcConnectFragment
import com.didichuxing.doraemonkit.kit.mc.ui.connect.DoKitMcConnectHistoryFragment
import com.didichuxing.doraemonkit.kit.mc.ui.connect.MultiControlAllFragment
import com.didichuxing.doraemonkit.kit.mc.oldui.host.DoKitMcHostFragment
import com.didichuxing.doraemonkit.kit.mc.oldui.main.DoKitMcMainFragment
import com.didichuxing.doraemonkit.kit.mc.oldui.record.DoKitMcDatasFragment
......@@ -23,7 +22,7 @@ import com.didichuxing.doraemonkit.widget.titlebar.HomeTitleBar
* 修订历史:
* ================================================
*/
class DoKitMcActivity : BaseActivity() {
class DoKitMcActivity : NewBaseActivity() {
private lateinit var mTitlebar: HomeTitleBar
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
......@@ -70,13 +69,9 @@ class DoKitMcActivity : BaseActivity() {
mTitlebar.setTitle("一机多控(原)")
DoKitMcMainFragment()
}
McPages.CONNECT -> {
mTitlebar.setTitle("一机多控(联网")
DoKitMcConnectFragment()
}
McPages.CONNECT_HISTORY -> {
mTitlebar.setTitle("一机多控")
DoKitMcConnectHistoryFragment()
MultiControlAllFragment()
}
McPages.HOST -> {
mTitlebar.setTitle("一机多控(主机")
......
......@@ -7,15 +7,15 @@ import android.view.View
import android.widget.FrameLayout
import android.widget.TextView
import com.didichuxing.doraemonkit.DoKit
import com.didichuxing.doraemonkit.kit.core.AbsDokitView
import com.didichuxing.doraemonkit.kit.core.DokitViewLayoutParams
import com.didichuxing.doraemonkit.kit.core.AbsDoKitView
import com.didichuxing.doraemonkit.kit.core.DoKitViewLayoutParams
import com.didichuxing.doraemonkit.mc.R
/**
* Created by jintai on 2019/09/26.
* 在相应的界面上弹出提示框
*/
class McDialogDoKitView : AbsDokitView() {
class McDialogDoKitView : AbsDoKitView() {
lateinit var mTvExceptionType: TextView
lateinit var mTvOk: TextView
......@@ -29,11 +29,11 @@ class McDialogDoKitView : AbsDokitView() {
return LayoutInflater.from(context).inflate(R.layout.dk_dokitview_dialog, null)
}
override fun initDokitViewLayoutParams(params: DokitViewLayoutParams) {
params.flags = DokitViewLayoutParams.FLAG_NOT_FOCUSABLE_AND_NOT_TOUCHABLE
override fun initDokitViewLayoutParams(params: DoKitViewLayoutParams) {
params.flags = DoKitViewLayoutParams.FLAG_NOT_FOCUSABLE_AND_NOT_TOUCHABLE
params.gravity = Gravity.CENTER
params.width = DokitViewLayoutParams.MATCH_PARENT
params.height = DokitViewLayoutParams.MATCH_PARENT
params.width = DoKitViewLayoutParams.MATCH_PARENT
params.height = DoKitViewLayoutParams.MATCH_PARENT
}
override fun onViewCreated(view: FrameLayout) {
......
package com.didichuxing.doraemonkit.kit.mc.ui.connect
import android.os.Bundle
import android.view.View
import android.widget.TextView
import androidx.lifecycle.lifecycleScope
import com.didichuxing.doraemonkit.DoKit
import com.didichuxing.doraemonkit.kit.test.TestMode
import com.didichuxing.doraemonkit.kit.core.BaseFragment
import com.didichuxing.doraemonkit.kit.mc.net.ConnectMode
import com.didichuxing.doraemonkit.kit.mc.DoKitMcManager
import com.didichuxing.doraemonkit.kit.mc.net.DokitMcConnectManager
import com.didichuxing.doraemonkit.kit.mc.ui.DoKitMcActivity
import com.didichuxing.doraemonkit.kit.mc.ui.adapter.McClientHistory
import com.didichuxing.doraemonkit.kit.mc.net.DoKitMcConnectClient
import com.didichuxing.doraemonkit.mc.R
import com.didichuxing.doraemonkit.util.ToastUtils
import kotlinx.coroutines.launch
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/12/10-10:52
* 描 述:
* 修订历史:
* ================================================
*/
class DoKitMcConnectFragment : BaseFragment() {
private var history: McClientHistory? = null
override fun onRequestLayout(): Int {
return R.layout.dk_fragment_mc_connect
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
history = DokitMcConnectManager.itemHistory
if (history != null) {
findViewById<TextView>(R.id.tv_host_info).text = "当前设备已连接主机:【${history?.host}】"
findViewById<TextView>(R.id.info).text = "ws://${history?.host}:${history?.port}${history?.path}"
} else {
findViewById<TextView>(R.id.tv_host_info).text = "当前设备已连接主机:--"
findViewById<TextView>(R.id.info).text = "ws://--"
}
val btb = findViewById<View>(R.id.btn_close)
history.let {
btb.isEnabled = it!!.enable
}
findViewById<View>(R.id.btn_close).setOnClickListener {
lifecycleScope.launch {
if (DokitMcConnectManager.connectMode == ConnectMode.CLOSE) {
ToastUtils.showShort("未开启联网模式")
} else {
DoKit.removeFloating(ConnectDokitView::class)
DoKitMcManager.CONNECT_MODE = TestMode.UNKNOWN
DokitMcConnectManager.currentConnectHistory = null
DoKitMcConnectClient.close()
if (activity is DoKitMcActivity) {
(activity as DoKitMcActivity).onBackPressed()
}
}
}
}
}
}
......@@ -7,33 +7,29 @@ import android.os.Bundle
import android.text.TextUtils
import android.view.View
import android.widget.Button
import android.widget.CompoundButton
import android.widget.Switch
import androidx.appcompat.app.AlertDialog
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.didichuxing.doraemonkit.DoKit
import com.didichuxing.doraemonkit.kit.test.TestMode
import com.didichuxing.doraemonkit.extension.isTrueWithCor
import com.didichuxing.doraemonkit.kit.connect.ConnectAddress
import com.didichuxing.doraemonkit.kit.core.BaseFragment
import com.didichuxing.doraemonkit.kit.mc.DoKitMcManager
import com.didichuxing.doraemonkit.kit.mc.net.DokitMcConnectManager
import com.didichuxing.doraemonkit.kit.mc.MultiControlConfig
import com.didichuxing.doraemonkit.kit.mc.MultiControlManager
import com.didichuxing.doraemonkit.kit.mc.OnMultiControlModeChangeListener
import com.didichuxing.doraemonkit.kit.mc.utils.ConnectHistoryUtils
import com.didichuxing.doraemonkit.kit.mc.net.DoKitMcConnectClient
import com.didichuxing.doraemonkit.kit.connect.data.LoginData
import com.didichuxing.doraemonkit.kit.mc.ui.*
import com.didichuxing.doraemonkit.kit.mc.ui.adapter.McClientHistory
import com.didichuxing.doraemonkit.kit.mc.ui.adapter.McClientHistoryAdapter
import com.didichuxing.doraemonkit.mc.R
import com.didichuxing.doraemonkit.util.GsonUtils
import com.didichuxing.doraemonkit.util.LogHelper
import com.didichuxing.doraemonkit.util.TimeUtils
import com.didichuxing.doraemonkit.util.ToastUtils
import com.didichuxing.doraemonkit.widget.recyclerview.DividerItemDecoration
import com.didichuxing.doraemonkit.zxing.activity.CaptureActivity
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.util.*
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
......@@ -47,34 +43,57 @@ import kotlin.coroutines.suspendCoroutine
* 修订历史:
* ================================================
*/
class DoKitMcConnectHistoryFragment : BaseFragment() {
class MultiControlAllFragment : BaseFragment() {
private val REQUEST_CODE_SCAN = 0x108
companion object {
private const val REQUEST_CODE_SCAN = 0x108
}
private lateinit var switch: Switch
private lateinit var mRv: RecyclerView
private lateinit var mAdapter: McClientHistoryAdapter
private lateinit var histories: MutableList<McClientHistory>
private val checkedChangeListener = CompoundButton.OnCheckedChangeListener { _, isChecked ->
if (isChecked) {
MultiControlManager.changeMode(TestMode.HOST)
} else {
MultiControlManager.changeMode(TestMode.CLIENT)
}
}
private val modeChangeListener = object : OnMultiControlModeChangeListener {
override fun onMultiControlModeChanged(testMode: TestMode) {
if (testMode == TestMode.HOST) {
changeSwitchChecked(true)
} else {
changeSwitchChecked(false)
}
}
}
private fun changeSwitchChecked(isChecked: Boolean) {
switch.setOnCheckedChangeListener(null)
switch.isChecked = isChecked
switch.setOnCheckedChangeListener(checkedChangeListener)
}
override fun onRequestLayout(): Int {
return R.layout.dk_fragment_mc_connect_history
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val switch: Switch = findViewById(R.id.dokit_mode_switch_btn)
switch = findViewById(R.id.dokit_mode_switch_btn)
switch.isChecked = DoKitMcManager.CONNECT_MODE == TestMode.HOST
switch.setOnCheckedChangeListener { buttonView, isChecked ->
if (isChecked) {
DokitMcConnectManager.changeHostMode()
} else {
DokitMcConnectManager.changeClientMode()
}
}
switch.isChecked = MultiControlManager.getMode() == TestMode.HOST
switch.setOnCheckedChangeListener(checkedChangeListener)
val add: Button = findViewById(R.id.add)
add.setOnClickListener { view ->
add.setOnClickListener {
startScan()
}
......@@ -85,12 +104,18 @@ class DoKitMcConnectHistoryFragment : BaseFragment() {
}
mAdapter.setOnItemClickListener { adapter, view, pos ->
val data = histories[pos]
DokitMcConnectManager.itemHistory = data
if (activity is DoKitMcActivity) {
(activity as DoKitMcActivity).pushFragment(McPages.CONNECT)
if (data.enable) {
lifecycleScope.launch {
privacyInterceptDialog("提示", "是否断开与当前主机链接").isTrueWithCor {
MultiControlConfig.currentConnectHistory = null
MultiControlManager.closeWorkMode()
ToastUtils.showShort("已断开链接")
updateHistoryView()
}
}
} else {
ToastUtils.showShort("该主机没有建立链接")
}
}
mAdapter.setOnItemLongClickListener { adapter, view, pos ->
......@@ -115,6 +140,8 @@ class DoKitMcConnectHistoryFragment : BaseFragment() {
updateHistoryView()
MultiControlManager.addOnMultiControlModeChangeListener(modeChangeListener)
}
......@@ -145,14 +172,20 @@ class DoKitMcConnectHistoryFragment : BaseFragment() {
updateHistoryView()
}
override fun onStart() {
super.onStart()
}
override fun onDestroy() {
super.onDestroy()
MultiControlManager.removeOnMultiControlModeChangeListener(modeChangeListener)
}
private fun updateHistoryView() {
lifecycleScope.launch {
val clients = ConnectHistoryUtils.loadClientHistory()
val current = DokitMcConnectManager.currentConnectHistory
val current = MultiControlConfig.currentConnectHistory
for (history in clients) {
if (current != null) {
history.enable = TextUtils.equals(history.url, current.url)
......@@ -189,7 +222,7 @@ class DoKitMcConnectHistoryFragment : BaseFragment() {
uri?.let {
val name = uri.host.toString()
val time = TimeUtils.date2String(Date())
val url = "ws://${uri.host}:${uri.port}${uri.path}"
val url = code
val history = McClientHistory(uri.host!!, uri.port, uri.path!!, name, time, url)
ConnectHistoryUtils.saveClientHistory(history)
handleConnect(history)
......@@ -198,9 +231,6 @@ class DoKitMcConnectHistoryFragment : BaseFragment() {
} catch (e: Exception) {
e.printStackTrace()
} finally {
// if (activity is DoKitMcActivity) {
// (activity as DoKitMcActivity).onBackPressed()
// }
}
} else {
handleNoResult()
......@@ -221,45 +251,16 @@ class DoKitMcConnectHistoryFragment : BaseFragment() {
}
private fun handleConnect(clientHistory: McClientHistory) {
if (DoKitMcManager.CONNECT_MODE != TestMode.UNKNOWN) {
DoKitMcConnectClient.close()
DoKitMcManager.CONNECT_MODE = TestMode.UNKNOWN
}
DoKitMcConnectClient.connect(
clientHistory.host!!,
clientHistory.port,
clientHistory.path!!,
if (clientHistory.connectSerial == null) "" else clientHistory.connectSerial
) { code, message ->
withContext(Dispatchers.Main) {
when (code) {
DoKitMcConnectClient.CONNECT_SUCCEED -> {
val loginData = GsonUtils.fromJson<LoginData>(message, LoginData::class.java)
clientHistory.connectSerial = loginData.connectSerial
ConnectHistoryUtils.saveClientHistory(clientHistory)
DokitMcConnectManager.currentConnectHistory = clientHistory
updateHistoryView()
DoKitMcManager.startClientMode()
DokitMcConnectManager.changeClientMode()
DoKitMcManager.CONNECT_MODE
//启动悬浮窗
DoKit.launchFloating(ConnectDokitView::class)
ToastUtils.showShort("已成功连接服务器")
}
DoKitMcConnectClient.CONNECT_FAILED -> {
DokitMcConnectManager.currentConnectHistory = null
updateHistoryView()
DoKitMcManager.closeWorkMode()
DoKitMcManager.CONNECT_MODE = TestMode.UNKNOWN
LogHelper.e(TAG, "message===>$message")
ToastUtils.showShort(message)
}
else -> {
LogHelper.e(TAG, "code=$code, message===>$message")
}
}
}
}
MultiControlConfig.currentConnectHistory = clientHistory
updateHistoryView()
val address = ConnectAddress(
clientHistory.name,
clientHistory.url,
clientHistory.time
)
MultiControlManager.startClientMode(address)
}
......
......@@ -7,10 +7,9 @@ import android.view.View
import android.widget.FrameLayout
import android.widget.TextView
import com.didichuxing.doraemonkit.kit.test.TestMode
import com.didichuxing.doraemonkit.kit.core.AbsDokitView
import com.didichuxing.doraemonkit.kit.core.DokitViewLayoutParams
import com.didichuxing.doraemonkit.kit.mc.DoKitMcManager
import com.didichuxing.doraemonkit.kit.mc.net.DokitMcConnectManager
import com.didichuxing.doraemonkit.kit.core.AbsDoKitView
import com.didichuxing.doraemonkit.kit.core.DoKitViewLayoutParams
import com.didichuxing.doraemonkit.kit.mc.MultiControlManager
import com.didichuxing.doraemonkit.kit.mc.ui.McPages
import com.didichuxing.doraemonkit.kit.mc.utils.McPageUtils
import com.didichuxing.doraemonkit.mc.R
......@@ -25,15 +24,25 @@ import com.didichuxing.doraemonkit.util.ConvertUtils
* 修订历史:
* ================================================
*/
class ConnectDokitView : AbsDokitView() {
class MultiControlDoKitView : AbsDoKitView() {
val HOST = "主机"
val CLIENT = "从机"
companion object {
private val HOST = R.string.dk_kit_multi_control_host
private val CLIENT = R.string.dk_kit_multi_control_client
private val doKitViewSet: MutableSet<MultiControlDoKitView> = mutableSetOf()
var textView: TextView? = null
fun updateConnectMode() {
doKitViewSet.forEach {
it.updateConnectMode()
}
}
}
override fun onCreate(context: Context?) {
private var textView: TextView? = null
override fun onCreate(context: Context?) {
doKitViewSet.add(this)
}
override fun onCreateView(context: Context?, rootView: FrameLayout?): View {
......@@ -48,32 +57,29 @@ class ConnectDokitView : AbsDokitView() {
McPageUtils.startFragment(McPages.CONNECT_HISTORY)
}
updateConnectMode()
DokitMcConnectManager.dokitFloatViews.add(this)
}
fun updateConnectMode() {
textView.let {
if (DoKitMcManager.CONNECT_MODE == TestMode.HOST) {
textView?.text = HOST
if (MultiControlManager.getMode() == TestMode.HOST) {
textView?.text = getString(HOST)
} else {
textView?.text = CLIENT
textView?.text = getString(CLIENT)
}
}
}
override fun initDokitViewLayoutParams(params: DokitViewLayoutParams) {
params.width = ConvertUtils.dp2px(70.0f)
params.height = ConvertUtils.dp2px(70.0f)
override fun initDokitViewLayoutParams(params: DoKitViewLayoutParams) {
params.width = ConvertUtils.dp2px(65.0f)
params.height = ConvertUtils.dp2px(65.0f)
params.gravity = Gravity.TOP or Gravity.LEFT
params.x = ConvertUtils.dp2px(280f)
params.y = ConvertUtils.dp2px(25f)
}
override fun onDestroy() {
doKitViewSet.remove(this)
super.onDestroy()
DokitMcConnectManager.dokitFloatViews.remove(this)
}
}
package com.didichuxing.doraemonkit.kit.mc.utils
import com.didichuxing.doraemonkit.kit.mc.DoKitMcManager
import com.didichuxing.doraemonkit.kit.mc.oldui.DoKitMcManager
import com.didichuxing.doraemonkit.util.SPUtils
......
package com.didichuxing.doraemonkit.kit.mc.utils
import com.didichuxing.doraemonkit.kit.connect.DoKitSConnectManager
import com.didichuxing.doraemonkit.kit.connect.DoKitConnectManager
import com.didichuxing.doraemonkit.kit.test.event.ControlEvent
import com.didichuxing.doraemonkit.kit.connect.data.PackageType
import com.didichuxing.doraemonkit.kit.connect.data.TextPackage
......@@ -20,7 +20,7 @@ object WSPackageUtils {
val wsPackage = TextPackage(
pid = id, type = type, data = GsonUtils.toJson(event),
contentType = "action",
connectSerial = DoKitSConnectManager.getConnectSerial()
connectSerial = DoKitConnectManager.getConnectSerial()
)
return GsonUtils.toJson(wsPackage)
}
......@@ -30,7 +30,7 @@ object WSPackageUtils {
pid = RandomUtils.random32HexString(),
type = type,
data = data,
connectSerial = DoKitSConnectManager.getConnectSerial()
connectSerial = DoKitConnectManager.getConnectSerial()
)
return GsonUtils.toJson(wsPackage)
}
......@@ -41,7 +41,7 @@ object WSPackageUtils {
type = type,
data = "",
contentType = contentType,
connectSerial = DoKitSConnectManager.getConnectSerial()
connectSerial = DoKitConnectManager.getConnectSerial()
)
return GsonUtils.toJson(wsPackage)
}
......
......@@ -5,4 +5,4 @@
<corners android:radius="35dp" />
<solid android:color="#3CBCA3" />
</shape>
\ No newline at end of file
</shape>
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="70dp"
android:layout_height="70dp"
android:background="@drawable/dk_dokitview_mc"
android:backgroundTint="@color/background_verbose"
android:layout_width="65dp"
android:layout_height="65dp"
android:orientation="vertical">
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
<androidx.cardview.widget.CardView
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_centerInParent="true"
android:text="从机"
android:textColor="@color/dk_color_FFFFFF"
android:textSize="18sp"
android:textStyle="bold" />
app:cardBackgroundColor="#ff3CBCA3"
app:cardCornerRadius="@dimen/dk_dp_30"
app:cardElevation="2dp"
app:cardPreventCornerOverlap="true">
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_gravity="center"
android:text="从机"
android:textColor="@color/dk_color_FFFFFF"
android:textSize="18sp"
android:textStyle="bold" />
</androidx.cardview.widget.CardView>
</RelativeLayout>
......@@ -21,4 +21,4 @@
android:textSize="20sp"
android:visibility="gone" />
</FrameLayout>
\ No newline at end of file
</FrameLayout>
......@@ -5,7 +5,6 @@ import android.app.Application
import android.content.Context
import android.os.Bundle
import android.view.View
import com.didichuxing.doraemonkit.constant.WSMode
import com.didichuxing.doraemonkit.kit.AbstractKit
import com.didichuxing.doraemonkit.kit.core.*
import com.didichuxing.doraemonkit.kit.network.okhttp.interceptor.DokitExtInterceptor
......@@ -182,8 +181,8 @@ public class DoKit private constructor() {
* 获取一机多控类型
*/
@JvmStatic
fun mcMode(): WSMode {
return WSMode.UNKNOW
fun mcMode(): String {
return ""
}
}
......@@ -286,4 +285,4 @@ public class DoKit private constructor() {
fun build() {
}
}
}
\ No newline at end of file
}
package com.didichuxing.doraemonkit.constant
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/11/17-17:11
* 描 述:事件对象类型
* 修订历史:
* ================================================
*/
/**
* ws mode
*/
enum class WSMode {
/**
* 位置类型,即为连接
*/
UNKNOW,
/**
* 主机
*/
HOST,
/**
* 从机
*/
CLIENT,
/**
*数据抓取中...
*/
RECORDING,
/**
* 查看用例列表
*/
MC_CASELIST
}
/**
* 事件类型
*/
enum class WSEType {
/**
* app 切换到前台
*/
APP_ON_FOREGROUND,
/**
* app 切换到后台
*/
APP_ON_BACKGROUND,
/**
* 测试事件
*/
WSE_TEST,
/**
* Activity 返回键事件
*/
ACTIVITY_BACK_PRESSED,
/**
* Activity finish
*/
ACTIVITY_FINISH,
/**
* WS 连接成功事件
*/
WSE_CONNECTED,
/**
* WS 断开连接
*/
WSE_CLOSE,
/**
* WS HOST 断开连接
*/
WSE_HOST_CLOSE,
/**
* 通用手势事件
*/
WSE_COMM_EVENT,
/**
* 自定义手势事件
*/
WSE_CUSTOM_EVENT,
}
package com.didichuxing.doraemonkit.kit.test
object DoKitTestManager {
/**
* 是否是主机模式
*/
fun isHostMode(): Boolean {
return false
}
/**
* 是否是从机模式
*/
fun isClientMode(): Boolean {
return false
}
/**
* 测试功能是否关闭
*/
fun isClose(): Boolean {
return true
}
fun getTestMode(): TestMode {
return TestMode.UNKNOWN
}
/**
* 开始测试功能
* 1、开始hook 或者关闭hook
*/
fun startTest(testMode: TestMode) {
}
/**
* 关闭测试功能
* 备注:关闭后测试相关功能将不工作
*/
fun closeTest() {
}
}
package com.didichuxing.doraemonkit.kit.test
/**
* 测试工作模式 mode
*/
enum class TestMode {
/**
*未知 :不允许执行采集也不允许执行模拟事件
*/
UNKNOWN,
/**
* 主机:采集事件
*/
HOST,
/**
* 从机:模拟执行事件
*/
CLIENT,
}
package com.didichuxing.doraemonkit.kit.test.all
import android.view.View
import com.didichuxing.doraemonkit.constant.WSMode
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/12/9-17:42
* 描 述:
* 修订历史:
* ================================================
*/
object DoKitMcManager {
var MC_CASE_ID: String = ""
var mcNetMockInterceptor: McNetMockInterceptor? = null
var WS_MODE: WSMode = WSMode.UNKNOW
var CONNECT_MODE: WSMode = WSMode.UNKNOW
/**
* 发送自定义事件
* @return view
* @return eventType 事件类型
* @return param 自定义参数
*/
fun sendCustomEvent(eventType: String, view: View? = null, param: Map<String, String>? = null) {
}
fun startHostMode() {
}
fun startClientMode() {
}
fun closeWorkMode() {
}
}
package com.didichuxing.doraemonkit.kit.test.all
import com.didichuxing.doraemonkit.kit.test.mock.HttpMatchedInfo
import com.didichuxing.doraemonkit.kit.test.mock.McMockKey
import com.didichuxing.doraemonkit.kit.test.mock.McResInfo
/**
* didi Create on 2022/1/21 .
*
* Copyright (c) 2022/1/21 by didiglobal.com.
*
* @author <a href="realonlyone@126.com">zhangjun</a>
* @version 1.0
* @Date 2022/1/21 2:54 下午
* @Description 用一句话说明文件功能
*/
interface McNetMockInterceptor {
fun intercept(key: McMockKey, mcResInfo: McResInfo<HttpMatchedInfo>): McResInfo<HttpMatchedInfo>
}
package com.didichuxing.doraemonkit.kit.test.mock
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2021/6/21-17:04
* 描 述:
* 修订历史:
* ================================================
*/
data class HttpMatchedInfo(
val key: String,
val responseBody: String
)
package com.didichuxing.doraemonkit.kit.test.mock
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2021/6/21-17:04
* 描 述:
* 修订历史:
* ================================================
*/
data class HttpUploadInfo(
val pId: String,
val caseId: String,
val originKey: String,
val key: String,
val method: String = "GET",
val path: String,
val fragment: String?,
val contentType: String?,
val query: Map<String, String>?,
val requestBody: Map<String, String>?,
val responseBody: String
)
package com.didichuxing.doraemonkit.kit.test.mock
data class McMockKey(
val key: String,
val originKey: String,
val query: String,
val requestBody: String
)
package com.didichuxing.doraemonkit.kit.test.mock
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2021/7/1-19:15
* 描 述:
* 修订历史:
* ================================================
*/
data class McResInfo<T>(
val code: Int = 0,
val msg: String = "",
var data: T? = null
)
......@@ -14,7 +14,7 @@ import com.kronos.dokit.pthread.dump
* @Since 2021/11/24
*
*/
class PThreadHookUiActivity : AppCompatActivity(R.layout.activity_pthread_hook) {
class PThreadHookUiActivity : AppCompatActivity() {
private lateinit var viewBinding :ActivityPthreadHookBinding
private val threadAdapter by lazy {
......@@ -23,6 +23,7 @@ class PThreadHookUiActivity : AppCompatActivity(R.layout.activity_pthread_hook)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_pthread_hook)
viewBinding = ActivityPthreadHookBinding.inflate(layoutInflater)
viewBinding.recyclerView.layoutManager = LinearLayoutManager(this)
viewBinding.recyclerView.adapter = threadAdapter
......
package com.didichuxing.doraemonkit.kit.test
import com.didichuxing.doraemonkit.kit.test.util.XposedHookUtils
import com.didichuxing.doraemonkit.kit.test.utils.XposedHookUtil
/**
......@@ -65,8 +65,8 @@ object DoKitTestManager {
*/
fun closeTest() {
testMode = TestMode.UNKNOWN
if (XposedHookUtils.isRunTimeHookEnable()) {
XposedHookUtils.stopRunTimeHook()
if (XposedHookUtil.isRunTimeHookEnable()) {
XposedHookUtil.stopRunTimeHook()
}
}
......@@ -91,15 +91,15 @@ object DoKitTestManager {
private fun startTestByHostMode() {
testMode = TestMode.HOST
if (!XposedHookUtils.isRunTimeHookEnable()) {
XposedHookUtils.startRunTimeHook()
if (!XposedHookUtil.isRunTimeHookEnable()) {
XposedHookUtil.startRunTimeHook()
}
}
private fun startTestByClientMode() {
testMode = TestMode.CLIENT
if (XposedHookUtils.isRunTimeHookEnable()) {
XposedHookUtils.stopRunTimeHook()
if (XposedHookUtil.isRunTimeHookEnable()) {
XposedHookUtil.stopRunTimeHook()
}
}
......
package com.didichuxing.doraemonkit.kit.test.event
import android.view.accessibility.AccessibilityEvent
/**
* didi Create on 2022/4/13 .
......@@ -20,6 +22,7 @@ enum class ActionType(private val id: Int, private val nameText: String) {
ON_SCROLL(3, "滑动"),
ON_FOCUS_CHANGE(4, "焦点改变"),
ON_INPUT_CHANGE(5, "输入"),
ON_INPUT_SELECTION_CHANGE(5, "输入光标位置"),
ON_TOUCH(6, "TOUCH"),
ON_TOUCH_START(7, "TOUCH START"),
ON_TOUCH_END(8, "TOUCH END"),
......@@ -28,7 +31,7 @@ enum class ActionType(private val id: Int, private val nameText: String) {
ON_CUSTOM_EVENT(30, "自定义");
val map:Map<Int,ActionType> = mutableMapOf()
val map: Map<Int, ActionType> = mutableMapOf()
fun getID(): Int {
return id
......@@ -37,4 +40,41 @@ enum class ActionType(private val id: Int, private val nameText: String) {
fun getDesc(): String {
return nameText
}
companion object {
fun valueOf(acc: AccessibilityEvent): ActionType {
var actionType: ActionType
when (acc.eventType) {
AccessibilityEvent.TYPE_VIEW_CLICKED -> {
actionType = ON_CLICK
}
AccessibilityEvent.TYPE_VIEW_LONG_CLICKED -> {
actionType = ON_LONG_CLICK
}
AccessibilityEvent.TYPE_VIEW_SCROLLED -> {
actionType = ON_SCROLL
}
AccessibilityEvent.TYPE_VIEW_FOCUSED -> {
actionType = ON_FOCUS_CHANGE
}
AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED -> {
actionType = ON_INPUT_CHANGE
}
AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED -> {
actionType = ON_INPUT_SELECTION_CHANGE
}
AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED -> {
actionType = UNKNOWN
}
AccessibilityEvent.TYPE_VIEW_SELECTED -> {
actionType = UNKNOWN
}
else -> {
actionType = UNKNOWN
}
}
return actionType
}
}
}
package com.didichuxing.doraemonkit.kit.test.event
import com.didichuxing.doraemonkit.kit.test.util.DateTime
import com.didichuxing.doraemonkit.kit.test.utils.DateTime
/**
* ================================================
......@@ -12,10 +12,11 @@ import com.didichuxing.doraemonkit.kit.test.util.DateTime
* ================================================
*/
data class ControlEvent(
var eventId: String = "",
val eventId: String = "",
val eventType: EventType,
val params: Map<String, String>? = null,
val viewC12c: ViewC12c? = null,
val dateTime: String = DateTime.nowTime(),
val diffTIme: Long = 0
val dateTimeMillis: Long = DateTime.nowTimeMillis(),
var diffTime: Long = 0
)
......@@ -2,7 +2,7 @@ package com.didichuxing.doraemonkit.kit.test.event
import android.app.Activity
import android.view.View
import com.didichuxing.doraemonkit.kit.test.util.RandomIdentityUtils
import com.didichuxing.doraemonkit.kit.test.utils.RandomIdentityUtil
/**
* didi Create on 2022/4/13 .
......@@ -17,11 +17,14 @@ import com.didichuxing.doraemonkit.kit.test.util.RandomIdentityUtils
object ControlEventManager {
private const val MAX_DIFF_TIME: Long = 15 * 1000
private var currentEventId: String = ""
private var lastEventDateTime: Long = System.currentTimeMillis()
private val onControlEventActionListenerSet: MutableSet<OnControlEventActionListener> = mutableSetOf()
private val onControlEventActionProcessListenerSet: MutableSet<OnControlEventActionProcessListener> = mutableSetOf()
private val onControlEventInterceptorSet: MutableSet<OnControlEventInterceptor> = mutableSetOf()
private val controlEventProcessor: ControlEventProcessor = ControlEventProcessor()
......@@ -38,7 +41,7 @@ object ControlEventManager {
}
fun createNextEventId(): String {
return RandomIdentityUtils.createAid()
return RandomIdentityUtil.createAid()
}
/**
......@@ -46,12 +49,53 @@ object ControlEventManager {
* 来自与事件监听
*/
fun onControlEventAction(activity: Activity?, view: View?, controlEvent: ControlEvent) {
updateEventId(controlEvent.eventId)
onControlEventActionListenerSet.forEach {
it.onControlEventAction(activity, view, controlEvent)
if (!onControlEventActionIntercept(activity, view, controlEvent)) {
updateEventId(controlEvent.eventId)
controlEvent.diffTime = getEventDiffTime()
onControlEventActionListenerSet.forEach {
it.onControlEventAction(activity, view, controlEvent)
}
}
}
private fun onControlEventActionIntercept(activity: Activity?, view: View?, controlEvent: ControlEvent): Boolean {
onControlEventInterceptorSet.forEach {
if (it.onControlEventAction(activity, view, controlEvent)) {
return true
}
}
return false
}
/**
* 重新设置开始时间
*/
fun resetLastEventDateTime() {
lastEventDateTime = System.currentTimeMillis()
}
private fun getEventDiffTime(): Long {
val currentTime = System.currentTimeMillis()
val diffTime = currentTime - lastEventDateTime
lastEventDateTime = currentTime
return if (diffTime > MAX_DIFF_TIME) {
MAX_DIFF_TIME
} else if (diffTime < 0) {
0
} else {
diffTime
}
}
fun addOnControlEventInterceptor(eventInterceptor: OnControlEventInterceptor) {
onControlEventInterceptorSet.add(eventInterceptor)
}
fun removeOnControlEventInterceptor(eventInterceptor: OnControlEventInterceptor) {
onControlEventInterceptorSet.remove(eventInterceptor)
}
fun addOnControlEventActionListener(actionListener: OnControlEventActionListener) {
onControlEventActionListenerSet.add(actionListener)
}
......@@ -69,6 +113,14 @@ object ControlEventManager {
controlEventProcessor.processControlEvent(controlEvent)
}
fun addOnControlEventActionProcessListener(processListener: OnControlEventActionProcessListener) {
onControlEventActionProcessListenerSet.add(processListener)
}
fun removeOnControlEventActionProcessListener(processListener: OnControlEventActionProcessListener) {
onControlEventActionProcessListenerSet.add(processListener)
}
/**
* 从机执行 ControlEvent 成功
*/
......@@ -87,14 +139,6 @@ object ControlEventManager {
}
}
fun addOnControlEventActionListener(processListener: OnControlEventActionProcessListener) {
onControlEventActionProcessListenerSet.add(processListener)
}
fun removeOnControlEventActionListener(processListener: OnControlEventActionProcessListener) {
onControlEventActionProcessListenerSet.remove(processListener)
}
fun getControlEventProcessor(): ControlEventProcessor {
return controlEventProcessor
}
......
package com.didichuxing.doraemonkit.kit.test.event
data class DoKitViewPanelNode(
val windowIndex: Int = -1,
val className: String
)
package com.didichuxing.doraemonkit.kit.test.event
import android.app.Activity
import android.view.View
/**
* didi Create on 2022/4/18 .
*
* Copyright (c) 2022/4/18 by didiglobal.com.
*
* @author <a href="realonlyone@126.com">zhangjun</a>
* @version 1.0
* @Date 2022/4/18 2:51 下午
* @Description 事件拦截器,在事件发送时,不需要的事件可以通过这里进行拦截
*/
interface OnControlEventInterceptor {
fun onControlEventAction(activity: Activity?, view: View?, controlEvent: ControlEvent): Boolean
}
......@@ -26,5 +26,6 @@ data class ViewC12c(
var scrollY: Long = -1,
var inputValue: String = "",
var position: Position? = null,
val doKitViewNode: DoKitViewNode? = null
val doKitViewNode: DoKitViewNode? = null,
val doKitViewPanelNode:DoKitViewPanelNode? = null
)
......@@ -5,17 +5,12 @@ import android.view.View
import android.view.accessibility.AccessibilityEvent
import android.widget.*
import com.didichuxing.doraemonkit.extension.tagName
import com.didichuxing.doraemonkit.kit.core.DokitFrameLayout
import com.didichuxing.doraemonkit.kit.core.DoKitFrameLayout
import com.didichuxing.doraemonkit.kit.test.DoKitTestManager
import com.didichuxing.doraemonkit.kit.test.event.ControlEventManager
import com.didichuxing.doraemonkit.kit.test.util.XposedHookUtils
import com.didichuxing.doraemonkit.kit.test.event.ControlEvent
import com.didichuxing.doraemonkit.kit.test.event.AccessibilityEventNode
import com.didichuxing.doraemonkit.kit.test.event.DoKitViewNode
import com.didichuxing.doraemonkit.kit.test.event.ViewC12c
import com.didichuxing.doraemonkit.kit.test.event.EventType
import com.didichuxing.doraemonkit.kit.test.util.ViewPathUtil
import com.didichuxing.doraemonkit.kit.test.util.WindowPathUtil
import com.didichuxing.doraemonkit.kit.test.event.*
import com.didichuxing.doraemonkit.kit.test.utils.XposedHookUtil
import com.didichuxing.doraemonkit.kit.test.utils.ViewPathUtil
import com.didichuxing.doraemonkit.kit.test.utils.WindowPathUtil
import com.didichuxing.doraemonkit.util.ConvertUtils
import com.didichuxing.doraemonkit.util.LogHelper
......@@ -50,7 +45,7 @@ object AccessibilityEventMonitor {
}
//针对dokit悬浮窗
AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED -> {
if (view is DokitFrameLayout) {
if (view is DoKitFrameLayout) {
onViewHandleEvent(view, event)
}
}
......@@ -68,9 +63,9 @@ object AccessibilityEventMonitor {
AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED -> {
onViewHandleEvent(view, event)
}
AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
AccessibilityEvent.TYPE_VIEW_SELECTED -> {
LogHelper.i(TAG, "TYPE_VIEW_SELECTED ,class=${view.javaClass},view=$view")
LogHelper.i(TAG, "TYPE_VIEW_SELECTED or TYPE_WINDOW_STATE_CHANGED ,class=${view.javaClass},view=$view")
}
else -> {
LogHelper.e(TAG, "type=${event.eventType},class=${view.javaClass},view=$view")
......@@ -79,7 +74,6 @@ object AccessibilityEventMonitor {
}
private fun onViewHandleEvent(view: View, accessibilityEvent: AccessibilityEvent) {
val activity = ViewPathUtil.getActivity(view)
val actionId = ControlEventManager.createNextEventId()
val viewC12c: ViewC12c = createViewC12c(view, accessibilityEvent)
......@@ -98,7 +92,7 @@ object AccessibilityEventMonitor {
private fun createViewC12c(view: View, acc: AccessibilityEvent): ViewC12c {
var viewRootImplIndex: Int = -1
var viewParents = WindowPathUtil.filterViewRoot(XposedHookUtils.ROOT_VIEWS);
var viewParents = WindowPathUtil.filterViewRoot(XposedHookUtil.ROOT_VIEWS);
viewParents?.let {
viewRootImplIndex = if (view.rootView.parent == null) {
it.size - 1
......@@ -106,7 +100,10 @@ object AccessibilityEventMonitor {
it.indexOf(view.rootView.parent)
}
}
val actionType: ActionType = ActionType.valueOf(acc)
return ViewC12c(
actionType = actionType,
actionName = actionType.getDesc(),
accEventType = acc.eventType,
windowIndex = viewRootImplIndex,
viewPaths = ViewPathUtil.createViewPathOfWindow(view),
......@@ -116,16 +113,25 @@ object AccessibilityEventMonitor {
} else {
""
},
doKitViewPanelNode = createDoKitViewPanel(view),
doKitViewNode = createDoKitViewInfo(view)
)
}
private fun createDoKitViewPanel(view: View): DoKitViewPanelNode? {
if (view.rootView is DoKitFrameLayout) {
val viewParents = WindowPathUtil.filterDoKitViewRoot(XposedHookUtil.ROOT_VIEWS)
val windowIndex = viewParents.indexOf(view.rootView.parent)
return DoKitViewPanelNode(windowIndex = windowIndex, className = (view.rootView as DoKitFrameLayout).title)
}
return null
}
/**
* 创建dokitview info
*/
private fun createDoKitViewInfo(view: View): DoKitViewNode? {
if (view !is DokitFrameLayout) {
if (view !is DoKitFrameLayout) {
return null
}
......
......@@ -5,11 +5,12 @@ import android.widget.TextView
import com.didichuxing.doraemonkit.extension.tagName
import com.didichuxing.doraemonkit.kit.test.DoKitTestManager
import com.didichuxing.doraemonkit.kit.test.event.ControlEventManager
import com.didichuxing.doraemonkit.kit.test.util.XposedHookUtils
import com.didichuxing.doraemonkit.kit.test.utils.XposedHookUtil
import com.didichuxing.doraemonkit.kit.test.event.ViewC12c
import com.didichuxing.doraemonkit.kit.test.event.EventType
import com.didichuxing.doraemonkit.kit.test.event.ControlEvent
import com.didichuxing.doraemonkit.kit.test.util.ViewPathUtil
import com.didichuxing.doraemonkit.kit.test.utils.ViewPathUtil
import com.didichuxing.doraemonkit.kit.test.utils.WindowPathUtil
/**
* didi Create on 2022/4/13 .
......@@ -50,14 +51,14 @@ object CustomEventMonitor {
private fun createViewC12c(view: View?, eventType: String, param: Map<String, String>?): ViewC12c {
var viewRootImplIndex: Int = -1
if (view != null) {
XposedHookUtils.ROOT_VIEWS?.let {
val viewParents = WindowPathUtil.filterViewRoot(XposedHookUtil.ROOT_VIEWS);
viewParents?.let {
viewRootImplIndex = if (view.rootView.parent == null) {
it.size - 1
} else {
it.indexOf(view.rootView.parent)
}
}
}
return ViewC12c(
......
......@@ -4,7 +4,7 @@ import com.didichuxing.doraemonkit.extension.tagName
import com.didichuxing.doraemonkit.kit.test.DoKitTestManager
import com.didichuxing.doraemonkit.kit.test.event.ControlEventManager
import com.didichuxing.doraemonkit.kit.test.event.EventType
import com.didichuxing.doraemonkit.kit.test.util.RandomIdentityUtils
import com.didichuxing.doraemonkit.kit.test.utils.RandomIdentityUtil
import com.didichuxing.doraemonkit.kit.test.event.ControlEvent
import com.didichuxing.doraemonkit.util.ActivityUtils
......@@ -28,7 +28,7 @@ object TcpMessageEventMonitor {
*/
fun onTcpMessageEvent(eventType: String, message: String = "") {
if (DoKitTestManager.isHostMode()) {
val actionId = RandomIdentityUtils.createAid()
val actionId = RandomIdentityUtil.createAid()
val wsEvent = ControlEvent(
actionId,
EventType.WSE_TCP_EVENT,
......
package com.didichuxing.doraemonkit.kit.test.event.processor
import android.app.Activity
import android.text.TextUtils
import android.view.View
import android.view.ViewGroup
import android.view.ViewParent
import com.didichuxing.doraemonkit.extension.tagName
import com.didichuxing.doraemonkit.kit.test.event.*
import com.didichuxing.doraemonkit.kit.test.util.XposedHookUtils
import com.didichuxing.doraemonkit.kit.test.util.ViewPathUtil
import com.didichuxing.doraemonkit.kit.test.util.WindowPathUtil
import com.didichuxing.doraemonkit.kit.test.utils.XposedHookUtil
import com.didichuxing.doraemonkit.kit.test.utils.ViewPathUtil
import com.didichuxing.doraemonkit.kit.test.utils.WindowPathUtil
import com.didichuxing.doraemonkit.util.ActivityUtils
import com.didichuxing.doraemonkit.util.LogHelper
import com.didichuxing.doraemonkit.util.ReflectUtils
......@@ -45,33 +47,36 @@ abstract class AbstractEventProcessor {
val viewC12c = it
val viewRoot: ViewParent? = findWindowRootView(it)
if (viewRoot == null) {
onControlEventProcessFailed(
controlEvent = controlEvent,
code = EventErrorCode.WINDOW_NOT_FIND,
message = "window 找不到"
)
val decorView: ViewGroup = ActivityUtils.getTopActivity().window.decorView as ViewGroup
onControlEventAction(controlEvent, viewC12c, decorView)
} else {
val decorView: ViewGroup = ReflectUtils.reflect(viewRoot).field("mView").get<View>() as ViewGroup
val targetView: View? = ViewPathUtil.findViewByViewParentInfo(decorView, viewC12c.viewPaths)
val activity = ViewPathUtil.getActivity(targetView)
if (targetView == null) {
onControlEventProcessFailed(
activity = activity,
controlEvent = controlEvent,
code = EventErrorCode.VIEW_NOT_FIND,
message = "view 找不到"
)
} else {
onSimulationEventAction(activity, targetView, viewC12c, controlEvent)
}
onControlEventAction(controlEvent, viewC12c, decorView)
}
}
}
private fun onControlEventAction(controlEvent: ControlEvent, viewC12c: ViewC12c, decorView: ViewGroup) {
val targetView: View? = ViewPathUtil.findViewByViewParentInfo(decorView, viewC12c.viewPaths)
val activity = ViewPathUtil.getActivity(targetView)
if (targetView == null && forceViewCheck()) {
onControlEventProcessFailed(
activity = activity,
controlEvent = controlEvent,
code = EventErrorCode.VIEW_NOT_FIND,
message = "view 找不到"
)
} else {
onSimulationEventAction(activity, targetView, viewC12c, controlEvent)
}
}
open fun forceViewCheck(): Boolean {
return true
}
abstract fun onSimulationEventAction(activity: Activity, view: View, viewC12c: ViewC12c, controlEvent: ControlEvent)
abstract fun onSimulationEventAction(activity: Activity, view: View?, viewC12c: ViewC12c, controlEvent: ControlEvent)
/**
* 从机执行 ControlEvent 成功
......@@ -90,7 +95,10 @@ abstract class AbstractEventProcessor {
private fun checkActivityNow(controlEvent: ControlEvent): Boolean {
controlEvent.params?.let {
if (it["activityName"] != ActivityUtils.getTopActivity()::class.tagName) {
val activityName = it["activityName"]
val nowActivity: Activity? = ActivityUtils.getTopActivity()
val nowActivityName: String = nowActivity?.tagName ?: ""
if (activityName != null && activityName != nowActivityName) {
return false
}
}
......@@ -99,10 +107,30 @@ abstract class AbstractEventProcessor {
private fun findWindowRootView(viewC12c: ViewC12c): ViewParent? {
if (XposedHookUtils.ROOT_VIEWS == null || viewC12c.windowIndex == -1) {
if (XposedHookUtil.ROOT_VIEWS == null) {
return null
}
val viewParent: ViewParent? = WindowPathUtil.findViewRoot(XposedHookUtils.ROOT_VIEWS, viewC12c.windowIndex)
var viewParent: ViewParent? = null
val panelNode = viewC12c.doKitViewPanelNode
if (panelNode != null) {
//增强查找DoKit悬浮窗口
val viewParents = WindowPathUtil.filterDoKitViewRoot(XposedHookUtil.ROOT_VIEWS)
if (panelNode.windowIndex >= viewParents.size) {
return null
}
val doKitViewParent = viewParents[panelNode.windowIndex]
val title = WindowPathUtil.getsDoKitViewRootTitle(doKitViewParent)
if (TextUtils.equals(panelNode.className, title)) {
viewParent = doKitViewParent
} else {
LogHelper.e("DoKit", "findWindowRootView() check failed.")
}
} else {
if (viewC12c.windowIndex > 0) {
viewParent = WindowPathUtil.findViewRoot(XposedHookUtil.ROOT_VIEWS, viewC12c.windowIndex)
}
}
return viewParent
}
......
......@@ -9,7 +9,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager.widget.ViewPager
import com.didichuxing.doraemonkit.extension.isFalse
import com.didichuxing.doraemonkit.kit.core.DokitFrameLayout
import com.didichuxing.doraemonkit.kit.core.DoKitFrameLayout
import com.didichuxing.doraemonkit.kit.test.event.ViewC12c
import com.didichuxing.doraemonkit.kit.test.event.ControlEvent
import com.didichuxing.doraemonkit.kit.test.event.EventErrorCode
......@@ -28,8 +28,10 @@ import com.didichuxing.doraemonkit.util.*
class AccessibilityEventProcessor : AbstractEventProcessor() {
override fun onSimulationEventAction(activity: Activity, view: View, viewC12c: ViewC12c, controlEvent: ControlEvent) {
dispatchSimulationEventAction(activity, view, viewC12c, controlEvent)
override fun onSimulationEventAction(activity: Activity, view: View?, viewC12c: ViewC12c, controlEvent: ControlEvent) {
if (view != null){
dispatchSimulationEventAction(activity, view, viewC12c, controlEvent)
}
}
/**
......@@ -147,13 +149,11 @@ class AccessibilityEventProcessor : AbstractEventProcessor() {
}
private fun onSimulationViewMoveEvent(activity: Activity, targetView: View, viewC12c: ViewC12c, controlEvent: ControlEvent) {
if (targetView is DokitFrameLayout && targetView.layoutParams is FrameLayout.LayoutParams) {
val layoutParams: FrameLayout.LayoutParams =
targetView.layoutParams as FrameLayout.LayoutParams
layoutParams.leftMargin =
viewC12c.doKitViewNode?.leftMargin!!
layoutParams.topMargin =
viewC12c.doKitViewNode.topMargin
if (targetView is DoKitFrameLayout && targetView.layoutParams is FrameLayout.LayoutParams) {
val layoutParams: FrameLayout.LayoutParams = targetView.layoutParams as FrameLayout.LayoutParams
layoutParams.leftMargin = viewC12c.doKitViewNode?.leftMargin!!
layoutParams.topMargin = viewC12c.doKitViewNode.topMargin
targetView.layoutParams = layoutParams
}
}
......
......@@ -20,7 +20,7 @@ import com.didichuxing.doraemonkit.util.*
class CustomEventProcessor : AbstractEventProcessor() {
override fun onSimulationEventAction(activity: Activity, view: View, viewC12c: ViewC12c, controlEvent: ControlEvent) {
override fun onSimulationEventAction(activity: Activity, view: View?, viewC12c: ViewC12c, controlEvent: ControlEvent) {
//执行自定义事件
DoKitManager.MC_CLIENT_PROCESSOR?.process(
ActivityUtils.getTopActivity(),
......@@ -31,4 +31,7 @@ class CustomEventProcessor : AbstractEventProcessor() {
onControlEventProcessSuccess(activity, view, controlEvent)
}
override fun forceViewCheck(): Boolean {
return false
}
}
......@@ -20,7 +20,7 @@ import com.didichuxing.doraemonkit.util.DoKitCommUtil
*/
class LifecycleEventProcessor : AbstractEventProcessor() {
override fun onSimulationEventAction(activity: Activity, view: View, viewC12c: ViewC12c, controlEvent: ControlEvent) {
override fun onSimulationEventAction(activity: Activity, view: View?, viewC12c: ViewC12c, controlEvent: ControlEvent) {
}
......
......@@ -19,7 +19,7 @@ import com.didichuxing.doraemonkit.kit.test.mock.tcp.TcpMockManager
*/
class TcpMessageEventProcessor : AbstractEventProcessor() {
override fun onSimulationEventAction(activity: Activity, view: View, viewC12c: ViewC12c, controlEvent: ControlEvent) {
override fun onSimulationEventAction(activity: Activity, view: View?, viewC12c: ViewC12c, controlEvent: ControlEvent) {
}
......
......@@ -2,8 +2,7 @@ package com.didichuxing.doraemonkit.kit.test.hook
import android.view.View
import android.view.accessibility.AccessibilityEvent
import com.didichuxing.doraemonkit.kit.test.TestMode
import com.didichuxing.doraemonkit.kit.core.DokitFrameLayout
import com.didichuxing.doraemonkit.kit.core.DoKitFrameLayout
import com.didichuxing.doraemonkit.kit.test.DoKitTestManager
import com.didichuxing.doraemonkit.kit.test.event.monitor.AccessibilityEventMonitor
import com.didichuxing.doraemonkit.util.LogHelper
......@@ -41,7 +40,7 @@ class ViewOnInitializeAccessibilityEventHook : XC_MethodHook() {
val accessibilityEvent = it.args[0] as AccessibilityEvent
LogHelper.i(TAG, "view==>${view},accessibilityEvent=${accessibilityEvent},eventType===${Integer.toHexString(accessibilityEvent.eventType)}")
if (view is DokitFrameLayout && accessibilityEvent.eventType == AccessibilityEvent.TYPE_VIEW_FOCUSED) {
if (view is DoKitFrameLayout && accessibilityEvent.eventType == AccessibilityEvent.TYPE_VIEW_FOCUSED) {
return
}
AccessibilityEventMonitor.onAccessibilityEvent(view, accessibilityEvent)
......
......@@ -30,6 +30,7 @@ import java.util.*
* 修订历史:
* ================================================
*/
@Deprecated("DoKitProxyMockInterceptor")
class DoKitMockInterceptor : AbsDoKitInterceptor() {
private val mExceptionHandler = CoroutineExceptionHandler { _, throwable ->
LogHelper.e(TAG, "error message: ${throwable.message}")
......@@ -122,7 +123,7 @@ class DoKitMockInterceptor : AbsDoKitInterceptor() {
val interceptor: HttpMockInterceptor? = MockManager.httpMockInterceptor
if (interceptor != null) {
result = interceptor.intercept(McMockKey(key,k,strQuery,strRequestBody), result)
result = interceptor.intercept(McMockKey(key, k, strQuery, strRequestBody), result)
}
LogHelper.i(TAG, "MCMOCKLOG OK key=$key,code=${result.code} originKey===>$k data=${result.data}")
......
......@@ -2,9 +2,8 @@ package com.didichuxing.doraemonkit.kit.test.mock.http
import com.didichuxing.doraemonkit.kit.test.mock.proxy.*
import com.didichuxing.doraemonkit.kit.network.okhttp.interceptor.AbsDoKitInterceptor
import com.didichuxing.doraemonkit.kit.test.DoKitTestManager
import com.didichuxing.doraemonkit.kit.test.mock.MockManager
import com.didichuxing.doraemonkit.kit.test.util.RandomIdentityUtils
import com.didichuxing.doraemonkit.kit.test.utils.RandomIdentityUtil
import com.didichuxing.doraemonkit.util.LogHelper
import io.ktor.http.*
import kotlinx.coroutines.CoroutineExceptionHandler
......@@ -34,11 +33,11 @@ class DoKitProxyMockInterceptor : AbsDoKitInterceptor() {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
if (DoKitTestManager.isHostMode() || DoKitTestManager.isClientMode()) {
if (MockManager.isHostMode() || MockManager.isClientMode()) {
if (!ProxyMockUtils.filterRequest(request)) {
if (DoKitTestManager.isHostMode()) {
if (MockManager.isHostMode()) {
//主机处理方式
val did = RandomIdentityUtils.createDid()
val did = RandomIdentityUtil.createDid()
val proxyRequest = ProxyMockUtils.createProxyRequest(did, request)
MockManager.requestStart(proxyRequest)
var response: Response
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册