Repository: didi/DoKit Branch: master Commit: 626827cddb2f Files: 3038 Total size: 9.7 MB Directory structure: gitextract_2kvtefj7/ ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ └── -dokit-------------.md │ └── issue_template.md ├── .gitignore ├── Android/ │ ├── .editorconfig │ ├── .gitignore │ ├── README.md │ ├── app/ │ │ ├── .gitignore │ │ ├── build.gradle │ │ ├── doraemonkit.gradle │ │ ├── keystore/ │ │ │ └── test.keystore │ │ ├── libs/ │ │ │ └── BaiduLBS_Android.jar │ │ ├── proguard-rules.pro │ │ └── src/ │ │ ├── androidTest/ │ │ │ └── java/ │ │ │ └── com/ │ │ │ └── didichuxing/ │ │ │ └── doraemondemo/ │ │ │ └── ExampleInstrumentedTest.java │ │ ├── debug/ │ │ │ └── java/ │ │ │ ├── AndroidManifest.xml │ │ │ └── com/ │ │ │ └── didichuxing/ │ │ │ └── doraemondemo/ │ │ │ ├── AopApp.java │ │ │ ├── App.kt │ │ │ └── amap/ │ │ │ ├── AMapUtil.java │ │ │ ├── ChString.java │ │ │ ├── DrivingRouteOverLay.java │ │ │ ├── FloatGpsMockRouteKitView.java │ │ │ ├── FloatGpsPresetMockKit.java │ │ │ ├── FloatGpsPresetMockKitView.java │ │ │ ├── RouteOverlay.java │ │ │ └── mockroute/ │ │ │ ├── BearingUtils.java │ │ │ ├── LogUtils.java │ │ │ ├── MockGPSTaskData.java │ │ │ └── MockGPSTaskManager.java │ │ ├── main/ │ │ │ ├── AndroidManifest.xml │ │ │ ├── java/ │ │ │ │ └── com/ │ │ │ │ └── didichuxing/ │ │ │ │ └── doraemondemo/ │ │ │ │ ├── AopTest.java │ │ │ │ ├── BaseStatusBarActivity.java │ │ │ │ ├── DataBaseActivity.kt │ │ │ │ ├── EmptyActivity.kt │ │ │ │ ├── MainDoKitActivity.kt │ │ │ │ ├── WebViewSystemActivity.kt │ │ │ │ ├── WebViewX5Activity.kt │ │ │ │ ├── amap/ │ │ │ │ │ ├── AMapRouterFragment.kt │ │ │ │ │ ├── DefaultNaviListener.kt │ │ │ │ │ └── NaviRouteOverlay.kt │ │ │ │ ├── comm/ │ │ │ │ │ ├── CommBaseFragment.kt │ │ │ │ │ ├── CommFragmentActivity.kt │ │ │ │ │ ├── CommLauncher.kt │ │ │ │ │ └── CommViewModel.kt │ │ │ │ ├── db/ │ │ │ │ │ ├── DatabaseHelper.kt │ │ │ │ │ └── PersonDBHelper.java │ │ │ │ ├── dokit/ │ │ │ │ │ ├── BorderDoKitView.java │ │ │ │ │ ├── CustomDokitFragment.kt │ │ │ │ │ ├── DemoDoKitView.kt │ │ │ │ │ ├── DemoKit.kt │ │ │ │ │ ├── SimpleDoKitView.java │ │ │ │ │ ├── TestSimpleDoKitFloatView.kt │ │ │ │ │ ├── TestSimpleDokitFloatViewKit.kt │ │ │ │ │ ├── TestSimpleDokitFragmentKit.kt │ │ │ │ │ └── ViewSetupHelper.java │ │ │ │ ├── mc/ │ │ │ │ │ ├── DoKitButton.kt │ │ │ │ │ ├── DoKitRecycleView.kt │ │ │ │ │ ├── DoKitWebView.kt │ │ │ │ │ ├── FileUtils.java │ │ │ │ │ ├── MCActivity.kt │ │ │ │ │ ├── MyProxyWebView.java │ │ │ │ │ ├── MyTestWebView.java │ │ │ │ │ ├── MyTestWebViewBuilder.java │ │ │ │ │ ├── NetMainActivity.kt │ │ │ │ │ ├── RVAdapter.kt │ │ │ │ │ ├── SlideBar.java │ │ │ │ │ ├── VPAdapter.kt │ │ │ │ │ ├── VpFragment.kt │ │ │ │ │ └── WebViewActivity.kt │ │ │ │ ├── module/ │ │ │ │ │ ├── CrashTest.kt │ │ │ │ │ ├── DoKitItemView.java │ │ │ │ │ ├── MethodCostTest.kt │ │ │ │ │ ├── ModuleActivity.java │ │ │ │ │ ├── ShadowDrawable.java │ │ │ │ │ ├── bigbitmap/ │ │ │ │ │ │ └── BigBitmapActivity.kt │ │ │ │ │ ├── db/ │ │ │ │ │ │ └── DataBaseTest.kt │ │ │ │ │ ├── http/ │ │ │ │ │ │ ├── CustomInterceptor.kt │ │ │ │ │ │ ├── FileUploadTest.kt │ │ │ │ │ │ ├── OkHttpMock.kt │ │ │ │ │ │ ├── RetrofitMock.kt │ │ │ │ │ │ └── URLConnectionMock.kt │ │ │ │ │ ├── leak/ │ │ │ │ │ │ └── LeakActivity.kt │ │ │ │ │ └── retrofit/ │ │ │ │ │ ├── GithubService.kt │ │ │ │ │ └── GithubUserInfo.kt │ │ │ │ ├── old/ │ │ │ │ │ ├── BaseActivity.kt │ │ │ │ │ ├── MainAdapter.kt │ │ │ │ │ ├── MainDebugActivityOkhttpV3.kt │ │ │ │ │ ├── MainDebugActivityOkhttpV4.kt │ │ │ │ │ ├── MapActivity.kt │ │ │ │ │ └── MapShowingLocationActivity.kt │ │ │ │ └── test/ │ │ │ │ ├── ScreenRecordingService.java │ │ │ │ ├── ScreenRecordingTest.java │ │ │ │ └── screen/ │ │ │ │ └── ScreenRecordingDoKitView.kt │ │ │ └── res/ │ │ │ ├── drawable/ │ │ │ │ ├── bg_unlock_bar_btn_normal_v5.xml │ │ │ │ ├── bg_unlock_bar_normal_v5.xml │ │ │ │ ├── dk_btn_background.xml │ │ │ │ ├── dk_info_background.xml │ │ │ │ ├── dk_line_divider.xml │ │ │ │ ├── dk_shape_float_view_bg.xml │ │ │ │ └── ic_launcher_background.xml │ │ │ ├── drawable-v24/ │ │ │ │ └── ic_launcher_foreground.xml │ │ │ ├── layout/ │ │ │ │ ├── activity_amap_path.xml │ │ │ │ ├── activity_big_bitmap_mock.xml │ │ │ │ ├── activity_comm.xml │ │ │ │ ├── activity_dokit_main.xml │ │ │ │ ├── activity_empty.xml │ │ │ │ ├── activity_leak.xml │ │ │ │ ├── activity_main.xml │ │ │ │ ├── activity_map.xml │ │ │ │ ├── activity_mc.xml │ │ │ │ ├── activity_net_main.xml │ │ │ │ ├── activity_normal_webview.xml │ │ │ │ ├── activity_second.xml │ │ │ │ ├── activity_webview.xml │ │ │ │ ├── activity_ws.xml │ │ │ │ ├── activity_x5_webview.xml │ │ │ │ ├── dk_demo.xml │ │ │ │ ├── dk_layout_simple_dokit_float_view.xml │ │ │ │ ├── dk_screen_show_view.xml │ │ │ │ ├── fragment_amap.xml │ │ │ │ ├── fragment_vp.xml │ │ │ │ ├── item_lv.xml │ │ │ │ ├── item_main_rv.xml │ │ │ │ ├── item_rv.xml │ │ │ │ ├── item_sc.xml │ │ │ │ ├── layout_demo_custom.xml │ │ │ │ ├── layout_mock_location_preset.xml │ │ │ │ ├── layout_mock_route.xml │ │ │ │ ├── layout_slidebar.xml │ │ │ │ └── view_dokit_item_view.xml │ │ │ ├── values/ │ │ │ │ ├── atts.xml │ │ │ │ ├── colors.xml │ │ │ │ ├── dimens.xml │ │ │ │ ├── strings.xml │ │ │ │ └── styles.xml │ │ │ ├── values-en-rUS/ │ │ │ │ └── strings.xml │ │ │ ├── values-zh-rCN/ │ │ │ │ └── strings.xml │ │ │ └── xml/ │ │ │ └── network_config.xml │ │ ├── release/ │ │ │ └── java/ │ │ │ ├── AndroidManifest.xml │ │ │ └── com/ │ │ │ └── didichuxing/ │ │ │ └── doraemondemo/ │ │ │ └── App.kt │ │ └── test/ │ │ └── java/ │ │ └── com/ │ │ └── didichuxing/ │ │ └── doraemondemo/ │ │ ├── ExampleUnitTest.kt │ │ ├── KotlinBaseUnitTest.kt │ │ ├── KotlinCaseTest.kt │ │ └── TestJava.java │ ├── build.gradle │ ├── buildSrc/ │ │ ├── .gitignore │ │ ├── build.gradle │ │ └── src/ │ │ └── main/ │ │ ├── kotlin/ │ │ │ └── com/ │ │ │ └── didichuxing/ │ │ │ └── doraemonkit/ │ │ │ └── plugin/ │ │ │ ├── DoKitExt.kt │ │ │ ├── DoKitExtUtil.kt │ │ │ ├── DoKitPlugin.kt │ │ │ ├── DoKitPluginUtil.kt │ │ │ ├── DoKitTransformTaskExecutionListener.kt │ │ │ ├── extension/ │ │ │ │ ├── BigImageExtension.kt │ │ │ │ ├── DoKitExtension.kt │ │ │ │ ├── GpsExtension.kt │ │ │ │ ├── NetworkExtension.kt │ │ │ │ ├── SlowMethodExtension.kt │ │ │ │ └── WebViewExtension.kt │ │ │ ├── processor/ │ │ │ │ ├── DoKitComponentHandler.kt │ │ │ │ └── DoKitPluginConfigProcessor.kt │ │ │ ├── stack_method/ │ │ │ │ ├── MethodStackNode.kt │ │ │ │ └── MethodStackNodeUtil.kt │ │ │ ├── thirdlib/ │ │ │ │ ├── ThirdLibInfo.kt │ │ │ │ └── ThirdLibVariantProcessor.kt │ │ │ └── transform/ │ │ │ ├── DoKitBaseTransform.kt │ │ │ ├── DoKitCommonTransform.kt │ │ │ ├── DoKitCommonTransformV34.kt │ │ │ ├── DoKitDependTransform.kt │ │ │ ├── DoKitDependTransformV34.kt │ │ │ ├── DoKitTransformContext.kt │ │ │ ├── DoKitTransformInvocation.kt │ │ │ ├── asmtransform/ │ │ │ │ ├── BaseDoKitAsmTransformer.kt │ │ │ │ └── DoKitAsmTransformer.kt │ │ │ └── classtransform/ │ │ │ ├── AbsClassTransformer.kt │ │ │ ├── BigImgClassTransformer.kt │ │ │ ├── CommClassTransformer.kt │ │ │ ├── EnterMSClassTransformer.kt │ │ │ ├── GPSAMapClassTransformer.kt │ │ │ ├── GPSBDClassTransformer.kt │ │ │ ├── GPSClassTransformer.kt │ │ │ ├── GPSTencentClassTransformer.kt │ │ │ ├── GSMClassTransformer.kt │ │ │ ├── MSDClassTransformer.kt │ │ │ ├── Okhttp3ClassTransformer.kt │ │ │ ├── ThirdLibsClassTransformer.kt │ │ │ ├── UrlConnectionTransformer.kt │ │ │ └── WebViewClassTransformer.kt │ │ └── resources/ │ │ └── META-INF/ │ │ └── gradle-plugins/ │ │ └── com.didi.dokit.debug.properties │ ├── config.gradle │ ├── dokit/ │ │ ├── .gitignore │ │ ├── build.gradle │ │ ├── gradle.properties │ │ ├── proguard-rules.pro │ │ ├── src/ │ │ │ ├── androidTest/ │ │ │ │ └── java/ │ │ │ │ └── com/ │ │ │ │ └── didichuxing/ │ │ │ │ └── doraemonkit/ │ │ │ │ └── ExampleInstrumentedTest.java │ │ │ ├── main/ │ │ │ │ ├── AndroidManifest.xml │ │ │ │ ├── assets/ │ │ │ │ │ ├── dokit_system_kits.json │ │ │ │ │ ├── h5help/ │ │ │ │ │ │ ├── dokit.js │ │ │ │ │ │ ├── dokit_js_hook.html │ │ │ │ │ │ └── dokit_js_vconsole_hook.html │ │ │ │ │ └── map/ │ │ │ │ │ └── map.html │ │ │ │ ├── cpp/ │ │ │ │ │ ├── CMakeLists.txt │ │ │ │ │ ├── art.cpp │ │ │ │ │ ├── art.h │ │ │ │ │ └── main.cpp │ │ │ │ ├── java/ │ │ │ │ │ └── com/ │ │ │ │ │ └── didichuxing/ │ │ │ │ │ └── doraemonkit/ │ │ │ │ │ ├── DebugFileProvider.java │ │ │ │ │ ├── DoKit.kt │ │ │ │ │ ├── DoKitActivityLifecycleCallbacks.kt │ │ │ │ │ ├── DoKitCallBack.kt │ │ │ │ │ ├── DoKitEnv.kt │ │ │ │ │ ├── DoKitFragmentLifecycleCallbacks.kt │ │ │ │ │ ├── DoKitOrientationEventListener.kt │ │ │ │ │ ├── DoKitReal.kt │ │ │ │ │ ├── DoraemonKit.kt │ │ │ │ │ ├── aop/ │ │ │ │ │ │ ├── DokitPluginConfig.java │ │ │ │ │ │ ├── DokitThirdLibInfo.java │ │ │ │ │ │ ├── MethodCostUtil.kt │ │ │ │ │ │ ├── OkHttpHook.java │ │ │ │ │ │ ├── WebViewHook.java │ │ │ │ │ │ ├── bigimg/ │ │ │ │ │ │ │ ├── coil/ │ │ │ │ │ │ │ │ ├── CoilHook.java │ │ │ │ │ │ │ │ └── DokitCoilTransformation.java │ │ │ │ │ │ │ ├── fresco/ │ │ │ │ │ │ │ │ ├── DokitFrescoPostprocessor.java │ │ │ │ │ │ │ │ └── FrescoHook.java │ │ │ │ │ │ │ ├── glide/ │ │ │ │ │ │ │ │ ├── DokitGlideRequestListener.java │ │ │ │ │ │ │ │ ├── DokitGlideTransform.java │ │ │ │ │ │ │ │ ├── GlideHook.java │ │ │ │ │ │ │ │ └── GlideTransformHook.java │ │ │ │ │ │ │ ├── imageloader/ │ │ │ │ │ │ │ │ ├── DokitImageLoadingListener.java │ │ │ │ │ │ │ │ └── ImageLoaderHook.java │ │ │ │ │ │ │ └── picasso/ │ │ │ │ │ │ │ ├── DokitPicassoTransformation.java │ │ │ │ │ │ │ └── PicassoHook.java │ │ │ │ │ │ ├── mc/ │ │ │ │ │ │ │ ├── DoKitListenerHelper.kt │ │ │ │ │ │ │ └── DoKitProxyActivity.kt │ │ │ │ │ │ ├── method_stack/ │ │ │ │ │ │ │ ├── MethodInvokNode.kt │ │ │ │ │ │ │ ├── MethodStackBean.kt │ │ │ │ │ │ │ ├── MethodStackUtil.kt │ │ │ │ │ │ │ └── StaticMethodObject.kt │ │ │ │ │ │ └── urlconnection/ │ │ │ │ │ │ ├── HttpUrlConnectionProxyUtil.java │ │ │ │ │ │ ├── MyTrustManager.java │ │ │ │ │ │ ├── ObsoleteUrlFactory.java │ │ │ │ │ │ └── OkhttpClientUtil.kt │ │ │ │ │ ├── config/ │ │ │ │ │ │ ├── AlignRulerConfig.java │ │ │ │ │ │ ├── ColorPickConfig.java │ │ │ │ │ │ ├── CrashCaptureConfig.java │ │ │ │ │ │ ├── DokitMemoryConfig.java │ │ │ │ │ │ ├── DokitSwitchConfig.java │ │ │ │ │ │ ├── FloatIconConfig.java │ │ │ │ │ │ ├── GlobalConfig.java │ │ │ │ │ │ ├── GpsMockConfig.java │ │ │ │ │ │ ├── LayoutBorderConfig.java │ │ │ │ │ │ ├── LogInfoConfig.java │ │ │ │ │ │ └── PerformanceSpInfoConfig.java │ │ │ │ │ ├── constant/ │ │ │ │ │ │ ├── BroadcastAction.java │ │ │ │ │ │ ├── BundleKey.java │ │ │ │ │ │ ├── CachesKey.java │ │ │ │ │ │ ├── DoKitModule.kt │ │ │ │ │ │ ├── FragmentIndex.java │ │ │ │ │ │ ├── PageTag.java │ │ │ │ │ │ ├── RequestCode.java │ │ │ │ │ │ ├── SharedPrefsKey.java │ │ │ │ │ │ └── SpInputType.java │ │ │ │ │ ├── datapick/ │ │ │ │ │ │ ├── DataPickBean.java │ │ │ │ │ │ ├── DataPickManager.java │ │ │ │ │ │ └── DataPickUtils.java │ │ │ │ │ ├── extension/ │ │ │ │ │ │ └── DokitExtension.kt │ │ │ │ │ ├── kit/ │ │ │ │ │ │ ├── AbstractKit.kt │ │ │ │ │ │ ├── Category.java │ │ │ │ │ │ ├── IKit.kt │ │ │ │ │ │ ├── alignruler/ │ │ │ │ │ │ │ ├── AlignLineView.java │ │ │ │ │ │ │ ├── AlignRulerInfoDoKitView.java │ │ │ │ │ │ │ ├── AlignRulerKit.kt │ │ │ │ │ │ │ ├── AlignRulerLineDoKitView.java │ │ │ │ │ │ │ ├── AlignRulerMarkerDoKitView.java │ │ │ │ │ │ │ └── AlignRulerSettingFragment.java │ │ │ │ │ │ ├── blockmonitor/ │ │ │ │ │ │ │ ├── BlockListAdapter.java │ │ │ │ │ │ │ ├── BlockListFragment.java │ │ │ │ │ │ │ ├── BlockMonitorFragment.java │ │ │ │ │ │ │ ├── BlockMonitorKit.kt │ │ │ │ │ │ │ ├── bean/ │ │ │ │ │ │ │ │ └── BlockInfo.java │ │ │ │ │ │ │ └── core/ │ │ │ │ │ │ │ ├── BlockCanaryUtils.java │ │ │ │ │ │ │ ├── BlockMonitorManager.java │ │ │ │ │ │ │ ├── MonitorCore.java │ │ │ │ │ │ │ ├── OnBlockInfoUpdateListener.java │ │ │ │ │ │ │ └── StackSampler.java │ │ │ │ │ │ ├── colorpick/ │ │ │ │ │ │ │ ├── ColorPickConstants.java │ │ │ │ │ │ │ ├── ColorPickManager.java │ │ │ │ │ │ │ ├── ColorPickerDoKitView.java │ │ │ │ │ │ │ ├── ColorPickerInfoDoKitView.java │ │ │ │ │ │ │ ├── ColorPickerKit.kt │ │ │ │ │ │ │ ├── ColorPickerSettingFragment.java │ │ │ │ │ │ │ ├── ColorPickerView.java │ │ │ │ │ │ │ ├── ImageCapture.java │ │ │ │ │ │ │ └── ScreenRecorderService.java │ │ │ │ │ │ ├── connect/ │ │ │ │ │ │ │ ├── ConnectAddress.kt │ │ │ │ │ │ │ ├── ConnectAddressStore.kt │ │ │ │ │ │ │ ├── ConnectConfig.kt │ │ │ │ │ │ │ ├── ConnectListAdapter.kt │ │ │ │ │ │ │ ├── DoKitConnectFragment.kt │ │ │ │ │ │ │ ├── DoKitConnectManager.kt │ │ │ │ │ │ │ ├── DoKitScanActivity.kt │ │ │ │ │ │ │ ├── DoKitStudioConnectKit.kt │ │ │ │ │ │ │ ├── data/ │ │ │ │ │ │ │ │ ├── BytePackage.kt │ │ │ │ │ │ │ │ ├── LoginData.kt │ │ │ │ │ │ │ │ ├── PackageType.kt │ │ │ │ │ │ │ │ └── TextPackage.kt │ │ │ │ │ │ │ ├── parser/ │ │ │ │ │ │ │ │ ├── ByteParser.kt │ │ │ │ │ │ │ │ └── JsonParser.kt │ │ │ │ │ │ │ └── ws/ │ │ │ │ │ │ │ ├── ConnectStatus.kt │ │ │ │ │ │ │ ├── OkHttpWebSocketSession.kt │ │ │ │ │ │ │ ├── OnWebSocketBytesMessageListener.kt │ │ │ │ │ │ │ ├── OnWebSocketCloseListener.kt │ │ │ │ │ │ │ ├── OnWebSocketLoginSuccessListener.kt │ │ │ │ │ │ │ ├── OnWebSocketMessageListener.kt │ │ │ │ │ │ │ ├── OnWebSocketQueueSizeOutListener.kt │ │ │ │ │ │ │ ├── OnWebSocketReConnectListener.kt │ │ │ │ │ │ │ ├── OnWebSocketStatusChangeListener.kt │ │ │ │ │ │ │ ├── OnWebSocketTextPackageListener.kt │ │ │ │ │ │ │ ├── WebSocketClient.kt │ │ │ │ │ │ │ ├── WebSocketSession.kt │ │ │ │ │ │ │ └── WsLog.kt │ │ │ │ │ │ ├── core/ │ │ │ │ │ │ │ ├── AbsDoKitFragment.kt │ │ │ │ │ │ │ ├── AbsDoKitView.kt │ │ │ │ │ │ │ ├── AbsDoKitViewManager.kt │ │ │ │ │ │ │ ├── ActivityLifecycleStatusInfo.kt │ │ │ │ │ │ │ ├── BaseActivity.kt │ │ │ │ │ │ │ ├── BaseFragment.kt │ │ │ │ │ │ │ ├── DoKitFrameLayout.java │ │ │ │ │ │ │ ├── DoKitIntent.kt │ │ │ │ │ │ │ ├── DoKitKeyEvent.kt │ │ │ │ │ │ │ ├── DoKitLifecycleInterface.kt │ │ │ │ │ │ │ ├── DoKitManager.kt │ │ │ │ │ │ │ ├── DoKitServiceEnum.kt │ │ │ │ │ │ │ ├── DoKitServiceManager.kt │ │ │ │ │ │ │ ├── DoKitView.java │ │ │ │ │ │ │ ├── DoKitViewInfo.kt │ │ │ │ │ │ │ ├── DoKitViewInterface.java │ │ │ │ │ │ │ ├── DoKitViewLayoutParams.java │ │ │ │ │ │ │ ├── DoKitViewManager.kt │ │ │ │ │ │ │ ├── DoKitViewManagerInterface.kt │ │ │ │ │ │ │ ├── DokitAbility.kt │ │ │ │ │ │ │ ├── GlobalSingleDoKitViewInfo.kt │ │ │ │ │ │ │ ├── LastDoKitViewPosInfo.java │ │ │ │ │ │ │ ├── McClientProcessor.kt │ │ │ │ │ │ │ ├── NewBaseActivity.kt │ │ │ │ │ │ │ ├── NormalDoKitViewManager.kt │ │ │ │ │ │ │ ├── SettingItem.java │ │ │ │ │ │ │ ├── SettingItemAdapter.java │ │ │ │ │ │ │ ├── SimpleDoKitLauncher.kt │ │ │ │ │ │ │ ├── SystemDoKitViewManager.kt │ │ │ │ │ │ │ ├── TouchProxy.java │ │ │ │ │ │ │ ├── TranslucentActivity.java │ │ │ │ │ │ │ └── UniversalActivity.kt │ │ │ │ │ │ ├── crash/ │ │ │ │ │ │ │ ├── CrashCaptureKit.kt │ │ │ │ │ │ │ ├── CrashCaptureMainFragment.java │ │ │ │ │ │ │ ├── CrashCaptureManager.java │ │ │ │ │ │ │ ├── CrashHistoryAdapter.java │ │ │ │ │ │ │ └── CrashInfo.java │ │ │ │ │ │ ├── dataclean/ │ │ │ │ │ │ │ ├── DataCleanFragment.kt │ │ │ │ │ │ │ └── DataCleanKit.kt │ │ │ │ │ │ ├── dbdebug/ │ │ │ │ │ │ │ ├── DbDebugFragment.java │ │ │ │ │ │ │ └── DbDebugKit.kt │ │ │ │ │ │ ├── dokitforweb/ │ │ │ │ │ │ │ ├── DoKitForWebJsInjectFragment.kt │ │ │ │ │ │ │ ├── DokitForWeb.kt │ │ │ │ │ │ │ └── DokitForWebKit.kt │ │ │ │ │ │ ├── fileexplorer/ │ │ │ │ │ │ │ ├── DBListAdapter.java │ │ │ │ │ │ │ ├── DatabaseDetailFragment.java │ │ │ │ │ │ │ ├── FileExplorerChooseDialog.java │ │ │ │ │ │ │ ├── FileExplorerFragment.java │ │ │ │ │ │ │ ├── FileExplorerKit.kt │ │ │ │ │ │ │ ├── FileInfo.java │ │ │ │ │ │ │ ├── FileInfoAdapter.java │ │ │ │ │ │ │ ├── ImageDetailFragment.java │ │ │ │ │ │ │ ├── SpAdapter.java │ │ │ │ │ │ │ ├── SpBean.java │ │ │ │ │ │ │ ├── SpFragment.java │ │ │ │ │ │ │ ├── SpInputView.java │ │ │ │ │ │ │ ├── TextContentAdapter.java │ │ │ │ │ │ │ ├── TextDetailFragment.java │ │ │ │ │ │ │ └── VideoPlayFragment.java │ │ │ │ │ │ ├── h5_help/ │ │ │ │ │ │ │ ├── DoKitJSI.kt │ │ │ │ │ │ │ ├── DoKitWebViewClient.kt │ │ │ │ │ │ │ ├── DoKitX5WebViewClient.kt │ │ │ │ │ │ │ ├── H5DoKitView.kt │ │ │ │ │ │ │ ├── H5Kit.kt │ │ │ │ │ │ │ ├── JsHookDataManager.kt │ │ │ │ │ │ │ ├── JsHttpUtil.kt │ │ │ │ │ │ │ ├── LocalStorageAdapter.kt │ │ │ │ │ │ │ ├── ProxyWebViewClient.kt │ │ │ │ │ │ │ ├── ProxyX5WebViewClient.kt │ │ │ │ │ │ │ ├── X5WebViewUtil.kt │ │ │ │ │ │ │ └── bean/ │ │ │ │ │ │ │ ├── JsRequestBean.kt │ │ │ │ │ │ │ └── StorageBean.kt │ │ │ │ │ │ ├── health/ │ │ │ │ │ │ │ ├── AbsCountDownDoKitView.kt │ │ │ │ │ │ │ ├── AppHealthInfoUtil.java │ │ │ │ │ │ │ ├── CountDownDoKitView.kt │ │ │ │ │ │ │ ├── HealthFragment.java │ │ │ │ │ │ │ ├── HealthFragmentChild0.java │ │ │ │ │ │ │ ├── HealthFragmentChild1.java │ │ │ │ │ │ │ ├── HealthKit.kt │ │ │ │ │ │ │ ├── UploadAppHealthCallback.java │ │ │ │ │ │ │ ├── UserInfoDialogProvider.java │ │ │ │ │ │ │ └── model/ │ │ │ │ │ │ │ └── AppHealthInfo.java │ │ │ │ │ │ ├── largepicture/ │ │ │ │ │ │ │ ├── LargeImageInfo.java │ │ │ │ │ │ │ ├── LargeImageListAdapter.java │ │ │ │ │ │ │ ├── LargeImageListFragment.java │ │ │ │ │ │ │ ├── LargePictureFragment.java │ │ │ │ │ │ │ ├── LargePictureItemAdapter.java │ │ │ │ │ │ │ ├── LargePictureKit.kt │ │ │ │ │ │ │ └── LargePictureManager.java │ │ │ │ │ │ ├── layoutborder/ │ │ │ │ │ │ │ ├── LayoutBorderKit.kt │ │ │ │ │ │ │ ├── LayoutBorderManager.java │ │ │ │ │ │ │ ├── LayoutBorderSettingFragment.java │ │ │ │ │ │ │ ├── LayoutLevelDoKitView.java │ │ │ │ │ │ │ ├── ScalpelFrameLayout.java │ │ │ │ │ │ │ ├── ViewBorderDrawable.java │ │ │ │ │ │ │ └── ViewBorderFrameLayout.java │ │ │ │ │ │ ├── loginfo/ │ │ │ │ │ │ │ ├── LogExportDialog.java │ │ │ │ │ │ │ ├── LogInfoDoKitView.java │ │ │ │ │ │ │ ├── LogInfoKit.kt │ │ │ │ │ │ │ ├── LogInfoManager.java │ │ │ │ │ │ │ ├── LogInfoSettingFragment.java │ │ │ │ │ │ │ ├── LogItemAdapter.java │ │ │ │ │ │ │ ├── LogLine.java │ │ │ │ │ │ │ ├── helper/ │ │ │ │ │ │ │ │ ├── LogcatHelper.java │ │ │ │ │ │ │ │ └── RuntimeHelper.java │ │ │ │ │ │ │ ├── reader/ │ │ │ │ │ │ │ │ ├── AbsLogcatReader.java │ │ │ │ │ │ │ │ ├── LogcatReader.java │ │ │ │ │ │ │ │ ├── LogcatReaderLoader.java │ │ │ │ │ │ │ │ ├── ScrubberUtils.java │ │ │ │ │ │ │ │ └── SingleLogcatReader.java │ │ │ │ │ │ │ └── util/ │ │ │ │ │ │ │ ├── ArrayUtil.java │ │ │ │ │ │ │ ├── SearchCriteria.java │ │ │ │ │ │ │ ├── StringUtil.java │ │ │ │ │ │ │ └── TagColorUtil.java │ │ │ │ │ │ ├── main/ │ │ │ │ │ │ │ └── MainIconDoKitView.kt │ │ │ │ │ │ ├── methodtrace/ │ │ │ │ │ │ │ ├── AppHealthMethodCostBean.java │ │ │ │ │ │ │ ├── AppHealthMethodCostBeanWrap.java │ │ │ │ │ │ │ └── MethodCostKit.kt │ │ │ │ │ │ ├── network/ │ │ │ │ │ │ │ ├── MockKit.kt │ │ │ │ │ │ │ ├── NetworkKit.kt │ │ │ │ │ │ │ ├── NetworkManager.java │ │ │ │ │ │ │ ├── OnNetworkInfoUpdateListener.java │ │ │ │ │ │ │ ├── bean/ │ │ │ │ │ │ │ │ ├── MockApiResponseBean.java │ │ │ │ │ │ │ │ ├── MockInterceptTitleBean.java │ │ │ │ │ │ │ │ ├── MockTemplateTitleBean.java │ │ │ │ │ │ │ │ ├── NetflowInfo.java │ │ │ │ │ │ │ │ ├── NetworkRecord.java │ │ │ │ │ │ │ │ ├── Request.java │ │ │ │ │ │ │ │ ├── Response.java │ │ │ │ │ │ │ │ └── WhiteHostBean.java │ │ │ │ │ │ │ ├── common/ │ │ │ │ │ │ │ │ ├── CommonHeaders.java │ │ │ │ │ │ │ │ ├── CommonInspectorRequest.java │ │ │ │ │ │ │ │ ├── CommonInspectorResponse.java │ │ │ │ │ │ │ │ └── NetworkPrinterHelper.java │ │ │ │ │ │ │ ├── core/ │ │ │ │ │ │ │ │ ├── DefaultResponseHandler.java │ │ │ │ │ │ │ │ ├── MimeMatcher.java │ │ │ │ │ │ │ │ ├── NetworkInterpreter.java │ │ │ │ │ │ │ │ ├── RequestBodyHelper.java │ │ │ │ │ │ │ │ ├── ResourceType.java │ │ │ │ │ │ │ │ ├── ResourceTypeHelper.java │ │ │ │ │ │ │ │ └── ResponseHandler.java │ │ │ │ │ │ │ ├── okhttp/ │ │ │ │ │ │ │ │ ├── ForwardingResponseBody.java │ │ │ │ │ │ │ │ ├── InterceptorUtil.java │ │ │ │ │ │ │ │ ├── OkHttpInspectorRequest.java │ │ │ │ │ │ │ │ ├── OkHttpInspectorResponse.java │ │ │ │ │ │ │ │ └── interceptor/ │ │ │ │ │ │ │ │ ├── AbsDoKitInterceptor.kt │ │ │ │ │ │ │ │ ├── DokitCapInterceptor.java │ │ │ │ │ │ │ │ ├── DokitExtInterceptor.kt │ │ │ │ │ │ │ │ ├── DokitLargePicInterceptor.java │ │ │ │ │ │ │ │ ├── DokitMockInterceptor.java │ │ │ │ │ │ │ │ └── DokitWeakNetworkInterceptor.java │ │ │ │ │ │ │ ├── room_db/ │ │ │ │ │ │ │ │ ├── AbsMockApiBean.java │ │ │ │ │ │ │ │ ├── DokitDatabase.java │ │ │ │ │ │ │ │ ├── DokitDbManager.java │ │ │ │ │ │ │ │ ├── MockApiDao.java │ │ │ │ │ │ │ │ ├── MockInterceptApiBean.java │ │ │ │ │ │ │ │ └── MockTemplateApiBean.java │ │ │ │ │ │ │ ├── stream/ │ │ │ │ │ │ │ │ ├── GunzippingOutputStream.java │ │ │ │ │ │ │ │ ├── HttpOutputStreamProxy.java │ │ │ │ │ │ │ │ ├── InputStreamProxy.java │ │ │ │ │ │ │ │ └── OutputStreamProxy.java │ │ │ │ │ │ │ ├── ui/ │ │ │ │ │ │ │ │ ├── InterceptDetailNodeProvider.java │ │ │ │ │ │ │ │ ├── InterceptMockAdapter.java │ │ │ │ │ │ │ │ ├── InterceptTitleNodeProvider.java │ │ │ │ │ │ │ │ ├── ListDropDownAdapter.java │ │ │ │ │ │ │ │ ├── MockTemplatePreviewFragment.java │ │ │ │ │ │ │ │ ├── NetWorkMainPagerAdapter.java │ │ │ │ │ │ │ │ ├── NetWorkMainPagerFragment.java │ │ │ │ │ │ │ │ ├── NetWorkMockFragment.java │ │ │ │ │ │ │ │ ├── NetWorkMonitorFragment.java │ │ │ │ │ │ │ │ ├── NetWorkSummaryView.java │ │ │ │ │ │ │ │ ├── NetworkDetailFragment.java │ │ │ │ │ │ │ │ ├── NetworkDetailView.java │ │ │ │ │ │ │ │ ├── NetworkListAdapter.java │ │ │ │ │ │ │ │ ├── NetworkListView.java │ │ │ │ │ │ │ │ ├── NetworkPagerAdapter.java │ │ │ │ │ │ │ │ ├── TemplateDetailNodeProvider.java │ │ │ │ │ │ │ │ ├── TemplateMockAdapter.java │ │ │ │ │ │ │ │ ├── TemplateTitleNodeProvider.java │ │ │ │ │ │ │ │ └── WhiteHostAdapter.java │ │ │ │ │ │ │ └── utils/ │ │ │ │ │ │ │ ├── ByteUtil.java │ │ │ │ │ │ │ ├── CostTimeUtil.java │ │ │ │ │ │ │ ├── ExceptionUtil.java │ │ │ │ │ │ │ ├── OkHttpResponse.kt │ │ │ │ │ │ │ ├── StreamUtil.java │ │ │ │ │ │ │ └── Utf8Charset.java │ │ │ │ │ │ ├── parameter/ │ │ │ │ │ │ │ ├── AbsParameterFragment.java │ │ │ │ │ │ │ ├── cpu/ │ │ │ │ │ │ │ │ ├── CpuKit.kt │ │ │ │ │ │ │ │ └── CpuMainPageFragment.java │ │ │ │ │ │ │ ├── frameInfo/ │ │ │ │ │ │ │ │ ├── FrameInfoFragment.java │ │ │ │ │ │ │ │ └── FrameInfoKit.kt │ │ │ │ │ │ │ └── ram/ │ │ │ │ │ │ │ ├── RamKit.kt │ │ │ │ │ │ │ └── RamMainPageFragment.java │ │ │ │ │ │ ├── performance/ │ │ │ │ │ │ │ ├── PerformanceCloseDoKitView.java │ │ │ │ │ │ │ ├── PerformanceCloseListener.java │ │ │ │ │ │ │ ├── PerformanceData.java │ │ │ │ │ │ │ ├── PerformanceDataAdapter.java │ │ │ │ │ │ │ ├── PerformanceDataManager.java │ │ │ │ │ │ │ ├── PerformanceDoKitView.java │ │ │ │ │ │ │ ├── PerformanceDokitViewManager.java │ │ │ │ │ │ │ ├── PerformanceFragment.java │ │ │ │ │ │ │ ├── PerformanceFragmentCloseListener.java │ │ │ │ │ │ │ ├── PolyLineAdapter.java │ │ │ │ │ │ │ ├── PolyLineItemView.java │ │ │ │ │ │ │ ├── datasource/ │ │ │ │ │ │ │ │ ├── CpuDataSource.java │ │ │ │ │ │ │ │ ├── DataSourceFactory.java │ │ │ │ │ │ │ │ ├── DefaultDataSource.java │ │ │ │ │ │ │ │ ├── FpsDataSource.java │ │ │ │ │ │ │ │ ├── IDataSource.java │ │ │ │ │ │ │ │ ├── NetworkDataSource.java │ │ │ │ │ │ │ │ └── RamDataSource.java │ │ │ │ │ │ │ ├── performanceViewInfo.java │ │ │ │ │ │ │ └── widget/ │ │ │ │ │ │ │ ├── CardiogramView.java │ │ │ │ │ │ │ ├── LineChart.java │ │ │ │ │ │ │ ├── LineData.java │ │ │ │ │ │ │ └── LineRender.java │ │ │ │ │ │ ├── sysinfo/ │ │ │ │ │ │ │ ├── DevelopmentPageKit.kt │ │ │ │ │ │ │ ├── LocalLangKit.kt │ │ │ │ │ │ │ ├── ServiceRunningKit.kt │ │ │ │ │ │ │ ├── SysInfoFragment.java │ │ │ │ │ │ │ ├── SysInfoItem.java │ │ │ │ │ │ │ ├── SysInfoItemAdapter.java │ │ │ │ │ │ │ ├── SysInfoKit.kt │ │ │ │ │ │ │ ├── ThirdLibInfoFragment.java │ │ │ │ │ │ │ ├── ThirdLibInfoItemAdapter.java │ │ │ │ │ │ │ ├── ThirdLibInfoKit.kt │ │ │ │ │ │ │ └── TitleItem.java │ │ │ │ │ │ ├── timecounter/ │ │ │ │ │ │ │ ├── AppStartInfoFragment.java │ │ │ │ │ │ │ ├── TimeCounterDoKitView.java │ │ │ │ │ │ │ ├── TimeCounterFragment.java │ │ │ │ │ │ │ ├── TimeCounterKit.kt │ │ │ │ │ │ │ ├── TimeCounterListAdapter.java │ │ │ │ │ │ │ ├── TimeCounterListFragment.java │ │ │ │ │ │ │ ├── TimeCounterManager.java │ │ │ │ │ │ │ ├── bean/ │ │ │ │ │ │ │ │ └── CounterInfo.java │ │ │ │ │ │ │ ├── counter/ │ │ │ │ │ │ │ │ ├── ActivityCounter.java │ │ │ │ │ │ │ │ └── AppCounter.java │ │ │ │ │ │ │ └── instrumentation/ │ │ │ │ │ │ │ ├── HandlerHooker.java │ │ │ │ │ │ │ └── ProxyHandlerCallback.java │ │ │ │ │ │ ├── toolpanel/ │ │ │ │ │ │ │ ├── ConfirmDialogProvider.kt │ │ │ │ │ │ │ ├── DokitManagerAdapter.kt │ │ │ │ │ │ │ ├── DokitManagerFragment.kt │ │ │ │ │ │ │ ├── DokitMoreAdapter.kt │ │ │ │ │ │ │ ├── DokitMoreFragment.kt │ │ │ │ │ │ │ ├── KitBeans.kt │ │ │ │ │ │ │ ├── KitWrapItem.kt │ │ │ │ │ │ │ ├── TipDialogProvider.kt │ │ │ │ │ │ │ ├── ToolPanelAdapter.kt │ │ │ │ │ │ │ ├── ToolPanelDoKitView.kt │ │ │ │ │ │ │ ├── ToolPanelUtil.kt │ │ │ │ │ │ │ ├── bean/ │ │ │ │ │ │ │ │ └── MorePageGroupBean.java │ │ │ │ │ │ │ └── decoration/ │ │ │ │ │ │ │ ├── FlexibleDividerDecoration.java │ │ │ │ │ │ │ ├── HorizontalDividerItemDecoration.java │ │ │ │ │ │ │ └── VerticalDividerItemDecoration.java │ │ │ │ │ │ ├── uiperformance/ │ │ │ │ │ │ │ ├── UIPerformanceDisplayDoKitView.java │ │ │ │ │ │ │ ├── UIPerformanceInfoDoKitView.java │ │ │ │ │ │ │ ├── UIPerformanceKit.kt │ │ │ │ │ │ │ ├── UIPerformanceManager.java │ │ │ │ │ │ │ └── UIPerformanceUtil.java │ │ │ │ │ │ ├── viewcheck/ │ │ │ │ │ │ │ ├── AimCircleView.java │ │ │ │ │ │ │ ├── DebugAccessibilityService.java │ │ │ │ │ │ │ ├── LayoutBorderView.java │ │ │ │ │ │ │ ├── ViewCheckDoKitView.java │ │ │ │ │ │ │ ├── ViewCheckDrawDoKitView.java │ │ │ │ │ │ │ ├── ViewCheckInfoDoKitView.java │ │ │ │ │ │ │ └── ViewCheckerKit.kt │ │ │ │ │ │ ├── weaknetwork/ │ │ │ │ │ │ │ ├── NetWokDoKitView.java │ │ │ │ │ │ │ ├── SpeedLimitRequestBody.java │ │ │ │ │ │ │ ├── SpeedLimitResponseBody.java │ │ │ │ │ │ │ ├── WeakNetworkFragment.java │ │ │ │ │ │ │ ├── WeakNetworkKit.kt │ │ │ │ │ │ │ └── WeakNetworkManager.java │ │ │ │ │ │ ├── webdoor/ │ │ │ │ │ │ │ ├── WebDoorDefaultFragment.java │ │ │ │ │ │ │ ├── WebDoorFragment.java │ │ │ │ │ │ │ ├── WebDoorHistoryAdapter.java │ │ │ │ │ │ │ ├── WebDoorKit.kt │ │ │ │ │ │ │ └── WebDoorManager.java │ │ │ │ │ │ └── webview/ │ │ │ │ │ │ ├── CommWebViewFragment.kt │ │ │ │ │ │ ├── OnWebViewTitleChangeCallBack.java │ │ │ │ │ │ └── WebViewManager.kt │ │ │ │ │ ├── model/ │ │ │ │ │ │ ├── LatLng.java │ │ │ │ │ │ └── ViewInfo.java │ │ │ │ │ ├── picasso/ │ │ │ │ │ │ ├── Action.java │ │ │ │ │ │ ├── AssetRequestHandler.java │ │ │ │ │ │ ├── BitmapHunter.java │ │ │ │ │ │ ├── Cache.java │ │ │ │ │ │ ├── Callback.java │ │ │ │ │ │ ├── ContactsPhotoRequestHandler.java │ │ │ │ │ │ ├── ContentStreamRequestHandler.java │ │ │ │ │ │ ├── DeferredRequestCreator.java │ │ │ │ │ │ ├── Dispatcher.java │ │ │ │ │ │ ├── DokitPicasso.java │ │ │ │ │ │ ├── Downloader.java │ │ │ │ │ │ ├── FetchAction.java │ │ │ │ │ │ ├── FileRequestHandler.java │ │ │ │ │ │ ├── GetAction.java │ │ │ │ │ │ ├── ImageViewAction.java │ │ │ │ │ │ ├── LruCache.java │ │ │ │ │ │ ├── MarkableInputStream.java │ │ │ │ │ │ ├── MediaStoreRequestHandler.java │ │ │ │ │ │ ├── MemoryPolicy.java │ │ │ │ │ │ ├── NetworkPolicy.java │ │ │ │ │ │ ├── NetworkRequestHandler.java │ │ │ │ │ │ ├── OkHttpDownloader.java │ │ │ │ │ │ ├── PicassoDrawable.java │ │ │ │ │ │ ├── PicassoExecutorService.java │ │ │ │ │ │ ├── RemoteViewsAction.java │ │ │ │ │ │ ├── Request.java │ │ │ │ │ │ ├── RequestCreator.java │ │ │ │ │ │ ├── RequestHandler.java │ │ │ │ │ │ ├── ResourceRequestHandler.java │ │ │ │ │ │ ├── Stats.java │ │ │ │ │ │ ├── StatsSnapshot.java │ │ │ │ │ │ ├── Target.java │ │ │ │ │ │ ├── TargetAction.java │ │ │ │ │ │ ├── Transformation.java │ │ │ │ │ │ ├── UrlConnectionDownloader.java │ │ │ │ │ │ └── Utils.java │ │ │ │ │ ├── reflection/ │ │ │ │ │ │ ├── BootstrapClass.java │ │ │ │ │ │ └── Reflection.java │ │ │ │ │ ├── util/ │ │ │ │ │ │ ├── ColorUtil.java │ │ │ │ │ │ ├── DataCleanUtil.java │ │ │ │ │ │ ├── DatabaseUtil.java │ │ │ │ │ │ ├── DoKitCacheUtils.java │ │ │ │ │ │ ├── DoKitClipboardUtils.java │ │ │ │ │ │ ├── DoKitCommUtil.java │ │ │ │ │ │ ├── DoKitExecutorUtil.java │ │ │ │ │ │ ├── DoKitFileUtil.java │ │ │ │ │ │ ├── DoKitFormatUtil.java │ │ │ │ │ │ ├── DoKitImageUtil.java │ │ │ │ │ │ ├── DoKitJsonUtil.java │ │ │ │ │ │ ├── DoKitMiscUtil.java │ │ │ │ │ │ ├── DoKitNotificationUtils.java │ │ │ │ │ │ ├── DoKitPermissionUtil.java │ │ │ │ │ │ ├── DoKitSPUtil.java │ │ │ │ │ │ ├── DoKitSystemUtil.java │ │ │ │ │ │ ├── DoKitWebUtil.java │ │ │ │ │ │ ├── DokitDeviceUtils.java │ │ │ │ │ │ ├── DoraemonStatisticsUtil.java │ │ │ │ │ │ ├── FileManager.java │ │ │ │ │ │ ├── LifecycleListenerUtil.java │ │ │ │ │ │ ├── LogHelper.java │ │ │ │ │ │ ├── Reflector.java │ │ │ │ │ │ ├── UIUtils.java │ │ │ │ │ │ └── threadpool/ │ │ │ │ │ │ ├── ThreadPoolProxy.java │ │ │ │ │ │ └── ThreadPoolProxyFactory.java │ │ │ │ │ ├── volley/ │ │ │ │ │ │ └── VolleyManager.kt │ │ │ │ │ ├── widget/ │ │ │ │ │ │ ├── JustifyTextView.java │ │ │ │ │ │ ├── MultiLineRadioGroup.java │ │ │ │ │ │ ├── bottomview/ │ │ │ │ │ │ │ ├── AssociationView.java │ │ │ │ │ │ │ ├── BottomUpWindow.java │ │ │ │ │ │ │ └── EditSpInputView.java │ │ │ │ │ │ ├── brvah/ │ │ │ │ │ │ │ ├── BaseBinderAdapter.kt │ │ │ │ │ │ │ ├── BaseDelegateMultiAdapter.kt │ │ │ │ │ │ │ ├── BaseMultiItemQuickAdapter.kt │ │ │ │ │ │ │ ├── BaseNodeAdapter.kt │ │ │ │ │ │ │ ├── BaseProviderMultiAdapter.kt │ │ │ │ │ │ │ ├── BaseQuickAdapter.kt │ │ │ │ │ │ │ ├── BaseSectionQuickAdapter.kt │ │ │ │ │ │ │ ├── animation/ │ │ │ │ │ │ │ │ ├── AlphaInAnimation.kt │ │ │ │ │ │ │ │ ├── BaseAnimation.kt │ │ │ │ │ │ │ │ ├── ScaleInAnimation.kt │ │ │ │ │ │ │ │ ├── SlideInBottomAnimation.kt │ │ │ │ │ │ │ │ ├── SlideInLeftAnimation.kt │ │ │ │ │ │ │ │ └── SlideInRightAnimation.kt │ │ │ │ │ │ │ ├── binder/ │ │ │ │ │ │ │ │ ├── BaseItemBinder.kt │ │ │ │ │ │ │ │ ├── QuickDataBindingItemBinder.kt │ │ │ │ │ │ │ │ ├── QuickItemBinder.kt │ │ │ │ │ │ │ │ └── QuickViewBindingItemBinder.kt │ │ │ │ │ │ │ ├── delegate/ │ │ │ │ │ │ │ │ └── BaseMultiTypeDelegate.kt │ │ │ │ │ │ │ ├── diff/ │ │ │ │ │ │ │ │ ├── BrvahAsyncDiffer.kt │ │ │ │ │ │ │ │ ├── BrvahAsyncDifferConfig.kt │ │ │ │ │ │ │ │ ├── BrvahListUpdateCallback.kt │ │ │ │ │ │ │ │ ├── DifferImp.java │ │ │ │ │ │ │ │ └── ListChangeListener.java │ │ │ │ │ │ │ ├── dragswipe/ │ │ │ │ │ │ │ │ └── DragAndSwipeCallback.java │ │ │ │ │ │ │ ├── entity/ │ │ │ │ │ │ │ │ ├── JSectionEntity.java │ │ │ │ │ │ │ │ ├── MultiItemEntity.kt │ │ │ │ │ │ │ │ ├── SectionEntity.kt │ │ │ │ │ │ │ │ └── node/ │ │ │ │ │ │ │ │ ├── BaseExpandNode.kt │ │ │ │ │ │ │ │ ├── BaseNode.kt │ │ │ │ │ │ │ │ └── NodeFooterImp.kt │ │ │ │ │ │ │ ├── listener/ │ │ │ │ │ │ │ │ ├── BaseListenerImp.java │ │ │ │ │ │ │ │ ├── DraggableListenerImp.java │ │ │ │ │ │ │ │ ├── GridSpanSizeLookup.java │ │ │ │ │ │ │ │ ├── LoadMoreListenerImp.java │ │ │ │ │ │ │ │ ├── OnItemChildClickListener.java │ │ │ │ │ │ │ │ ├── OnItemChildLongClickListener.java │ │ │ │ │ │ │ │ ├── OnItemClickListener.java │ │ │ │ │ │ │ │ ├── OnItemDragListener.java │ │ │ │ │ │ │ │ ├── OnItemLongClickListener.java │ │ │ │ │ │ │ │ ├── OnItemSwipeListener.java │ │ │ │ │ │ │ │ ├── OnLoadMoreListener.java │ │ │ │ │ │ │ │ ├── OnUpFetchListener.java │ │ │ │ │ │ │ │ └── UpFetchListenerImp.java │ │ │ │ │ │ │ ├── loadmore/ │ │ │ │ │ │ │ │ ├── BaseLoadMoreView.kt │ │ │ │ │ │ │ │ └── SimpleLoadMoreView.kt │ │ │ │ │ │ │ ├── module/ │ │ │ │ │ │ │ │ ├── DraggableModule.kt │ │ │ │ │ │ │ │ ├── LoadMoreModule.kt │ │ │ │ │ │ │ │ └── UpFetchModule.kt │ │ │ │ │ │ │ ├── provider/ │ │ │ │ │ │ │ │ ├── BaseItemProvider.kt │ │ │ │ │ │ │ │ └── BaseNodeProvider.kt │ │ │ │ │ │ │ ├── util/ │ │ │ │ │ │ │ │ └── AdapterUtils.kt │ │ │ │ │ │ │ └── viewholder/ │ │ │ │ │ │ │ ├── BaseDataBindingHolder.kt │ │ │ │ │ │ │ └── BaseViewHolder.kt │ │ │ │ │ │ ├── chart/ │ │ │ │ │ │ │ ├── BarChart.java │ │ │ │ │ │ │ └── PieChart.java │ │ │ │ │ │ ├── dialog/ │ │ │ │ │ │ │ ├── CommonDialogProvider.java │ │ │ │ │ │ │ ├── DialogInfo.java │ │ │ │ │ │ │ ├── DialogListener.kt │ │ │ │ │ │ │ ├── DialogProvider.java │ │ │ │ │ │ │ ├── SimpleDialogListener.java │ │ │ │ │ │ │ └── UniversalDialogFragment.java │ │ │ │ │ │ ├── dropdown/ │ │ │ │ │ │ │ ├── DkDropDownMenu.java │ │ │ │ │ │ │ └── Orientation.java │ │ │ │ │ │ ├── easyrefresh/ │ │ │ │ │ │ │ ├── ELog.java │ │ │ │ │ │ │ ├── EasyRefreshLayout.java │ │ │ │ │ │ │ ├── ILoadMoreView.java │ │ │ │ │ │ │ ├── IRefreshHeader.java │ │ │ │ │ │ │ ├── LoadModel.java │ │ │ │ │ │ │ ├── State.java │ │ │ │ │ │ │ ├── exception/ │ │ │ │ │ │ │ │ └── ERVHRuntimeException.java │ │ │ │ │ │ │ └── view/ │ │ │ │ │ │ │ ├── SimpleLoadMoreView.java │ │ │ │ │ │ │ └── SimpleRefreshHeaderView.java │ │ │ │ │ │ ├── jsonviewer/ │ │ │ │ │ │ │ ├── JsonRecyclerView.java │ │ │ │ │ │ │ ├── adapter/ │ │ │ │ │ │ │ │ ├── BaseJsonViewerAdapter.java │ │ │ │ │ │ │ │ └── JsonViewerAdapter.java │ │ │ │ │ │ │ ├── utils/ │ │ │ │ │ │ │ │ └── Utils.java │ │ │ │ │ │ │ └── view/ │ │ │ │ │ │ │ └── JsonItemView.java │ │ │ │ │ │ ├── recyclerview/ │ │ │ │ │ │ │ ├── AbsRecyclerAdapter.java │ │ │ │ │ │ │ ├── AbsViewBinder.java │ │ │ │ │ │ │ └── DividerItemDecoration.java │ │ │ │ │ │ ├── tableview/ │ │ │ │ │ │ │ ├── SelectionOperation.java │ │ │ │ │ │ │ ├── TableConfig.java │ │ │ │ │ │ │ ├── TableMeasurer.java │ │ │ │ │ │ │ ├── TableParser.java │ │ │ │ │ │ │ ├── TableProvider.java │ │ │ │ │ │ │ ├── bean/ │ │ │ │ │ │ │ │ ├── ArrayTableData.java │ │ │ │ │ │ │ │ ├── Cell.java │ │ │ │ │ │ │ │ ├── CellInfo.java │ │ │ │ │ │ │ │ ├── Column.java │ │ │ │ │ │ │ │ ├── ColumnInfo.java │ │ │ │ │ │ │ │ ├── TableData.java │ │ │ │ │ │ │ │ └── TableInfo.java │ │ │ │ │ │ │ ├── component/ │ │ │ │ │ │ │ │ ├── SmartTable.java │ │ │ │ │ │ │ │ ├── TableTitle.java │ │ │ │ │ │ │ │ └── YSequence.java │ │ │ │ │ │ │ ├── format/ │ │ │ │ │ │ │ │ ├── BaseSequenceFormat.java │ │ │ │ │ │ │ │ ├── FastTextDrawFormat.java │ │ │ │ │ │ │ │ ├── NumberSequenceFormat.java │ │ │ │ │ │ │ │ ├── TextDrawFormat.java │ │ │ │ │ │ │ │ └── TitleDrawFormat.java │ │ │ │ │ │ │ ├── intface/ │ │ │ │ │ │ │ │ ├── IComponent.java │ │ │ │ │ │ │ │ ├── IDrawFormat.java │ │ │ │ │ │ │ │ ├── IFormat.java │ │ │ │ │ │ │ │ ├── ISelectFormat.java │ │ │ │ │ │ │ │ ├── ISequenceFormat.java │ │ │ │ │ │ │ │ ├── IStyle.java │ │ │ │ │ │ │ │ ├── ITableTitle.java │ │ │ │ │ │ │ │ ├── ITitleDrawFormat.java │ │ │ │ │ │ │ │ └── ITouch.java │ │ │ │ │ │ │ ├── listener/ │ │ │ │ │ │ │ │ ├── Observable.java │ │ │ │ │ │ │ │ ├── OnColumnClickListener.java │ │ │ │ │ │ │ │ ├── OnColumnItemClickListener.java │ │ │ │ │ │ │ │ ├── OnTableChangeListener.java │ │ │ │ │ │ │ │ └── TableClickObserver.java │ │ │ │ │ │ │ ├── style/ │ │ │ │ │ │ │ │ ├── FontStyle.java │ │ │ │ │ │ │ │ └── LineStyle.java │ │ │ │ │ │ │ └── utils/ │ │ │ │ │ │ │ ├── DensityUtils.java │ │ │ │ │ │ │ ├── DrawUtils.java │ │ │ │ │ │ │ └── MatrixHelper.java │ │ │ │ │ │ ├── textview/ │ │ │ │ │ │ │ └── LabelTextView.java │ │ │ │ │ │ ├── titlebar/ │ │ │ │ │ │ │ ├── HomeTitleBar.java │ │ │ │ │ │ │ ├── LogTitleBar.java │ │ │ │ │ │ │ └── TitleBar.java │ │ │ │ │ │ ├── verticalviewpager/ │ │ │ │ │ │ │ ├── VerticalViewPager.java │ │ │ │ │ │ │ └── transforms/ │ │ │ │ │ │ │ ├── DefaultTransformer.java │ │ │ │ │ │ │ ├── StackTransformer.java │ │ │ │ │ │ │ └── ZoomOutTransformer.java │ │ │ │ │ │ ├── videoview/ │ │ │ │ │ │ │ ├── CustomVideoView.java │ │ │ │ │ │ │ └── MyVideoView.java │ │ │ │ │ │ └── webview/ │ │ │ │ │ │ ├── MyWebView.java │ │ │ │ │ │ └── MyWebViewClient.java │ │ │ │ │ └── zxing/ │ │ │ │ │ ├── activity/ │ │ │ │ │ │ └── CaptureActivity.java │ │ │ │ │ ├── camera/ │ │ │ │ │ │ ├── AutoFocusCallback.java │ │ │ │ │ │ ├── CameraConfigurationManager.java │ │ │ │ │ │ ├── CameraManager.java │ │ │ │ │ │ ├── FlashlightManager.java │ │ │ │ │ │ ├── PlanarYUVLuminanceSource.java │ │ │ │ │ │ └── PreviewCallback.java │ │ │ │ │ ├── decoding/ │ │ │ │ │ │ ├── CaptureActivityHandler.java │ │ │ │ │ │ ├── DecodeFormatManager.java │ │ │ │ │ │ ├── DecodeHandler.java │ │ │ │ │ │ ├── DecodeThread.java │ │ │ │ │ │ ├── FinishListener.java │ │ │ │ │ │ ├── InactivityTimer.java │ │ │ │ │ │ └── Intents.java │ │ │ │ │ └── view/ │ │ │ │ │ ├── ViewfinderResultPointCallback.java │ │ │ │ │ └── ViewfinderView.java │ │ │ │ └── res/ │ │ │ │ ├── anim/ │ │ │ │ │ ├── dk_dd_mask_in.xml │ │ │ │ │ ├── dk_dd_mask_out.xml │ │ │ │ │ ├── dk_dd_menu_in.xml │ │ │ │ │ ├── dk_dd_menu_out.xml │ │ │ │ │ ├── dk_easy_refresh_rotate_down.xml │ │ │ │ │ ├── dk_easy_refresh_rotate_infinite.xml │ │ │ │ │ └── dk_easy_refresh_rotate_up.xml │ │ │ │ ├── color/ │ │ │ │ │ ├── dk_confirm_button_text_color.xml │ │ │ │ │ ├── dk_network_pager_color.xml │ │ │ │ │ └── dk_radio_button_text_color.xml │ │ │ │ ├── drawable/ │ │ │ │ │ ├── dk_add_shape.xml │ │ │ │ │ ├── dk_app_toast_shape.xml │ │ │ │ │ ├── dk_brvah_sample_footer_loading_progress.xml │ │ │ │ │ ├── dk_btn_dokit_for_web_bg.xml │ │ │ │ │ ├── dk_btn_pause_style.xml │ │ │ │ │ ├── dk_btn_play_style.xml │ │ │ │ │ ├── dk_confirm_button_background.xml │ │ │ │ │ ├── dk_dialog_button_background.xml │ │ │ │ │ ├── dk_dialog_complete_bg.xml │ │ │ │ │ ├── dk_dialog_confirm_bg.xml │ │ │ │ │ ├── dk_divider.xml │ │ │ │ │ ├── dk_divider_gray.xml │ │ │ │ │ ├── dk_dokitview_studio_connect.xml │ │ │ │ │ ├── dk_dotted_line_horizontal.xml │ │ │ │ │ ├── dk_dotted_line_vertical.xml │ │ │ │ │ ├── dk_edittext_shape.xml │ │ │ │ │ ├── dk_float_ui_performance_info_bg.xml │ │ │ │ │ ├── dk_health_edittext_style.xml │ │ │ │ │ ├── dk_hint_bg.xml │ │ │ │ │ ├── dk_info_background.xml │ │ │ │ │ ├── dk_input_cursor.xml │ │ │ │ │ ├── dk_item_btn_img.xml │ │ │ │ │ ├── dk_item_performance_detail.xml │ │ │ │ │ ├── dk_jsonviewer_minus.xml │ │ │ │ │ ├── dk_jsonviewer_plus.xml │ │ │ │ │ ├── dk_kit_group_background.xml │ │ │ │ │ ├── dk_line_chart_selected_background.xml │ │ │ │ │ ├── dk_log_filter_background.xml │ │ │ │ │ ├── dk_net_work_monitor_list_selector.xml │ │ │ │ │ ├── dk_net_work_monitor_summary_selector.xml │ │ │ │ │ ├── dk_network_info_background.xml │ │ │ │ │ ├── dk_network_method_bg.xml │ │ │ │ │ ├── dk_network_platform_bg.xml │ │ │ │ │ ├── dk_perform_data_background.xml │ │ │ │ │ ├── dk_progressbar_style.xml │ │ │ │ │ ├── dk_radio_button_background.xml │ │ │ │ │ ├── dk_radio_button_background_left.xml │ │ │ │ │ ├── dk_radio_button_background_middle.xml │ │ │ │ │ ├── dk_radio_button_background_right.xml │ │ │ │ │ ├── dk_radio_button_checked_background.xml │ │ │ │ │ ├── dk_radio_button_checked_background_left.xml │ │ │ │ │ ├── dk_radio_button_checked_background_middle.xml │ │ │ │ │ ├── dk_radio_button_checked_background_right.xml │ │ │ │ │ ├── dk_radio_button_normal_background.xml │ │ │ │ │ ├── dk_radio_button_normal_background_left.xml │ │ │ │ │ ├── dk_radio_button_normal_background_middle.xml │ │ │ │ │ ├── dk_radio_button_normal_background_right.xml │ │ │ │ │ ├── dk_refresh_rotate_down.xml │ │ │ │ │ ├── dk_seekbar_style.xml │ │ │ │ │ ├── dk_setting_item_background.xml │ │ │ │ │ ├── dk_shape_float_view_bg.xml │ │ │ │ │ ├── dk_switch_background.xml │ │ │ │ │ ├── dk_time_counter_background.xml │ │ │ │ │ ├── dk_view_dot_connect.xml │ │ │ │ │ └── dk_web_door_history_item_background.xml │ │ │ │ ├── layout/ │ │ │ │ │ ├── dk_app_toast.xml │ │ │ │ │ ├── dk_brvah_quick_view_load_more.xml │ │ │ │ │ ├── dk_dialog_common.xml │ │ │ │ │ ├── dk_dialog_confirm.xml │ │ │ │ │ ├── dk_dialog_file_explorer_choose.xml │ │ │ │ │ ├── dk_dialog_tip.xml │ │ │ │ │ ├── dk_dialog_userinfo.xml │ │ │ │ │ ├── dk_dropdownmenu_tab_item.xml │ │ │ │ │ ├── dk_float_align_ruler_info.xml │ │ │ │ │ ├── dk_float_align_ruler_line.xml │ │ │ │ │ ├── dk_float_align_ruler_marker.xml │ │ │ │ │ ├── dk_float_color_picker.xml │ │ │ │ │ ├── dk_float_color_picker_info.xml │ │ │ │ │ ├── dk_float_count_down.xml │ │ │ │ │ ├── dk_float_h5_info.xml │ │ │ │ │ ├── dk_float_layout_level.xml │ │ │ │ │ ├── dk_float_lbs_route.xml │ │ │ │ │ ├── dk_float_log_info.xml │ │ │ │ │ ├── dk_float_network.xml │ │ │ │ │ ├── dk_float_perform_data.xml │ │ │ │ │ ├── dk_float_time_counter.xml │ │ │ │ │ ├── dk_float_ui_performance_display.xml │ │ │ │ │ ├── dk_float_ui_performance_info.xml │ │ │ │ │ ├── dk_float_view_check.xml │ │ │ │ │ ├── dk_float_view_check_draw.xml │ │ │ │ │ ├── dk_float_view_check_info.xml │ │ │ │ │ ├── dk_fragment_align_ruler_setting.xml │ │ │ │ │ ├── dk_fragment_app_start_info.xml │ │ │ │ │ ├── dk_fragment_block_list.xml │ │ │ │ │ ├── dk_fragment_block_monitor_index.xml │ │ │ │ │ ├── dk_fragment_color_picker_setting.xml │ │ │ │ │ ├── dk_fragment_comm_webview.xml │ │ │ │ │ ├── dk_fragment_cpu_cache_log.xml │ │ │ │ │ ├── dk_fragment_crash_capture_detail.xml │ │ │ │ │ ├── dk_fragment_crash_capture_main.xml │ │ │ │ │ ├── dk_fragment_crash_detail_info.xml │ │ │ │ │ ├── dk_fragment_data_clean.xml │ │ │ │ │ ├── dk_fragment_db_debug.xml │ │ │ │ │ ├── dk_fragment_db_detail.xml │ │ │ │ │ ├── dk_fragment_dokit_connect.xml │ │ │ │ │ ├── dk_fragment_dokit_for_web.xml │ │ │ │ │ ├── dk_fragment_file_explorer.xml │ │ │ │ │ ├── dk_fragment_file_manager_doc.xml │ │ │ │ │ ├── dk_fragment_health.xml │ │ │ │ │ ├── dk_fragment_health_child0.xml │ │ │ │ │ ├── dk_fragment_health_child1.xml │ │ │ │ │ ├── dk_fragment_image_detail.xml │ │ │ │ │ ├── dk_fragment_kit_manager.xml │ │ │ │ │ ├── dk_fragment_large_img_list.xml │ │ │ │ │ ├── dk_fragment_layout_border_setting.xml │ │ │ │ │ ├── dk_fragment_log_info_setting.xml │ │ │ │ │ ├── dk_fragment_method_cost.xml │ │ │ │ │ ├── dk_fragment_mock_template_preview.xml │ │ │ │ │ ├── dk_fragment_monitor_data_upload_page.xml │ │ │ │ │ ├── dk_fragment_monitor_pagedata.xml │ │ │ │ │ ├── dk_fragment_monitor_pagedata_item.xml │ │ │ │ │ ├── dk_fragment_monitor_pagedata_item_item.xml │ │ │ │ │ ├── dk_fragment_more.xml │ │ │ │ │ ├── dk_fragment_net_main_pager.xml │ │ │ │ │ ├── dk_fragment_net_mock.xml │ │ │ │ │ ├── dk_fragment_net_monitor.xml │ │ │ │ │ ├── dk_fragment_network_monitor_detail.xml │ │ │ │ │ ├── dk_fragment_network_monitor_list.xml │ │ │ │ │ ├── dk_fragment_network_summary_page.xml │ │ │ │ │ ├── dk_fragment_parameter.xml │ │ │ │ │ ├── dk_fragment_performance_large_picture_setting.xml │ │ │ │ │ ├── dk_fragment_simple_dokit_page.xml │ │ │ │ │ ├── dk_fragment_sp_show.xml │ │ │ │ │ ├── dk_fragment_sys_info.xml │ │ │ │ │ ├── dk_fragment_text_detail.xml │ │ │ │ │ ├── dk_fragment_third_lib_info.xml │ │ │ │ │ ├── dk_fragment_time_counter_index.xml │ │ │ │ │ ├── dk_fragment_time_counter_list.xml │ │ │ │ │ ├── dk_fragment_top_activity.xml │ │ │ │ │ ├── dk_fragment_video_play.xml │ │ │ │ │ ├── dk_fragment_view_check.xml │ │ │ │ │ ├── dk_fragment_weak_network.xml │ │ │ │ │ ├── dk_fragment_web_door.xml │ │ │ │ │ ├── dk_fragment_web_door_default.xml │ │ │ │ │ ├── dk_home_title_bar.xml │ │ │ │ │ ├── dk_item_bar_chart.xml │ │ │ │ │ ├── dk_item_block_list.xml │ │ │ │ │ ├── dk_item_connect_address.xml │ │ │ │ │ ├── dk_item_crash_history.xml │ │ │ │ │ ├── dk_item_data_clean.xml │ │ │ │ │ ├── dk_item_default_drop_down.xml │ │ │ │ │ ├── dk_item_file_info.xml │ │ │ │ │ ├── dk_item_group_exit.xml │ │ │ │ │ ├── dk_item_group_kit.xml │ │ │ │ │ ├── dk_item_group_kit_manager.xml │ │ │ │ │ ├── dk_item_group_mode.xml │ │ │ │ │ ├── dk_item_group_title.xml │ │ │ │ │ ├── dk_item_group_version.xml │ │ │ │ │ ├── dk_item_kit.xml │ │ │ │ │ ├── dk_item_large_img_list.xml │ │ │ │ │ ├── dk_item_layout_bottom_up_select_window.xml │ │ │ │ │ ├── dk_item_localstorage.xml │ │ │ │ │ ├── dk_item_log.xml │ │ │ │ │ ├── dk_item_more_content.xml │ │ │ │ │ ├── dk_item_more_header.xml │ │ │ │ │ ├── dk_item_network_list.xml │ │ │ │ │ ├── dk_item_performance_close.xml │ │ │ │ │ ├── dk_item_performance_detail.xml │ │ │ │ │ ├── dk_item_performance_wrap.xml │ │ │ │ │ ├── dk_item_setting.xml │ │ │ │ │ ├── dk_item_sp_input.xml │ │ │ │ │ ├── dk_item_sys_info.xml │ │ │ │ │ ├── dk_item_sys_title.xml │ │ │ │ │ ├── dk_item_text_content.xml │ │ │ │ │ ├── dk_item_third_lib_info.xml │ │ │ │ │ ├── dk_item_time_counter_list.xml │ │ │ │ │ ├── dk_item_tips_view.xml │ │ │ │ │ ├── dk_item_web_door_history.xml │ │ │ │ │ ├── dk_item_white_host.xml │ │ │ │ │ ├── dk_jsonviewer_layout_item_view.xml │ │ │ │ │ ├── dk_label_text_view.xml │ │ │ │ │ ├── dk_layout_localstorage_empty.xml │ │ │ │ │ ├── dk_layout_sessionstorage_empty.xml │ │ │ │ │ ├── dk_light_hint_layout.xml │ │ │ │ │ ├── dk_log_title_bar.xml │ │ │ │ │ ├── dk_main_launch_icon.xml │ │ │ │ │ ├── dk_mock_intercept_content_item.xml │ │ │ │ │ ├── dk_mock_template_content_item.xml │ │ │ │ │ ├── dk_mock_title_item.xml │ │ │ │ │ ├── dk_performance_close_wrap.xml │ │ │ │ │ ├── dk_performance_wrap.xml │ │ │ │ │ ├── dk_radio_button.xml │ │ │ │ │ ├── dk_radio_table_layout.xml │ │ │ │ │ ├── dk_radio_table_row.xml │ │ │ │ │ ├── dk_refresh_default_load_more.xml │ │ │ │ │ ├── dk_refresh_default_refresh_header.xml │ │ │ │ │ ├── dk_rv_empty_layout.xml │ │ │ │ │ ├── dk_rv_empty_layout2.xml │ │ │ │ │ ├── dk_title_bar.xml │ │ │ │ │ ├── dk_tool_panel.xml │ │ │ │ │ ├── dk_tool_panel_old.xml │ │ │ │ │ ├── dk_video_layout.xml │ │ │ │ │ ├── dk_view_line_chart.xml │ │ │ │ │ ├── dk_view_network_detail_pager_title.xml │ │ │ │ │ ├── dk_view_network_request.xml │ │ │ │ │ ├── dk_view_network_tab_layout.xml │ │ │ │ │ ├── dk_volume_hint_layout.xml │ │ │ │ │ ├── dk_zxing_activity_scanner.xml │ │ │ │ │ ├── kd_item_sp_input.xml │ │ │ │ │ └── layout_mock_pos_adjust.xml │ │ │ │ ├── values/ │ │ │ │ │ ├── attrs.xml │ │ │ │ │ ├── colors.xml │ │ │ │ │ ├── dimens.xml │ │ │ │ │ ├── font_sizes.xml │ │ │ │ │ ├── ids.xml │ │ │ │ │ ├── strings.xml │ │ │ │ │ └── styles.xml │ │ │ │ ├── values-en-rUS/ │ │ │ │ │ └── strings.xml │ │ │ │ ├── values-zh-rCN/ │ │ │ │ │ └── strings.xml │ │ │ │ ├── values-zh-rTW/ │ │ │ │ │ └── strings.xml │ │ │ │ └── xml/ │ │ │ │ ├── dokit_debug_provider_paths.xml │ │ │ │ └── dokit_network_config.xml │ │ │ └── test/ │ │ │ └── java/ │ │ │ └── com/ │ │ │ └── didichuxing/ │ │ │ └── doraemonkit/ │ │ │ ├── DokitTest.java │ │ │ └── kit/ │ │ │ └── connect/ │ │ │ ├── ByteParserTest.kt │ │ │ ├── WebSocketClientTest.kt │ │ │ └── WebSocketSessionTest.java │ │ └── upload.sh │ ├── dokit-autotest/ │ │ ├── .gitignore │ │ ├── build.gradle │ │ ├── gradle.properties │ │ ├── proguard-rules.pro │ │ ├── src/ │ │ │ ├── main/ │ │ │ │ ├── AndroidManifest.xml │ │ │ │ ├── java/ │ │ │ │ │ └── com/ │ │ │ │ │ └── didichuxing/ │ │ │ │ │ └── doraemonkit/ │ │ │ │ │ └── kit/ │ │ │ │ │ └── autotest/ │ │ │ │ │ ├── AutoTestControlKit.kt │ │ │ │ │ ├── AutoTestManager.kt │ │ │ │ │ ├── AutoTestMessage.kt │ │ │ │ │ ├── AutoTestState.kt │ │ │ │ │ ├── DelayHandler.kt │ │ │ │ │ └── ui/ │ │ │ │ │ ├── AutotestPage.java │ │ │ │ │ ├── DoKitAutotestActivity.java │ │ │ │ │ ├── DoKitAutotestConnectFragment.kt │ │ │ │ │ ├── DoKitAutotestFragment.kt │ │ │ │ │ └── RecordingCaseDoKitView.kt │ │ │ │ └── res/ │ │ │ │ ├── drawable/ │ │ │ │ │ ├── dk_autotest_dialog_bg.xml │ │ │ │ │ ├── dk_autotest_flash_blue_bg.xml │ │ │ │ │ ├── dk_autotest_flash_green_bg.xml │ │ │ │ │ ├── dk_autotest_flash_red_bg.xml │ │ │ │ │ └── dk_btn_autotest_bg.xml │ │ │ │ ├── layout/ │ │ │ │ │ ├── dk_activity_autotest.xml │ │ │ │ │ ├── dk_autotest_view_recording_case.xml │ │ │ │ │ ├── dk_fragment_autotest_connect.xml │ │ │ │ │ └── dk_fragment_autotest_main.xml │ │ │ │ ├── values/ │ │ │ │ │ └── strings.xml │ │ │ │ ├── values-en-rCN/ │ │ │ │ │ └── strings.xml │ │ │ │ ├── values-zh-rTW/ │ │ │ │ │ └── strings.xml │ │ │ │ └── values-zh-rUS/ │ │ │ │ └── strings.xml │ │ │ └── test/ │ │ │ └── java/ │ │ │ └── com/ │ │ │ └── didichuxing/ │ │ │ └── doraemonkit/ │ │ │ └── kit/ │ │ │ └── autotest/ │ │ │ └── ExampleUnitTest.java │ │ └── upload.sh │ ├── dokit-ft/ │ │ ├── .gitignore │ │ ├── build.gradle │ │ ├── gradle.properties │ │ ├── proguard-rules.pro │ │ ├── src/ │ │ │ └── main/ │ │ │ ├── AndroidManifest.xml │ │ │ ├── java/ │ │ │ │ └── com/ │ │ │ │ └── didichuxing/ │ │ │ │ └── doraemonkit/ │ │ │ │ └── kit/ │ │ │ │ └── filemanager/ │ │ │ │ ├── DokitFileRouter.kt │ │ │ │ ├── FileManagerUtil.kt │ │ │ │ ├── FileTransferFragment.kt │ │ │ │ ├── FileTransferKit.kt │ │ │ │ ├── HttpServer.kt │ │ │ │ ├── ability/ │ │ │ │ │ ├── DokitFtAbility.kt │ │ │ │ │ └── DokitFtModuleProcessor.kt │ │ │ │ ├── action/ │ │ │ │ │ ├── RequestErrorAction.kt │ │ │ │ │ ├── file/ │ │ │ │ │ │ ├── CreateFolderAction.kt │ │ │ │ │ │ ├── DeleteFileAction.kt │ │ │ │ │ │ ├── DeviceInfoAction.kt │ │ │ │ │ │ ├── FileDetailAction.kt │ │ │ │ │ │ ├── FileListAction.kt │ │ │ │ │ │ ├── IndexAction.kt │ │ │ │ │ │ ├── RenameFileAction.kt │ │ │ │ │ │ ├── SaveFileAction.kt │ │ │ │ │ │ └── UploadFileAction.kt │ │ │ │ │ └── sql/ │ │ │ │ │ └── DatabaseAction.kt │ │ │ │ ├── bean/ │ │ │ │ │ ├── DirInfo.kt │ │ │ │ │ ├── RenameFileInfo.kt │ │ │ │ │ └── SaveFileInfo.kt │ │ │ │ ├── convert/ │ │ │ │ │ └── GsonConverter.kt │ │ │ │ └── sqlite/ │ │ │ │ ├── DBManager.kt │ │ │ │ ├── bean/ │ │ │ │ │ ├── RowFiledInfo.kt │ │ │ │ │ ├── RowRequestInfo.kt │ │ │ │ │ └── TableFieldInfo.kt │ │ │ │ ├── dao/ │ │ │ │ │ ├── EncryptSQLiteDB.kt │ │ │ │ │ ├── NormalSQLiteDB.kt │ │ │ │ │ └── SQLiteDB.kt │ │ │ │ ├── factory/ │ │ │ │ │ ├── DBFactory.kt │ │ │ │ │ ├── EncryptDBFactory.kt │ │ │ │ │ └── NormalDBFactory.kt │ │ │ │ └── util/ │ │ │ │ └── DBUtil.kt │ │ │ └── res/ │ │ │ └── layout/ │ │ │ └── dk_fragment_file_transfer.xml │ │ └── upload.sh │ ├── dokit-gps-mock/ │ │ ├── .gitignore │ │ ├── build.gradle │ │ ├── consumer-rules.pro │ │ ├── gradle.properties │ │ ├── libs/ │ │ │ └── BaiduLBS_Android.jar │ │ ├── proguard-rules.pro │ │ └── src/ │ │ ├── androidTest/ │ │ │ └── java/ │ │ │ └── com/ │ │ │ └── didichuxing/ │ │ │ └── doraemonkit/ │ │ │ └── ExampleInstrumentedTest.java │ │ ├── main/ │ │ │ ├── AndroidManifest.xml │ │ │ ├── java/ │ │ │ │ └── com/ │ │ │ │ └── didichuxing/ │ │ │ │ └── doraemonkit/ │ │ │ │ └── gps_mock/ │ │ │ │ ├── ability/ │ │ │ │ │ ├── DokitFtAbility.kt │ │ │ │ │ └── DokitFtModuleProcessor.kt │ │ │ │ ├── common/ │ │ │ │ │ ├── BdMapRouteData.java │ │ │ │ │ └── Utils.java │ │ │ │ ├── gpsmock/ │ │ │ │ │ ├── BaseServiceHooker.kt │ │ │ │ │ ├── BinderHookHandler.java │ │ │ │ │ ├── CordTransformUtil.java │ │ │ │ │ ├── GpsMockFragment.java │ │ │ │ │ ├── GpsMockKit.kt │ │ │ │ │ ├── GpsMockManager.java │ │ │ │ │ ├── GpsMockProxyManager.kt │ │ │ │ │ ├── LocationBuilder.java │ │ │ │ │ ├── LocationHookHandler.java │ │ │ │ │ ├── LocationHooker.java │ │ │ │ │ ├── MethodHandler.kt │ │ │ │ │ ├── RouteMockThread.java │ │ │ │ │ ├── ServiceHookManager.kt │ │ │ │ │ ├── TelephonyHooker.java │ │ │ │ │ ├── TencentLocationImp.java │ │ │ │ │ └── WifiHooker.java │ │ │ │ ├── lbs/ │ │ │ │ │ ├── common/ │ │ │ │ │ │ ├── AMapDrivingRouteOverLay.java │ │ │ │ │ │ ├── AMapRouteOverlay.java │ │ │ │ │ │ ├── AMapUtil.kt │ │ │ │ │ │ ├── Constants.java │ │ │ │ │ │ └── LocInfo.java │ │ │ │ │ ├── manual/ │ │ │ │ │ │ ├── FloatGpsMockCache.java │ │ │ │ │ │ ├── GPSTools.java │ │ │ │ │ │ ├── PosAdjustKit.kt │ │ │ │ │ │ └── PosAdjustKitView.java │ │ │ │ │ ├── preset/ │ │ │ │ │ │ ├── FloatGpsPresetMockCache.java │ │ │ │ │ │ └── MockLocList.java │ │ │ │ │ └── route/ │ │ │ │ │ ├── AMapRealNavMockView.kt │ │ │ │ │ ├── FloatGpsRouteMockCache.java │ │ │ │ │ ├── NaviSettings.kt │ │ │ │ │ └── RealNavMockKit.kt │ │ │ │ ├── location/ │ │ │ │ │ └── GpsStatusUtil.java │ │ │ │ ├── map/ │ │ │ │ │ ├── AMapLocationChangedListenerProxy.java │ │ │ │ │ ├── AMapLocationClientProxy.java │ │ │ │ │ ├── AMapLocationListenerProxy.java │ │ │ │ │ ├── AMapLocationSourceProxy.java │ │ │ │ │ ├── AMapNaviListenerProxy.java │ │ │ │ │ ├── AMapNaviListenerProxyDelegate.java │ │ │ │ │ ├── AMapNaviListenerProxyNoOp.java │ │ │ │ │ ├── BDAbsLocationListenerProxy.java │ │ │ │ │ ├── BDLocationListenerProxy.java │ │ │ │ │ ├── BDLocationUtil.java │ │ │ │ │ ├── DMapLocationListener.kt │ │ │ │ │ ├── TencentLocationListenerProxy.java │ │ │ │ │ └── ThirdMapLocationListenerUtil.java │ │ │ │ ├── sysservicehook/ │ │ │ │ │ ├── ActivityMangerHooker.kt │ │ │ │ │ ├── ActivityTaskMangerHooker.kt │ │ │ │ │ ├── GetInstalledApplicationsMethodHandler.kt │ │ │ │ │ ├── PackageManagerHooker.kt │ │ │ │ │ └── StartActivityMethodHandler.kt │ │ │ │ └── widget/ │ │ │ │ ├── CustomDialogFragment.java │ │ │ │ ├── DrivingRouteOverlay.java │ │ │ │ ├── IDialogHelper.java │ │ │ │ ├── OverlayManager.java │ │ │ │ ├── PositionSelectDialogHelper.java │ │ │ │ ├── PositionSelectRecyclerAdapter.java │ │ │ │ ├── RouteMockDokitView.kt │ │ │ │ └── SeekRangeBar.java │ │ │ └── res/ │ │ │ ├── drawable/ │ │ │ │ ├── dk_bg_btn_round_rectangle.xml │ │ │ │ ├── dk_bg_custom_spinner.xml │ │ │ │ ├── dk_bg_dokitview_route_mock.xml │ │ │ │ ├── dk_bg_edt_hint.xml │ │ │ │ ├── dk_bg_gray_round_rectangle.xml │ │ │ │ ├── dk_bg_round_edt.xml │ │ │ │ ├── dk_icon_loc_circle_shape.xml │ │ │ │ ├── dk_seek_bar_background_shape.xml │ │ │ │ ├── dk_seek_bar_circle_indicator.xml │ │ │ │ └── dk_seek_bar_foreground_shape.xml │ │ │ ├── layout/ │ │ │ │ ├── dk_dokitview_route_mock.xml │ │ │ │ ├── dk_fragment_gps_mock.xml │ │ │ │ ├── dk_item_position_select.xml │ │ │ │ └── dk_position_select_dialog_fragment.xml │ │ │ ├── values/ │ │ │ │ ├── attrs.xml │ │ │ │ ├── colors.xml │ │ │ │ ├── dimens.xml │ │ │ │ └── strings.xml │ │ │ ├── values-en-rCN/ │ │ │ │ └── strings.xml │ │ │ ├── values-zh-rTW/ │ │ │ │ └── strings.xml │ │ │ ├── values-zh-rUS/ │ │ │ │ └── strings.xml │ │ │ └── xml/ │ │ │ └── gps_mock_root_scene.xml │ │ └── test/ │ │ └── java/ │ │ └── com/ │ │ └── didichuxing/ │ │ └── doraemonkit/ │ │ └── ExampleUnitTest.java │ ├── dokit-leakcanary/ │ │ ├── .gitignore │ │ ├── build.gradle │ │ ├── gradle.properties │ │ ├── proguard-rules.pro │ │ ├── src/ │ │ │ └── main/ │ │ │ ├── AndroidManifest.xml │ │ │ ├── aidl/ │ │ │ │ └── com/ │ │ │ │ └── didichuxing/ │ │ │ │ └── doraemonkit/ │ │ │ │ └── aidl/ │ │ │ │ ├── IReceiverAidlInterface.aidl │ │ │ │ └── ISenderAidlInterface.aidl │ │ │ ├── java/ │ │ │ │ └── com/ │ │ │ │ ├── didichuxing/ │ │ │ │ │ └── doraemonkit/ │ │ │ │ │ ├── LeakCanaryManager.java │ │ │ │ │ ├── abridge/ │ │ │ │ │ │ ├── AbridgeCallBack.java │ │ │ │ │ │ ├── AbridgeManager.java │ │ │ │ │ │ ├── AbridgeMessengerCallBack.java │ │ │ │ │ │ ├── AbridgeMessengerManager.java │ │ │ │ │ │ ├── IBridge.java │ │ │ │ │ │ └── service/ │ │ │ │ │ │ ├── ABridgeService.java │ │ │ │ │ │ └── MessengerService.java │ │ │ │ │ └── kit/ │ │ │ │ │ └── leakcanary/ │ │ │ │ │ └── LeakCanaryKit.java │ │ │ │ └── squareup/ │ │ │ │ ├── haha/ │ │ │ │ │ └── perflib/ │ │ │ │ │ └── HahaSpy.java │ │ │ │ └── leakcanary/ │ │ │ │ ├── AbstractAnalysisResultService.java │ │ │ │ ├── ActivityRefWatcher.java │ │ │ │ ├── AnalysisResult.java │ │ │ │ ├── AnalyzedHeap.java │ │ │ │ ├── AnalyzerProgressListener.java │ │ │ │ ├── AndroidDebuggerControl.java │ │ │ │ ├── AndroidExcludedRefs.java │ │ │ │ ├── AndroidHeapDumper.java │ │ │ │ ├── AndroidReachabilityInspectors.java │ │ │ │ ├── AndroidRefWatcherBuilder.java │ │ │ │ ├── AndroidWatchExecutor.java │ │ │ │ ├── CanaryLog.java │ │ │ │ ├── DebuggerControl.java │ │ │ │ ├── DefaultLeakDirectoryProvider.java │ │ │ │ ├── DisplayLeakService.java │ │ │ │ ├── ExcludedRefs.java │ │ │ │ ├── Exclusion.java │ │ │ │ ├── FailTestOnLeakRunListener.java │ │ │ │ ├── GcTrigger.java │ │ │ │ ├── HahaHelper.java │ │ │ │ ├── HeapAnalyzer.java │ │ │ │ ├── HeapDump.java │ │ │ │ ├── HeapDumper.java │ │ │ │ ├── InstrumentationLeakDetector.java │ │ │ │ ├── InstrumentationLeakResults.java │ │ │ │ ├── KeyedWeakReference.java │ │ │ │ ├── LeakCanary.java │ │ │ │ ├── LeakDirectoryProvider.java │ │ │ │ ├── LeakNode.java │ │ │ │ ├── LeakReference.java │ │ │ │ ├── LeakTrace.java │ │ │ │ ├── LeakTraceElement.java │ │ │ │ ├── Preconditions.java │ │ │ │ ├── Reachability.java │ │ │ │ ├── RefWatcher.java │ │ │ │ ├── RefWatcherBuilder.java │ │ │ │ ├── Retryable.java │ │ │ │ ├── ServiceHeapDumpListener.java │ │ │ │ ├── ShortestPathFinder.java │ │ │ │ ├── TrackedReference.java │ │ │ │ ├── UploadLeakService.java │ │ │ │ ├── WatchExecutor.java │ │ │ │ └── internal/ │ │ │ │ ├── ActivityLifecycleCallbacksAdapter.java │ │ │ │ ├── AndroidOFragmentRefWatcher.java │ │ │ │ ├── DisplayLeakActivity.java │ │ │ │ ├── DisplayLeakAdapter.java │ │ │ │ ├── DisplayLeakConnectorView.java │ │ │ │ ├── ForegroundService.java │ │ │ │ ├── FragmentRefWatcher.java │ │ │ │ ├── FutureResult.java │ │ │ │ ├── HeapAnalyzerService.java │ │ │ │ ├── LeakCanaryFileProvider.java │ │ │ │ ├── LeakCanaryInternals.java │ │ │ │ ├── LeakCanarySingleThreadFactory.java │ │ │ │ ├── MoreDetailsView.java │ │ │ │ ├── RequestStoragePermissionActivity.java │ │ │ │ ├── RowElementLayout.java │ │ │ │ ├── SquigglySpan.java │ │ │ │ └── SupportFragmentRefWatcher.java │ │ │ └── res/ │ │ │ ├── drawable/ │ │ │ │ ├── leak_canary_icon_foreground.xml │ │ │ │ └── leak_canary_toast_background.xml │ │ │ ├── layout/ │ │ │ │ ├── leak_canary_display_leak.xml │ │ │ │ ├── leak_canary_heap_dump_toast.xml │ │ │ │ ├── leak_canary_leak_row.xml │ │ │ │ ├── leak_canary_ref_row.xml │ │ │ │ └── leak_canary_ref_top_row.xml │ │ │ ├── values/ │ │ │ │ ├── leak_canary_attrs.xml │ │ │ │ ├── leak_canary_colors.xml │ │ │ │ ├── leak_canary_dimens.xml │ │ │ │ ├── leak_canary_icon_background.xml │ │ │ │ ├── leak_canary_public.xml │ │ │ │ ├── leak_canary_strings.xml │ │ │ │ ├── leak_canary_themes.xml │ │ │ │ └── strings.xml │ │ │ └── xml/ │ │ │ └── leak_canary_file_paths.xml │ │ └── upload.sh │ ├── dokit-mc/ │ │ ├── .gitignore │ │ ├── build.gradle │ │ ├── gradle.properties │ │ ├── proguard-rules.pro │ │ ├── src/ │ │ │ ├── main/ │ │ │ │ ├── AndroidManifest.xml │ │ │ │ ├── java/ │ │ │ │ │ └── com/ │ │ │ │ │ └── didichuxing/ │ │ │ │ │ └── doraemonkit/ │ │ │ │ │ └── kit/ │ │ │ │ │ └── mc/ │ │ │ │ │ ├── AbstractMultiController.kt │ │ │ │ │ ├── ClientMultiController.kt │ │ │ │ │ ├── DelayHandler.kt │ │ │ │ │ ├── HostMultiController.kt │ │ │ │ │ ├── MultiControlConfig.kt │ │ │ │ │ ├── MultiControlKit.kt │ │ │ │ │ ├── MultiControlKitTest.kt │ │ │ │ │ ├── MultiControlManager.kt │ │ │ │ │ ├── OnMultiControlModeChangeListener.kt │ │ │ │ │ ├── ability/ │ │ │ │ │ │ ├── DoKitMcAbility.kt │ │ │ │ │ │ └── DoKitMcModuleProcessor.kt │ │ │ │ │ ├── net/ │ │ │ │ │ │ ├── ConnectMode.kt │ │ │ │ │ │ ├── DoKitMcClient.kt │ │ │ │ │ │ ├── DoKitMcHostServer.kt │ │ │ │ │ │ ├── DokitMcConnectManager.kt │ │ │ │ │ │ ├── WSEventProcessor.kt │ │ │ │ │ │ ├── WSSRouter.kt │ │ │ │ │ │ └── WSServerProcessor.kt │ │ │ │ │ ├── oldui/ │ │ │ │ │ │ ├── DoKitMcManager.kt │ │ │ │ │ │ ├── client/ │ │ │ │ │ │ │ ├── ClientDoKitView.kt │ │ │ │ │ │ │ ├── DoKitMcClientFragment.kt │ │ │ │ │ │ │ └── DoKitMcClientHistoryFragment.kt │ │ │ │ │ │ ├── host/ │ │ │ │ │ │ │ ├── DoKitMcHostFragment.kt │ │ │ │ │ │ │ └── HostDoKitView.kt │ │ │ │ │ │ ├── main/ │ │ │ │ │ │ │ └── DoKitMcMainFragment.kt │ │ │ │ │ │ └── record/ │ │ │ │ │ │ ├── DoKitMcDatasFragment.kt │ │ │ │ │ │ └── RecordingDoKitView.kt │ │ │ │ │ ├── report/ │ │ │ │ │ │ ├── MCRecordManager.kt │ │ │ │ │ │ ├── RecordActionCase.kt │ │ │ │ │ │ ├── RecordActionStep.kt │ │ │ │ │ │ ├── RecordData.kt │ │ │ │ │ │ └── ScreenShotManager.java │ │ │ │ │ ├── ui/ │ │ │ │ │ │ ├── BorderDoKitView.java │ │ │ │ │ │ ├── DoKitMcActivity.kt │ │ │ │ │ │ ├── DoKitMcScanActivity.kt │ │ │ │ │ │ ├── McDialogDoKitView.kt │ │ │ │ │ │ ├── McPages.kt │ │ │ │ │ │ ├── adapter/ │ │ │ │ │ │ │ ├── McCaseInfoDialogProvider.kt │ │ │ │ │ │ │ ├── McCaseListAdapter.kt │ │ │ │ │ │ │ └── McClientHistoryAdapter.kt │ │ │ │ │ │ └── connect/ │ │ │ │ │ │ ├── MultiControlAllFragment.kt │ │ │ │ │ │ └── MultiControlDoKitView.kt │ │ │ │ │ └── utils/ │ │ │ │ │ ├── ActivityStatusUtil.kt │ │ │ │ │ ├── ClientHistoryUtils.kt │ │ │ │ │ ├── CodeUtils.kt │ │ │ │ │ ├── ConnectHistoryUtils.kt │ │ │ │ │ ├── DensityUtils.kt │ │ │ │ │ ├── McCaseUtils.kt │ │ │ │ │ ├── McPageUtils.kt │ │ │ │ │ └── WSPackageUtils.kt │ │ │ │ └── res/ │ │ │ │ ├── drawable/ │ │ │ │ │ ├── dk_btn_mc_bg.xml │ │ │ │ │ ├── dk_dokitview_mc.xml │ │ │ │ │ ├── dk_dokitview_mc_connect.xml │ │ │ │ │ └── dk_dokitview_mc_recoding.xml │ │ │ │ ├── layout/ │ │ │ │ │ ├── dk_activity_mc.xml │ │ │ │ │ ├── dk_dialog_mc_case_info.xml │ │ │ │ │ ├── dk_dokitview_client.xml │ │ │ │ │ ├── dk_dokitview_connect.xml │ │ │ │ │ ├── dk_dokitview_dialog.xml │ │ │ │ │ ├── dk_dokitview_host.xml │ │ │ │ │ ├── dk_dokitview_recording.xml │ │ │ │ │ ├── dk_fragment_mc_client.xml │ │ │ │ │ ├── dk_fragment_mc_client_history.xml │ │ │ │ │ ├── dk_fragment_mc_connect.xml │ │ │ │ │ ├── dk_fragment_mc_connect_history.xml │ │ │ │ │ ├── dk_fragment_mc_datas.xml │ │ │ │ │ ├── dk_fragment_mc_host.xml │ │ │ │ │ ├── dk_fragment_mc_select.xml │ │ │ │ │ ├── dk_item_mc_case.xml │ │ │ │ │ └── dk_item_mc_client.xml │ │ │ │ ├── values/ │ │ │ │ │ └── strings.xml │ │ │ │ ├── values-en-rCN/ │ │ │ │ │ └── strings.xml │ │ │ │ ├── values-zh-rTW/ │ │ │ │ │ └── strings.xml │ │ │ │ ├── values-zh-rUS/ │ │ │ │ │ └── strings.xml │ │ │ │ └── xml/ │ │ │ │ └── mc_accessibity_config.xml │ │ │ └── test/ │ │ │ └── java/ │ │ │ └── com/ │ │ │ └── didichuxing/ │ │ │ └── doraemonkit/ │ │ │ └── ExampleUnitTest.kt │ │ └── upload.sh │ ├── dokit-no-op/ │ │ ├── .gitignore │ │ ├── build.gradle │ │ ├── gradle.properties │ │ ├── proguard-rules.pro │ │ ├── src/ │ │ │ └── main/ │ │ │ ├── AndroidManifest.xml │ │ │ └── java/ │ │ │ └── com/ │ │ │ └── didichuxing/ │ │ │ └── doraemonkit/ │ │ │ ├── DoKit.kt │ │ │ ├── DoKitCallBack.kt │ │ │ ├── DoKitMCHummerHelper.java │ │ │ ├── DoraemonKit.kt │ │ │ ├── kit/ │ │ │ │ ├── AbstractKit.kt │ │ │ │ ├── Category.java │ │ │ │ ├── IKit.kt │ │ │ │ ├── core/ │ │ │ │ │ ├── AbsDokitFragment.kt │ │ │ │ │ ├── AbsDokitView.kt │ │ │ │ │ ├── BaseFragment.kt │ │ │ │ │ ├── DokitFrameLayout.java │ │ │ │ │ ├── DokitIntent.kt │ │ │ │ │ ├── DokitView.java │ │ │ │ │ ├── DokitViewInterface.java │ │ │ │ │ ├── DokitViewLayoutParams.java │ │ │ │ │ ├── DokitViewManager.kt │ │ │ │ │ ├── DokitViewManagerInterface.kt │ │ │ │ │ ├── McClientProcessor.kt │ │ │ │ │ └── TouchProxy.java │ │ │ │ ├── network/ │ │ │ │ │ ├── bean/ │ │ │ │ │ │ ├── NetworkRecord.java │ │ │ │ │ │ ├── Request.java │ │ │ │ │ │ └── Response.java │ │ │ │ │ └── okhttp/ │ │ │ │ │ └── interceptor/ │ │ │ │ │ └── DokitExtInterceptor.kt │ │ │ │ ├── performance/ │ │ │ │ │ └── PerformanceValueListener.kt │ │ │ │ ├── test/ │ │ │ │ │ ├── DoKitTestManager.kt │ │ │ │ │ └── TestMode.kt │ │ │ │ └── webdoor/ │ │ │ │ └── WebDoorManager.java │ │ │ └── tcp/ │ │ │ └── ability/ │ │ │ ├── MessageReceiverHook.java │ │ │ ├── MessageSenderHook.java │ │ │ └── TcpMessageHook.java │ │ └── upload.sh │ ├── dokit-okhttp-api/ │ │ ├── .gitignore │ │ ├── build.gradle │ │ ├── gradle.properties │ │ ├── proguard-rules.pro │ │ ├── src/ │ │ │ └── main/ │ │ │ ├── AndroidManifest.xml │ │ │ └── java/ │ │ │ └── com/ │ │ │ └── didichuxing/ │ │ │ └── doraemonkit/ │ │ │ └── okhttp_api/ │ │ │ └── OkHttpWrap.kt │ │ └── upload.sh │ ├── dokit-okhttp-v3/ │ │ ├── .gitignore │ │ ├── build.gradle │ │ ├── gradle.properties │ │ ├── proguard-rules.pro │ │ ├── src/ │ │ │ └── main/ │ │ │ ├── AndroidManifest.xml │ │ │ └── java/ │ │ │ └── com/ │ │ │ └── didichuxing/ │ │ │ └── doraemonkit/ │ │ │ └── okhttp_api/ │ │ │ ├── ByteCountBufferedSinkV3.java │ │ │ └── OkHttpWrapV3.kt │ │ └── upload.sh │ ├── dokit-okhttp-v4/ │ │ ├── .gitignore │ │ ├── build.gradle │ │ ├── gradle.properties │ │ ├── proguard-rules.pro │ │ ├── src/ │ │ │ └── main/ │ │ │ ├── AndroidManifest.xml │ │ │ └── java/ │ │ │ └── com/ │ │ │ └── didichuxing/ │ │ │ └── doraemonkit/ │ │ │ └── okhttp_api/ │ │ │ ├── ByteCountBufferedSinkV4.java │ │ │ └── OkHttpWrapV4.kt │ │ └── upload.sh │ ├── dokit-plugin/ │ │ ├── .gitignore │ │ ├── build.gradle │ │ ├── gradle.properties │ │ ├── src/ │ │ │ └── main/ │ │ │ ├── kotlin/ │ │ │ │ └── com/ │ │ │ │ └── didichuxing/ │ │ │ │ └── doraemonkit/ │ │ │ │ └── plugin/ │ │ │ │ ├── DoKitExt.kt │ │ │ │ ├── DoKitExtUtil.kt │ │ │ │ ├── DoKitPlugin.kt │ │ │ │ ├── DoKitPluginUtil.kt │ │ │ │ ├── DoKitTransformTaskExecutionListener.kt │ │ │ │ ├── extension/ │ │ │ │ │ ├── BigImageExtension.kt │ │ │ │ │ ├── DoKitExtension.kt │ │ │ │ │ ├── GpsExtension.kt │ │ │ │ │ ├── NetworkExtension.kt │ │ │ │ │ ├── SlowMethodExtension.kt │ │ │ │ │ └── WebViewExtension.kt │ │ │ │ ├── processor/ │ │ │ │ │ ├── DoKitComponentHandler.kt │ │ │ │ │ └── DoKitPluginConfigProcessor.kt │ │ │ │ ├── stack_method/ │ │ │ │ │ ├── MethodStackNode.kt │ │ │ │ │ └── MethodStackNodeUtil.kt │ │ │ │ ├── thirdlib/ │ │ │ │ │ ├── ThirdLibInfo.kt │ │ │ │ │ └── ThirdLibVariantProcessor.kt │ │ │ │ └── transform/ │ │ │ │ ├── DoKitBaseTransform.kt │ │ │ │ ├── DoKitCommonTransform.kt │ │ │ │ ├── DoKitCommonTransformV34.kt │ │ │ │ ├── DoKitDependTransform.kt │ │ │ │ ├── DoKitDependTransformV34.kt │ │ │ │ ├── DoKitTransformContext.kt │ │ │ │ ├── DoKitTransformInvocation.kt │ │ │ │ ├── asmtransform/ │ │ │ │ │ ├── BaseDoKitAsmTransformer.kt │ │ │ │ │ └── DoKitAsmTransformer.kt │ │ │ │ └── classtransform/ │ │ │ │ ├── AbsClassTransformer.kt │ │ │ │ ├── BigImgClassTransformer.kt │ │ │ │ ├── CommClassTransformer.kt │ │ │ │ ├── EnterMSClassTransformer.kt │ │ │ │ ├── GPSAMapClassTransformer.kt │ │ │ │ ├── GPSBDClassTransformer.kt │ │ │ │ ├── GPSClassTransformer.kt │ │ │ │ ├── GPSTencentClassTransformer.kt │ │ │ │ ├── GSMClassTransformer.kt │ │ │ │ ├── MSDClassTransformer.kt │ │ │ │ ├── Okhttp3ClassTransformer.kt │ │ │ │ ├── ThirdLibsClassTransformer.kt │ │ │ │ ├── UrlConnectionTransformer.kt │ │ │ │ └── WebViewClassTransformer.kt │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── gradle-plugins/ │ │ │ └── com.didi.dokit.properties │ │ └── upload.sh │ ├── dokit-pthread-hook/ │ │ ├── .gitignore │ │ ├── build.gradle │ │ ├── consumer-rules.pro │ │ ├── gradle.properties │ │ ├── proguard-rules.pro │ │ └── src/ │ │ ├── androidTest/ │ │ │ └── java/ │ │ │ └── com/ │ │ │ └── kronos/ │ │ │ └── dokit/ │ │ │ └── pthread/ │ │ │ └── ExampleInstrumentedTest.kt │ │ ├── main/ │ │ │ ├── AndroidManifest.xml │ │ │ ├── java/ │ │ │ │ └── com/ │ │ │ │ └── kronos/ │ │ │ │ └── dokit/ │ │ │ │ └── pthread/ │ │ │ │ ├── AutoDumpListener.kt │ │ │ │ ├── PThreadDumpHelper.kt │ │ │ │ ├── PThreadEntity.kt │ │ │ │ ├── PThreadKit.kt │ │ │ │ ├── ViewExtensions.kt │ │ │ │ └── ui/ │ │ │ │ ├── PThreadHookUiActivity.kt │ │ │ │ └── ThreadAdapter.kt │ │ │ └── res/ │ │ │ ├── layout/ │ │ │ │ ├── activity_pthread_hook.xml │ │ │ │ └── recycler_view_thread_hook.xml │ │ │ └── values/ │ │ │ └── values.xml │ │ └── test/ │ │ └── java/ │ │ └── com/ │ │ └── kronos/ │ │ └── dokit/ │ │ └── pthread/ │ │ └── ExampleUnitTest.kt │ ├── dokit-test/ │ │ ├── .gitignore │ │ ├── build.gradle │ │ ├── consumer-rules.pro │ │ ├── gradle.properties │ │ ├── proguard-rules.pro │ │ ├── src/ │ │ │ ├── main/ │ │ │ │ ├── AndroidManifest.xml │ │ │ │ └── java/ │ │ │ │ └── com/ │ │ │ │ └── didichuxing/ │ │ │ │ └── doraemonkit/ │ │ │ │ └── kit/ │ │ │ │ └── test/ │ │ │ │ ├── DoKitTestManager.kt │ │ │ │ ├── OnTestModeChangeListener.kt │ │ │ │ ├── TestMode.kt │ │ │ │ ├── event/ │ │ │ │ │ ├── AccessibilityEventNode.kt │ │ │ │ │ ├── ActionType.kt │ │ │ │ │ ├── ControlEvent.kt │ │ │ │ │ ├── ControlEventManager.kt │ │ │ │ │ ├── ControlEventProcessor.kt │ │ │ │ │ ├── DoKitViewNode.kt │ │ │ │ │ ├── DoKitViewPanelNode.kt │ │ │ │ │ ├── EventErrorCode.kt │ │ │ │ │ ├── EventType.kt │ │ │ │ │ ├── OnControlEventActionListener.kt │ │ │ │ │ ├── OnControlEventActionProcessListener.kt │ │ │ │ │ ├── OnControlEventInterceptor.kt │ │ │ │ │ ├── Position.kt │ │ │ │ │ ├── SystemViewNode.kt │ │ │ │ │ ├── ViewC12c.kt │ │ │ │ │ ├── WindowNode.kt │ │ │ │ │ ├── monitor/ │ │ │ │ │ │ ├── AccessibilityEventMonitor.kt │ │ │ │ │ │ ├── CustomEventMonitor.kt │ │ │ │ │ │ ├── LifecycleEventMonitor.kt │ │ │ │ │ │ └── TcpMessageEventMonitor.kt │ │ │ │ │ └── processor/ │ │ │ │ │ ├── AbstractEventProcessor.kt │ │ │ │ │ ├── AccessibilityEventProcessor.kt │ │ │ │ │ ├── CustomEventProcessor.kt │ │ │ │ │ ├── LifecycleEventProcessor.kt │ │ │ │ │ └── TcpMessageEventProcessor.kt │ │ │ │ ├── hook/ │ │ │ │ │ ├── AccessibilityGetInstanceMethodHook.kt │ │ │ │ │ ├── ViewOnClickListenerEventHook.kt │ │ │ │ │ ├── ViewOnClickListenerProxy.kt │ │ │ │ │ └── ViewOnInitializeAccessibilityEventHook.kt │ │ │ │ ├── mock/ │ │ │ │ │ ├── HttpMockInterceptor.kt │ │ │ │ │ ├── MockManager.kt │ │ │ │ │ ├── OnHttpProxyMockDataListener.kt │ │ │ │ │ ├── OnHttpProxyMockSendListener.kt │ │ │ │ │ ├── ProxyMockCallback.kt │ │ │ │ │ ├── data/ │ │ │ │ │ │ ├── AppInfo.kt │ │ │ │ │ │ ├── CaseInfo.kt │ │ │ │ │ │ ├── HostInfo.kt │ │ │ │ │ │ ├── HttpMatchedInfo.kt │ │ │ │ │ │ ├── HttpUploadInfo.kt │ │ │ │ │ │ ├── McCaseInfo.kt │ │ │ │ │ │ ├── McConfigInfo.kt │ │ │ │ │ │ ├── McMockKey.kt │ │ │ │ │ │ └── McResInfo.kt │ │ │ │ │ ├── http/ │ │ │ │ │ │ ├── DoKitMockInterceptor.kt │ │ │ │ │ │ ├── DoKitProxyMockInterceptor.kt │ │ │ │ │ │ └── HttpMockServer.kt │ │ │ │ │ ├── proxy/ │ │ │ │ │ │ ├── ProxyCallback.kt │ │ │ │ │ │ ├── ProxyMockManager.kt │ │ │ │ │ │ ├── ProxyMockUtils.kt │ │ │ │ │ │ ├── ProxyQueryData.java │ │ │ │ │ │ ├── ProxyRequest.kt │ │ │ │ │ │ └── ProxyResponse.kt │ │ │ │ │ └── tcp/ │ │ │ │ │ ├── TcpMockManager.kt │ │ │ │ │ └── TcpMockMessageProcessor.kt │ │ │ │ ├── report/ │ │ │ │ │ ├── AutoTestMessage.kt │ │ │ │ │ ├── AutoTestState.kt │ │ │ │ │ ├── FileUploadManager.kt │ │ │ │ │ ├── MCRecordManager.kt │ │ │ │ │ ├── MyWindowBitmap.kt │ │ │ │ │ ├── RecordActionCase.kt │ │ │ │ │ ├── RecordActionStep.kt │ │ │ │ │ ├── RecordData.kt │ │ │ │ │ └── ScreenShotManager.java │ │ │ │ ├── utils/ │ │ │ │ │ ├── DateTime.kt │ │ │ │ │ ├── RandomIdentityUtil.kt │ │ │ │ │ ├── ReflectHookUtil.java │ │ │ │ │ ├── ViewPathUtil.kt │ │ │ │ │ ├── WindowPathUtil.java │ │ │ │ │ └── XposedHookUtil.kt │ │ │ │ └── widget/ │ │ │ │ ├── FlashImageView.kt │ │ │ │ └── FlashTextView.kt │ │ │ └── test/ │ │ │ └── java/ │ │ │ └── com/ │ │ │ └── didichuxing/ │ │ │ └── doraemonkit/ │ │ │ └── kit/ │ │ │ └── test/ │ │ │ └── ExampleUnitTest.java │ │ └── upload.sh │ ├── dokit-util/ │ │ ├── .gitignore │ │ ├── build.gradle │ │ ├── gradle.properties │ │ ├── proguard-rules.pro │ │ ├── src/ │ │ │ └── main/ │ │ │ ├── AndroidManifest.xml │ │ │ ├── java/ │ │ │ │ └── com/ │ │ │ │ └── didichuxing/ │ │ │ │ └── doraemonkit/ │ │ │ │ ├── constant/ │ │ │ │ │ ├── CacheConstants.java │ │ │ │ │ ├── MemoryConstants.java │ │ │ │ │ ├── PermissionConstants.java │ │ │ │ │ ├── RegexConstants.java │ │ │ │ │ └── TimeConstants.java │ │ │ │ └── util/ │ │ │ │ ├── ActivityUtils.java │ │ │ │ ├── AdaptScreenUtils.java │ │ │ │ ├── ApiUtils.java │ │ │ │ ├── AppStoreUtils.java │ │ │ │ ├── AppUtils.java │ │ │ │ ├── ArrayUtils.java │ │ │ │ ├── BarUtils.java │ │ │ │ ├── BatteryUtils.java │ │ │ │ ├── BitUtils.java │ │ │ │ ├── BrightnessUtils.java │ │ │ │ ├── BusUtils.java │ │ │ │ ├── CacheDiskStaticUtils.java │ │ │ │ ├── CacheDiskUtils.java │ │ │ │ ├── CacheDoubleStaticUtils.java │ │ │ │ ├── CacheDoubleUtils.java │ │ │ │ ├── CacheMemoryStaticUtils.java │ │ │ │ ├── CacheMemoryUtils.java │ │ │ │ ├── CameraUtils.java │ │ │ │ ├── CleanUtils.java │ │ │ │ ├── ClickUtils.java │ │ │ │ ├── ClipboardUtils.java │ │ │ │ ├── CloneUtils.java │ │ │ │ ├── CloseUtils.java │ │ │ │ ├── CollectionUtils.java │ │ │ │ ├── ColorUtils.java │ │ │ │ ├── ConvertUtils.java │ │ │ │ ├── CoordinateUtils.java │ │ │ │ ├── CountryUtils.java │ │ │ │ ├── CrashUtils.java │ │ │ │ ├── DangerousUtils.java │ │ │ │ ├── DebouncingUtils.java │ │ │ │ ├── DeviceUtils.java │ │ │ │ ├── DialogUtils.java │ │ │ │ ├── EncodeUtils.java │ │ │ │ ├── EncryptUtils.java │ │ │ │ ├── FileIOUtils.java │ │ │ │ ├── FileUtils.java │ │ │ │ ├── FlashlightUtils.java │ │ │ │ ├── FragmentUtils.java │ │ │ │ ├── GlideUtils.java │ │ │ │ ├── GsonUtils.java │ │ │ │ ├── HttpsUtil.java │ │ │ │ ├── ImageUtils.java │ │ │ │ ├── IntentUtils.java │ │ │ │ ├── JsonUtils.java │ │ │ │ ├── KeyboardUtils.java │ │ │ │ ├── LanguageUtils.java │ │ │ │ ├── LocationUtils.java │ │ │ │ ├── LogUtils.java │ │ │ │ ├── LunarUtils.java │ │ │ │ ├── MapUtils.java │ │ │ │ ├── MessengerUtils.java │ │ │ │ ├── MetaDataUtils.java │ │ │ │ ├── NetworkUtils.java │ │ │ │ ├── NotificationUtils.java │ │ │ │ ├── NumberUtils.java │ │ │ │ ├── ObjectUtils.java │ │ │ │ ├── PathUtils.java │ │ │ │ ├── PermissionUtils.java │ │ │ │ ├── PhoneUtils.java │ │ │ │ ├── PinyinUtils.java │ │ │ │ ├── ProcessUtils.java │ │ │ │ ├── RandomUtils.java │ │ │ │ ├── ReflectUtils.java │ │ │ │ ├── RegexUtils.java │ │ │ │ ├── ResourceUtils.java │ │ │ │ ├── RetrofitUtils.java │ │ │ │ ├── RomUtils.java │ │ │ │ ├── SDCardUtils.java │ │ │ │ ├── SPStaticUtils.java │ │ │ │ ├── SPUtils.java │ │ │ │ ├── ScreenUtils.java │ │ │ │ ├── ServiceUtils.java │ │ │ │ ├── ShadowUtils.java │ │ │ │ ├── ShellUtils.java │ │ │ │ ├── SizeUtils.java │ │ │ │ ├── SnackbarUtils.java │ │ │ │ ├── SpanUtils.java │ │ │ │ ├── StringUtils.java │ │ │ │ ├── TemperatureUtils.java │ │ │ │ ├── ThreadUtils.java │ │ │ │ ├── ThrowableUtils.java │ │ │ │ ├── TimeUtils.java │ │ │ │ ├── ToastUtils.java │ │ │ │ ├── TouchUtils.java │ │ │ │ ├── UiMessageUtils.java │ │ │ │ ├── UriUtils.java │ │ │ │ ├── Utils.java │ │ │ │ ├── UtilsActivityLifecycleImpl.java │ │ │ │ ├── UtilsBridge.java │ │ │ │ ├── UtilsFileProvider.java │ │ │ │ ├── UtilsTransActivity.java │ │ │ │ ├── UtilsTransActivity4MainProcess.java │ │ │ │ ├── VibrateUtils.java │ │ │ │ ├── ViewUtils.java │ │ │ │ ├── VolumeUtils.java │ │ │ │ └── ZipUtils.java │ │ │ └── res/ │ │ │ ├── drawable/ │ │ │ │ └── utils_toast_bg.xml │ │ │ ├── layout/ │ │ │ │ └── utils_toast_view.xml │ │ │ ├── values/ │ │ │ │ └── styles.xml │ │ │ ├── values-v21/ │ │ │ │ └── styles.xml │ │ │ └── xml/ │ │ │ └── util_code_provider_paths.xml │ │ └── upload.sh │ ├── dokit-weex/ │ │ ├── .gitignore │ │ ├── build.gradle │ │ ├── gradle.properties │ │ ├── proguard-rules.pro │ │ ├── src/ │ │ │ └── main/ │ │ │ ├── AndroidManifest.xml │ │ │ ├── java/ │ │ │ │ └── com/ │ │ │ │ └── didichuxing/ │ │ │ │ └── doraemonkit/ │ │ │ │ └── weex/ │ │ │ │ ├── common/ │ │ │ │ │ └── DKCommonActivity.java │ │ │ │ ├── devtool/ │ │ │ │ │ ├── DevToolActivity.java │ │ │ │ │ ├── DevToolScanActivity.java │ │ │ │ │ └── WeexDevToolKit.kt │ │ │ │ ├── info/ │ │ │ │ │ ├── WeexInfo.java │ │ │ │ │ ├── WeexInfoAdapter.java │ │ │ │ │ ├── WeexInfoFragment.java │ │ │ │ │ ├── WeexInfoHacker.java │ │ │ │ │ └── WeexInfoKit.kt │ │ │ │ ├── log/ │ │ │ │ │ ├── WeexLogInfoDoKitView.java │ │ │ │ │ └── WeexLogKit.kt │ │ │ │ └── storage/ │ │ │ │ ├── StorageAdapter.java │ │ │ │ ├── StorageDialogFragment.java │ │ │ │ ├── StorageFragment.java │ │ │ │ ├── StorageHacker.java │ │ │ │ ├── StorageInfo.java │ │ │ │ └── WeexStorageKit.kt │ │ │ └── res/ │ │ │ ├── layout/ │ │ │ │ ├── dk_fragment_info.xml │ │ │ │ ├── dk_fragment_info_item.xml │ │ │ │ ├── dk_fragment_storage.xml │ │ │ │ ├── dk_item_storage_dialog.xml │ │ │ │ ├── dk_item_storage_watch.xml │ │ │ │ └── dk_weex_float_log_info.xml │ │ │ ├── values/ │ │ │ │ ├── colors.xml │ │ │ │ ├── dimens.xml │ │ │ │ ├── strings.xml │ │ │ │ └── styles.xml │ │ │ ├── values-en-rUS/ │ │ │ │ └── strings.xml │ │ │ ├── values-zh-rCN/ │ │ │ │ └── strings.xml │ │ │ └── values-zh-rTW/ │ │ │ └── strings.xml │ │ └── upload.sh │ ├── dokit_module.json │ ├── gradle/ │ │ └── wrapper/ │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties │ ├── gradle.properties │ ├── gradlew │ ├── gradlew.bat │ ├── settings.gradle │ ├── upload.gradle │ ├── upload_didi.sh │ ├── upload_local.sh │ ├── upload_maven.sh │ ├── upload_maven_central.gradle │ └── upload_private.gradle ├── CODE_OF_CONDUCT.md ├── CODE_OF_CONDUCT.zh-cn.md ├── CONTRIBUTING.md ├── Doc/ │ ├── android-ReleaseNotes.md │ ├── iOS-ReleaseNotes.md │ ├── iOS_cn_guide.md │ ├── iOS_en_guide.md │ ├── miniapp-ReleaseNotes.md │ └── miniapp_cn_guide.md ├── DoraemonKit.podspec ├── Flutter/ │ ├── .gitignore │ ├── .metadata │ ├── CHANGELOG.md │ ├── LICENSE │ ├── README.md │ ├── analysis_options.yaml │ ├── example/ │ │ ├── .gitignore │ │ ├── .metadata │ │ ├── LICENSE │ │ ├── android/ │ │ │ ├── .gitignore │ │ │ ├── app/ │ │ │ │ ├── build.gradle │ │ │ │ └── src/ │ │ │ │ ├── debug/ │ │ │ │ │ └── AndroidManifest.xml │ │ │ │ ├── main/ │ │ │ │ │ ├── AndroidManifest.xml │ │ │ │ │ ├── kotlin/ │ │ │ │ │ │ └── com/ │ │ │ │ │ │ └── linjizong/ │ │ │ │ │ │ └── flutter_app/ │ │ │ │ │ │ └── MainActivity.kt │ │ │ │ │ └── res/ │ │ │ │ │ ├── drawable/ │ │ │ │ │ │ └── launch_background.xml │ │ │ │ │ └── values/ │ │ │ │ │ └── styles.xml │ │ │ │ └── profile/ │ │ │ │ └── AndroidManifest.xml │ │ │ ├── build.gradle │ │ │ ├── gradle/ │ │ │ │ └── wrapper/ │ │ │ │ └── gradle-wrapper.properties │ │ │ ├── gradle.properties │ │ │ └── settings.gradle │ │ ├── ios/ │ │ │ ├── .gitignore │ │ │ ├── Flutter/ │ │ │ │ ├── .last_build_id │ │ │ │ ├── AppFrameworkInfo.plist │ │ │ │ ├── Debug.xcconfig │ │ │ │ └── Release.xcconfig │ │ │ ├── Podfile │ │ │ ├── Runner/ │ │ │ │ ├── AppDelegate.h │ │ │ │ ├── AppDelegate.m │ │ │ │ ├── Assets.xcassets/ │ │ │ │ │ ├── AppIcon.appiconset/ │ │ │ │ │ │ └── Contents.json │ │ │ │ │ └── LaunchImage.imageset/ │ │ │ │ │ ├── Contents.json │ │ │ │ │ └── README.md │ │ │ │ ├── Base.lproj/ │ │ │ │ │ ├── LaunchScreen.storyboard │ │ │ │ │ └── Main.storyboard │ │ │ │ ├── Info.plist │ │ │ │ ├── Runner-Bridging-Header.h │ │ │ │ └── main.m │ │ │ ├── Runner.xcodeproj/ │ │ │ │ ├── project.pbxproj │ │ │ │ ├── project.xcworkspace/ │ │ │ │ │ └── xcshareddata/ │ │ │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ │ │ └── WorkspaceSettings.xcsettings │ │ │ │ └── xcshareddata/ │ │ │ │ └── xcschemes/ │ │ │ │ └── Runner.xcscheme │ │ │ └── Runner.xcworkspace/ │ │ │ └── xcshareddata/ │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ │ ├── lib/ │ │ │ ├── main.dart │ │ │ └── page2.dart │ │ ├── local.properties │ │ ├── pubspec.yaml │ │ └── test/ │ │ └── dokit_library_test.dart │ ├── flutterw │ ├── lib/ │ │ ├── dokit.dart │ │ ├── engine/ │ │ │ ├── dokit_binding.dart │ │ │ └── dokit_http.dart │ │ ├── kit/ │ │ │ ├── apm/ │ │ │ │ ├── apm.dart │ │ │ │ ├── fps_kit.dart │ │ │ │ ├── http_kit.dart │ │ │ │ ├── launch/ │ │ │ │ │ ├── model.dart │ │ │ │ │ ├── page_launch_kit.dart │ │ │ │ │ └── route_observer.dart │ │ │ │ ├── log_kit.dart │ │ │ │ ├── memory_kit.dart │ │ │ │ ├── method_channel_kit.dart │ │ │ │ ├── route_kit.dart │ │ │ │ ├── source_code_kit.dart │ │ │ │ └── vm/ │ │ │ │ ├── version.dart │ │ │ │ ├── vm_helper.dart │ │ │ │ └── vm_service_wrapper.dart │ │ │ ├── biz/ │ │ │ │ └── biz.dart │ │ │ ├── common/ │ │ │ │ ├── basic_info.dart │ │ │ │ └── common.dart │ │ │ ├── kit.dart │ │ │ ├── observer.dart │ │ │ └── visual/ │ │ │ ├── color_pick.dart │ │ │ ├── view_check.dart │ │ │ └── visual.dart │ │ ├── ui/ │ │ │ ├── dokit_app.dart │ │ │ ├── dokit_btn.dart │ │ │ ├── kit_page.dart │ │ │ └── resident_page.dart │ │ ├── util/ │ │ │ ├── byte_util.dart │ │ │ ├── screen_util.dart │ │ │ └── time_util.dart │ │ └── widget/ │ │ ├── dash_decoration.dart │ │ ├── fps_chart.dart │ │ ├── source_code/ │ │ │ ├── source_code_view.dart │ │ │ └── syntax_highlighter.dart │ │ └── widget_build_chain/ │ │ ├── widget_build_chain_page.dart │ │ └── widget_details_page.dart │ ├── pubspec.yaml │ └── test/ │ └── widget_test.dart ├── LICENSE ├── README.md ├── README_EN.md ├── Web/ │ ├── .gitignore │ ├── DEVELOPMENT.md │ ├── LICENSE │ ├── README.md │ ├── jest.config.js │ ├── lerna-debug.log │ ├── lerna.json │ ├── package.json │ ├── packages/ │ │ ├── core/ │ │ │ ├── README.md │ │ │ ├── __tests__/ │ │ │ │ └── index.test.js │ │ │ ├── package.json │ │ │ ├── rollup.config.js │ │ │ └── src/ │ │ │ ├── common/ │ │ │ │ ├── components/ │ │ │ │ │ ├── card.vue │ │ │ │ │ ├── dokit-ui/ │ │ │ │ │ │ ├── index.js │ │ │ │ │ │ └── layout/ │ │ │ │ │ │ ├── col.css │ │ │ │ │ │ ├── col.js │ │ │ │ │ │ ├── index.js │ │ │ │ │ │ ├── row.css │ │ │ │ │ │ └── row.js │ │ │ │ │ ├── toast/ │ │ │ │ │ │ ├── index.js │ │ │ │ │ │ └── toast.vue │ │ │ │ │ ├── top-bar.vue │ │ │ │ │ └── version.vue │ │ │ │ └── js/ │ │ │ │ ├── EventPlayback.js │ │ │ │ ├── EventRecorder.js │ │ │ │ ├── UIController.js │ │ │ │ ├── dom-events-to-record.js │ │ │ │ ├── feature.js │ │ │ │ ├── finder/ │ │ │ │ │ └── index.js │ │ │ │ ├── icon.js │ │ │ │ ├── lifecycle.js │ │ │ │ ├── node.js │ │ │ │ ├── socket.js │ │ │ │ ├── store.js │ │ │ │ ├── util.js │ │ │ │ └── xpath/ │ │ │ │ └── index.js │ │ │ ├── components/ │ │ │ │ ├── app.vue │ │ │ │ ├── elements-highlight.vue │ │ │ │ ├── home.vue │ │ │ │ ├── hostSuspendedBall.vue │ │ │ │ ├── independ-container.vue │ │ │ │ └── router-container.vue │ │ │ ├── index.js │ │ │ ├── router/ │ │ │ │ ├── index.js │ │ │ │ ├── router.js │ │ │ │ └── routes.js │ │ │ └── store/ │ │ │ └── index.js │ │ ├── utils/ │ │ │ ├── README.md │ │ │ ├── __tests__/ │ │ │ │ └── index.test.js │ │ │ ├── package.json │ │ │ ├── rollup.config.js │ │ │ └── src/ │ │ │ ├── deepClone.js │ │ │ ├── dom.js │ │ │ ├── dragable.js │ │ │ ├── eventEmiter.js │ │ │ ├── index.js │ │ │ ├── md5.js │ │ │ ├── network.js │ │ │ └── utils.js │ │ ├── web/ │ │ │ ├── README.md │ │ │ ├── __tests__/ │ │ │ │ └── index.test.js │ │ │ ├── package.json │ │ │ ├── playground/ │ │ │ │ └── index.html │ │ │ ├── rollup.config.js │ │ │ └── src/ │ │ │ ├── assets/ │ │ │ │ ├── CssStore.js │ │ │ │ ├── deepClone.js │ │ │ │ └── util.js │ │ │ ├── common/ │ │ │ │ ├── Card.vue │ │ │ │ └── info-card.vue │ │ │ ├── components/ │ │ │ │ ├── ScanerComponent.vue │ │ │ │ ├── ToolHelloWorld.vue │ │ │ │ └── ToolsContainer.vue │ │ │ ├── feature.js │ │ │ ├── index.js │ │ │ └── plugins/ │ │ │ ├── align-ruler/ │ │ │ │ ├── align-ruler.vue │ │ │ │ ├── index.js │ │ │ │ └── info-box.vue │ │ │ ├── api-mock/ │ │ │ │ ├── index.js │ │ │ │ ├── interface-item.vue │ │ │ │ └── main.vue │ │ │ ├── app-info/ │ │ │ │ ├── ToolAppInfo.vue │ │ │ │ └── index.js │ │ │ ├── console/ │ │ │ │ ├── console-tap.vue │ │ │ │ ├── css/ │ │ │ │ │ └── var.less │ │ │ │ ├── index.js │ │ │ │ ├── js/ │ │ │ │ │ └── console.js │ │ │ │ ├── log-container.vue │ │ │ │ ├── log-detail.vue │ │ │ │ ├── log-item.vue │ │ │ │ ├── main.vue │ │ │ │ └── op-command.vue │ │ │ ├── demo-plugin/ │ │ │ │ ├── ToolHelloWorld.vue │ │ │ │ └── index.js │ │ │ ├── demo-single-plugin/ │ │ │ │ ├── FPS.vue │ │ │ │ ├── IndependPluginDemo.vue │ │ │ │ └── index.js │ │ │ ├── element/ │ │ │ │ ├── components/ │ │ │ │ │ ├── ElementAttributes.vue │ │ │ │ │ ├── ElementBoxModel.vue │ │ │ │ │ ├── ElementBreadcrumb.vue │ │ │ │ │ ├── ElementComputedStyle.vue │ │ │ │ │ └── ElementStyles.vue │ │ │ │ ├── elementContainer.vue │ │ │ │ ├── elementDetails.vue │ │ │ │ ├── elementSnippet.vue │ │ │ │ ├── elementTree.vue │ │ │ │ └── index.js │ │ │ ├── h5-door/ │ │ │ │ ├── ToolH5Door.vue │ │ │ │ └── index.js │ │ │ ├── network/ │ │ │ │ ├── index.js │ │ │ │ ├── js/ │ │ │ │ │ └── request.js │ │ │ │ ├── main.vue │ │ │ │ └── request-item.vue │ │ │ ├── one-machine-with-multiple-controls/ │ │ │ │ ├── app.vue │ │ │ │ └── index.js │ │ │ ├── resources/ │ │ │ │ ├── css/ │ │ │ │ │ └── var.less │ │ │ │ ├── index.js │ │ │ │ ├── js/ │ │ │ │ │ └── resources.js │ │ │ │ ├── main.vue │ │ │ │ ├── resource-container.vue │ │ │ │ ├── resource-item.vue │ │ │ │ └── resource-tap.vue │ │ │ ├── scan-code/ │ │ │ │ ├── app.vue │ │ │ │ └── index.js │ │ │ ├── storage/ │ │ │ │ ├── cookie.vue │ │ │ │ ├── index.js │ │ │ │ ├── info-card.vue │ │ │ │ ├── js/ │ │ │ │ │ └── storage.js │ │ │ │ ├── local-storage.vue │ │ │ │ ├── main.vue │ │ │ │ └── session-storage.vue │ │ │ └── web-vitals-time/ │ │ │ ├── index.js │ │ │ └── info-box.vue │ │ └── web-independent/ │ │ ├── README.md │ │ ├── package.json │ │ ├── rollup.config.js │ │ └── src/ │ │ └── index.js │ ├── playground/ │ │ ├── independent.html │ │ ├── index.html │ │ ├── index2.html │ │ ├── index3.html │ │ └── liveReload.js │ └── scripts/ │ └── dev-playground.js ├── iOS/ │ ├── .gitignore │ ├── Demo/ │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets/ │ │ │ ├── AccentColor.colorset/ │ │ │ │ └── Contents.json │ │ │ ├── AppIcon.appiconset/ │ │ │ │ └── Contents.json │ │ │ └── Contents.json │ │ ├── Base.lproj/ │ │ │ ├── LaunchScreen.storyboard │ │ │ └── Main.storyboard │ │ ├── Info.plist │ │ └── ViewController.swift │ ├── Demo.xcodeproj/ │ │ ├── project.pbxproj │ │ └── xcshareddata/ │ │ └── xcschemes/ │ │ └── Demo.xcscheme │ ├── DoKit/ │ │ ├── Assets/ │ │ │ ├── Assets.xcassets/ │ │ │ │ ├── Contents.json │ │ │ │ └── dokit_logo.imageset/ │ │ │ │ └── Contents.json │ │ │ └── DKTrayViewController.xib │ │ └── Classes/ │ │ ├── CFoundation/ │ │ │ ├── common.h │ │ │ ├── hook.c │ │ │ └── hook.h │ │ ├── Core/ │ │ │ ├── DKTrayViewController.h │ │ │ ├── DKTrayViewController.m │ │ │ ├── DoKit.h │ │ │ └── DoKit.m │ │ ├── EventSynthesize/ │ │ │ ├── UITouch+DKEventSynthesize.h │ │ │ ├── UITouch+DKEventSynthesize.m │ │ │ ├── UIView+EventSynthesize.h │ │ │ └── UIView+EventSynthesize.m │ │ └── Foundation/ │ │ ├── DKMultiControlProtocol.h │ │ ├── DKMultiControlProtocol.m │ │ ├── DKMultiControlStreamManager.h │ │ ├── DKMultiControlStreamManager.m │ │ ├── DKQRCodeScanLogic.h │ │ ├── DKQRCodeScanLogic.m │ │ ├── DKQRCodeScanView.h │ │ ├── DKQRCodeScanView.m │ │ ├── DKQRCodeScanViewController.h │ │ ├── DKQRCodeScanViewController.m │ │ ├── DKWebSocketSession.h │ │ ├── DKWebSocketSession.m │ │ ├── DTO/ │ │ │ ├── DKActionDTOModel.h │ │ │ ├── DKActionDTOModel.m │ │ │ ├── DKCommonDTOModel.h │ │ │ ├── DKCommonDTOModel.m │ │ │ ├── DKDataRequestDTOModel.h │ │ │ ├── DKDataRequestDTOModel.m │ │ │ ├── DKDataResponseDTOModel.h │ │ │ ├── DKDataResponseDTOModel.m │ │ │ ├── DKLoginDataDTOModel.h │ │ │ └── DKLoginDataDTOModel.m │ │ ├── NSURLSessionConfiguration+DoKit.h │ │ └── NSURLSessionConfiguration+DoKit.m │ ├── DoraemonKit/ │ │ ├── Framework/ │ │ │ └── DoraemonLoadAnalyze.framework/ │ │ │ ├── DoraemonLoadAnalyze │ │ │ ├── Headers/ │ │ │ │ └── DoraemonLoadAnalyze.h │ │ │ ├── Info.plist │ │ │ └── Modules/ │ │ │ └── module.modulemap │ │ ├── Resource/ │ │ │ ├── Assets.xcassets/ │ │ │ │ ├── Contents.json │ │ │ │ ├── dk_icon_mc.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── dk_mc_banner.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_align.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_app_info.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_app_start_time.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_arrow_down.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_back.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_back_dark.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_check_circle.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_check_circle_fill.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_close.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_close_dark.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_close_white.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_cpu.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_crash.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_database.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_default.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_dir.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_expand.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_expand_no.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_file.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_file_2.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_file_sync.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_file_sync_banner.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_fps.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_h5.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_health.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_health_bg.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_health_cell_bg.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_health_end.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_health_slide.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_health_slideTop.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_health_start.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_hierarchy_info.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_hierarchy_parent.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_hierarchy_select.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_hierarchy_subview.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_js.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_kadun.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_location.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_log.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_logo.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_logo_dark.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_max.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_memory.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_memory_leak.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_method_use_time.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_min.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_mock.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_mock_cancle.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_mock_data.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_mock_data_selected.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_mock_detail_down.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_mock_detail_up.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_mock_filter_down.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_mock_filter_up.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_mock_gps.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_mock_selected.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_mock_up.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_mock_up_selected.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_more.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_net.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_netflow_list_select.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_netflow_list_unselect.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_netflow_summary_select.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_netflow_summary_unselect.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_nslog.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_qingchu.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_scan.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_scan_line.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_search.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_search_highlight.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_self.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_setting.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_straw.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_time_profiler.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_ui.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_view_check.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_view_level.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_viewmetrics.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── doraemon_visual.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ └── doraemon_weaknet.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── en.lproj/ │ │ │ │ └── Doraemon.strings │ │ │ └── zh-Hans.lproj/ │ │ │ └── Doraemon.strings │ │ └── Src/ │ │ ├── Core/ │ │ │ ├── Base/ │ │ │ │ ├── DoraemonBaseViewController.h │ │ │ │ ├── DoraemonBaseViewController.m │ │ │ │ ├── DoraemonNavBarItemModel.h │ │ │ │ ├── DoraemonNavBarItemModel.m │ │ │ │ ├── DoraemonStatusBarViewController.h │ │ │ │ ├── DoraemonStatusBarViewController.m │ │ │ │ └── View/ │ │ │ │ ├── DoraemonBaseBigTitleView.h │ │ │ │ └── DoraemonBaseBigTitleView.m │ │ │ ├── Cache/ │ │ │ │ ├── DoraemonCacheManager.h │ │ │ │ └── DoraemonCacheManager.m │ │ │ ├── Category/ │ │ │ │ ├── Foundation+Doraemon.m │ │ │ │ ├── NSObject+Doraemon.h │ │ │ │ ├── NSObject+Doraemon.m │ │ │ │ ├── UIColor+Doraemon.h │ │ │ │ ├── UIColor+Doraemon.m │ │ │ │ ├── UIImage+Doraemon.h │ │ │ │ ├── UIImage+Doraemon.m │ │ │ │ ├── UIView+Doraemon.h │ │ │ │ ├── UIView+Doraemon.m │ │ │ │ ├── UIViewController+Doraemon.h │ │ │ │ └── UIViewController+Doraemon.m │ │ │ ├── CommonUI/ │ │ │ │ ├── Alert/ │ │ │ │ │ ├── DoraemonAlertUtil.h │ │ │ │ │ └── DoraemonAlertUtil.m │ │ │ │ ├── CellBtn/ │ │ │ │ │ ├── DoraemonCellButton.h │ │ │ │ │ └── DoraemonCellButton.m │ │ │ │ ├── CellInput/ │ │ │ │ │ ├── DoraemonCellInput.h │ │ │ │ │ └── DoraemonCellInput.m │ │ │ │ ├── CellSwitch/ │ │ │ │ │ ├── DoraemonCellSwitch.h │ │ │ │ │ └── DoraemonCellSwitch.m │ │ │ │ ├── Charts/ │ │ │ │ │ ├── DoraemonBarChart.h │ │ │ │ │ ├── DoraemonBarChart.m │ │ │ │ │ ├── DoraemonChart.h │ │ │ │ │ ├── DoraemonChart.m │ │ │ │ │ ├── DoraemonChartAxis.h │ │ │ │ │ ├── DoraemonChartAxis.m │ │ │ │ │ ├── DoraemonChartDataItem.h │ │ │ │ │ ├── DoraemonChartDataItem.m │ │ │ │ │ ├── DoraemonPieChart.h │ │ │ │ │ ├── DoraemonPieChart.m │ │ │ │ │ ├── DoraemonXAxis.h │ │ │ │ │ ├── DoraemonXAxis.m │ │ │ │ │ ├── DoraemonYAxis.h │ │ │ │ │ └── DoraemonYAxis.m │ │ │ │ ├── Label/ │ │ │ │ │ ├── DoraemonCopyLabel.h │ │ │ │ │ └── DoraemonCopyLabel.m │ │ │ │ ├── Oscillogram/ │ │ │ │ │ ├── DoraemonOscillogramView.h │ │ │ │ │ ├── DoraemonOscillogramView.m │ │ │ │ │ ├── DoraemonOscillogramViewController.h │ │ │ │ │ ├── DoraemonOscillogramViewController.m │ │ │ │ │ ├── DoraemonOscillogramWindow.h │ │ │ │ │ ├── DoraemonOscillogramWindow.m │ │ │ │ │ ├── DoraemonOscillogramWindowManager.h │ │ │ │ │ └── DoraemonOscillogramWindowManager.m │ │ │ │ ├── Toast/ │ │ │ │ │ ├── DoraemonToastUtil.h │ │ │ │ │ └── DoraemonToastUtil.m │ │ │ │ └── Visual/ │ │ │ │ ├── DoraemonVisualInfoWindow.h │ │ │ │ ├── DoraemonVisualInfoWindow.m │ │ │ │ ├── DoraemonVisualMagnifierWindow.h │ │ │ │ └── DoraemonVisualMagnifierWindow.m │ │ │ ├── Define/ │ │ │ │ └── DoraemonDefine.h │ │ │ ├── DoraemonKit.h │ │ │ ├── Entry/ │ │ │ │ ├── Entry/ │ │ │ │ │ ├── DoraemonEntryWindow.h │ │ │ │ │ └── DoraemonEntryWindow.m │ │ │ │ └── Home/ │ │ │ │ ├── Cell/ │ │ │ │ │ ├── DoraemonHomeCell.h │ │ │ │ │ ├── DoraemonHomeCell.m │ │ │ │ │ ├── DoraemonHomeCloseCell.h │ │ │ │ │ ├── DoraemonHomeCloseCell.m │ │ │ │ │ ├── DoraemonHomeFootCell.h │ │ │ │ │ ├── DoraemonHomeFootCell.m │ │ │ │ │ ├── DoraemonHomeHeadCell.h │ │ │ │ │ └── DoraemonHomeHeadCell.m │ │ │ │ ├── DoraemonHomeViewController.h │ │ │ │ ├── DoraemonHomeViewController.m │ │ │ │ ├── DoraemonHomeWindow.h │ │ │ │ ├── DoraemonHomeWindow.m │ │ │ │ ├── DoraemonNavigationController.h │ │ │ │ ├── DoraemonNavigationController.m │ │ │ │ └── Settings/ │ │ │ │ ├── DoraemonSettingCell.h │ │ │ │ ├── DoraemonSettingCell.m │ │ │ │ ├── DoraemonSettingViewController.h │ │ │ │ ├── DoraemonSettingViewController.m │ │ │ │ └── KitManager/ │ │ │ │ ├── DoraemonKitManagerViewController.h │ │ │ │ ├── DoraemonKitManagerViewController.m │ │ │ │ └── View/ │ │ │ │ ├── DoraemonKitManagerCell.h │ │ │ │ ├── DoraemonKitManagerCell.m │ │ │ │ ├── DoraemonKitManagerHeadCell.h │ │ │ │ ├── DoraemonKitManagerHeadCell.m │ │ │ │ ├── DoraemonKitManagerResetCell.h │ │ │ │ └── DoraemonKitManagerResetCell.m │ │ │ ├── Manager/ │ │ │ │ ├── DoraemonManager.h │ │ │ │ └── DoraemonManager.m │ │ │ ├── Network/ │ │ │ │ └── Interceptor/ │ │ │ │ ├── DoraemonNSURLProtocol.h │ │ │ │ ├── DoraemonNSURLProtocol.m │ │ │ │ ├── DoraemonNetworkInterceptor.h │ │ │ │ ├── DoraemonNetworkInterceptor.m │ │ │ │ ├── DoraemonURLSessionDemux.h │ │ │ │ ├── DoraemonURLSessionDemux.m │ │ │ │ ├── NSURLSessionConfiguration+Doraemon.h │ │ │ │ └── NSURLSessionConfiguration+Doraemon.m │ │ │ ├── Plugin/ │ │ │ │ ├── Common/ │ │ │ │ │ ├── AppInfo/ │ │ │ │ │ │ ├── DoraemonAppInfoCell.h │ │ │ │ │ │ ├── DoraemonAppInfoCell.m │ │ │ │ │ │ ├── DoraemonAppInfoPlugin.h │ │ │ │ │ │ ├── DoraemonAppInfoPlugin.m │ │ │ │ │ │ ├── DoraemonAppInfoUtil.h │ │ │ │ │ │ ├── DoraemonAppInfoUtil.m │ │ │ │ │ │ ├── DoraemonAppInfoViewController.h │ │ │ │ │ │ └── DoraemonAppInfoViewController.m │ │ │ │ │ ├── AppSetting/ │ │ │ │ │ │ ├── DoraemonAppSettingPlugin.h │ │ │ │ │ │ └── DoraemonAppSettingPlugin.m │ │ │ │ │ ├── DeleteLocalData/ │ │ │ │ │ │ ├── DoraemonDeleteLocalDataPlugin.h │ │ │ │ │ │ ├── DoraemonDeleteLocalDataPlugin.m │ │ │ │ │ │ ├── DoraemonDeleteLocalDataViewController.h │ │ │ │ │ │ └── DoraemonDeleteLocalDataViewController.m │ │ │ │ │ ├── H5/ │ │ │ │ │ │ ├── DoraemonDefaultWebViewController.h │ │ │ │ │ │ ├── DoraemonDefaultWebViewController.m │ │ │ │ │ │ ├── DoraemonH5Plugin.h │ │ │ │ │ │ ├── DoraemonH5Plugin.m │ │ │ │ │ │ ├── DoraemonH5ViewController.h │ │ │ │ │ │ ├── DoraemonH5ViewController.m │ │ │ │ │ │ ├── DoraemonQRCodeViewController.h │ │ │ │ │ │ ├── DoraemonQRCodeViewController.m │ │ │ │ │ │ └── QRCode/ │ │ │ │ │ │ ├── DoraemonQRScanView.h │ │ │ │ │ │ └── DoraemonQRScanView.m │ │ │ │ │ ├── JavaScript/ │ │ │ │ │ │ ├── DoraemonJavaScriptDetailViewController.h │ │ │ │ │ │ ├── DoraemonJavaScriptDetailViewController.m │ │ │ │ │ │ ├── DoraemonJavaScriptPlugin.h │ │ │ │ │ │ ├── DoraemonJavaScriptPlugin.m │ │ │ │ │ │ ├── DoraemonJavaScriptViewController.h │ │ │ │ │ │ ├── DoraemonJavaScriptViewController.m │ │ │ │ │ │ └── Function/ │ │ │ │ │ │ ├── DoraemonJavaScriptManager.h │ │ │ │ │ │ └── DoraemonJavaScriptManager.m │ │ │ │ │ ├── NSLog/ │ │ │ │ │ │ ├── DoraemonNSLogPlugin.h │ │ │ │ │ │ ├── DoraemonNSLogPlugin.m │ │ │ │ │ │ ├── DoraemonNSLogViewController.h │ │ │ │ │ │ ├── DoraemonNSLogViewController.m │ │ │ │ │ │ ├── Function/ │ │ │ │ │ │ │ ├── DoraemonNSLogManager.h │ │ │ │ │ │ │ ├── DoraemonNSLogManager.m │ │ │ │ │ │ │ ├── DoraemonNSLogModel.h │ │ │ │ │ │ │ └── DoraemonNSLogModel.m │ │ │ │ │ │ └── List/ │ │ │ │ │ │ ├── DoraemonNSLogListCell.h │ │ │ │ │ │ ├── DoraemonNSLogListCell.m │ │ │ │ │ │ ├── DoraemonNSLogListViewController.h │ │ │ │ │ │ ├── DoraemonNSLogListViewController.m │ │ │ │ │ │ ├── DoraemonNSLogSearchView.h │ │ │ │ │ │ └── DoraemonNSLogSearchView.m │ │ │ │ │ ├── NSUserDefaults/ │ │ │ │ │ │ ├── DoraemonNSUserDefaultsPlugin.h │ │ │ │ │ │ ├── DoraemonNSUserDefaultsPlugin.m │ │ │ │ │ │ ├── Model/ │ │ │ │ │ │ │ ├── DoraemonNSUserDefaultsModel.h │ │ │ │ │ │ │ └── DoraemonNSUserDefaultsModel.m │ │ │ │ │ │ └── ViewControllers/ │ │ │ │ │ │ ├── DoraemonNSUserDefaultsEditViewController.h │ │ │ │ │ │ ├── DoraemonNSUserDefaultsEditViewController.m │ │ │ │ │ │ ├── DoraemonNSUserDefaultsViewController.h │ │ │ │ │ │ └── DoraemonNSUserDefaultsViewController.m │ │ │ │ │ └── Sanbox/ │ │ │ │ │ ├── DoraemonSandboxPlugin.h │ │ │ │ │ ├── DoraemonSandboxPlugin.m │ │ │ │ │ ├── Util/ │ │ │ │ │ │ ├── DoraemonDBManager.h │ │ │ │ │ │ └── DoraemonDBManager.m │ │ │ │ │ └── VC/ │ │ │ │ │ ├── DB/ │ │ │ │ │ │ ├── DoraemonDBCell.h │ │ │ │ │ │ ├── DoraemonDBCell.m │ │ │ │ │ │ ├── DoraemonDBRowView.h │ │ │ │ │ │ ├── DoraemonDBRowView.m │ │ │ │ │ │ ├── DoraemonDBShowView.h │ │ │ │ │ │ ├── DoraemonDBShowView.m │ │ │ │ │ │ ├── DoraemonDBTableViewController.h │ │ │ │ │ │ └── DoraemonDBTableViewController.m │ │ │ │ │ ├── DoraemonSanboxDetailViewController.h │ │ │ │ │ ├── DoraemonSanboxDetailViewController.m │ │ │ │ │ ├── DoraemonSandboxCell.h │ │ │ │ │ ├── DoraemonSandboxCell.m │ │ │ │ │ ├── DoraemonSandboxModel.h │ │ │ │ │ ├── DoraemonSandboxModel.m │ │ │ │ │ ├── DoraemonSandboxViewController.h │ │ │ │ │ └── DoraemonSandboxViewController.m │ │ │ │ ├── Performance/ │ │ │ │ │ ├── ANR/ │ │ │ │ │ │ ├── Detail/ │ │ │ │ │ │ │ ├── DoraemonANRDetailViewController.h │ │ │ │ │ │ │ └── DoraemonANRDetailViewController.m │ │ │ │ │ │ ├── DoraemonANRPlugin.h │ │ │ │ │ │ ├── DoraemonANRPlugin.m │ │ │ │ │ │ ├── DoraemonANRViewController.h │ │ │ │ │ │ ├── DoraemonANRViewController.m │ │ │ │ │ │ ├── Function/ │ │ │ │ │ │ │ ├── DoraemonANRManager.h │ │ │ │ │ │ │ ├── DoraemonANRManager.m │ │ │ │ │ │ │ ├── DoraemonANRTool.h │ │ │ │ │ │ │ ├── DoraemonANRTool.m │ │ │ │ │ │ │ ├── DoraemonANRTracker.h │ │ │ │ │ │ │ ├── DoraemonANRTracker.m │ │ │ │ │ │ │ ├── DoraemonPingThread.h │ │ │ │ │ │ │ └── DoraemonPingThread.m │ │ │ │ │ │ └── List/ │ │ │ │ │ │ ├── DoraemonANRListCell.h │ │ │ │ │ │ ├── DoraemonANRListCell.m │ │ │ │ │ │ ├── DoraemonANRListViewController.h │ │ │ │ │ │ └── DoraemonANRListViewController.m │ │ │ │ │ ├── CPU/ │ │ │ │ │ │ ├── DoraemonCPUPlugin.h │ │ │ │ │ │ ├── DoraemonCPUPlugin.m │ │ │ │ │ │ ├── DoraemonCPUViewController.h │ │ │ │ │ │ ├── DoraemonCPUViewController.m │ │ │ │ │ │ └── Function/ │ │ │ │ │ │ ├── DoraemonCPUOscillogramViewController.h │ │ │ │ │ │ ├── DoraemonCPUOscillogramViewController.m │ │ │ │ │ │ ├── DoraemonCPUOscillogramWindow.h │ │ │ │ │ │ ├── DoraemonCPUOscillogramWindow.m │ │ │ │ │ │ ├── DoraemonCPUUtil.h │ │ │ │ │ │ └── DoraemonCPUUtil.m │ │ │ │ │ ├── Crash/ │ │ │ │ │ │ ├── DoraemonCrashPlugin.h │ │ │ │ │ │ ├── DoraemonCrashPlugin.m │ │ │ │ │ │ ├── DoraemonCrashViewController.h │ │ │ │ │ │ ├── DoraemonCrashViewController.m │ │ │ │ │ │ ├── Function/ │ │ │ │ │ │ │ ├── DoraemonCrashSignalExceptionHandler.h │ │ │ │ │ │ │ ├── DoraemonCrashSignalExceptionHandler.m │ │ │ │ │ │ │ ├── DoraemonCrashTool.h │ │ │ │ │ │ │ ├── DoraemonCrashTool.m │ │ │ │ │ │ │ ├── DoraemonCrashUncaughtExceptionHandler.h │ │ │ │ │ │ │ └── DoraemonCrashUncaughtExceptionHandler.m │ │ │ │ │ │ └── List/ │ │ │ │ │ │ ├── DoraemonCrashListCell.h │ │ │ │ │ │ ├── DoraemonCrashListCell.m │ │ │ │ │ │ ├── DoraemonCrashListViewController.h │ │ │ │ │ │ └── DoraemonCrashListViewController.m │ │ │ │ │ ├── FPS/ │ │ │ │ │ │ ├── DoraemonFPSPlugin.h │ │ │ │ │ │ ├── DoraemonFPSPlugin.m │ │ │ │ │ │ ├── DoraemonFPSViewController.h │ │ │ │ │ │ ├── DoraemonFPSViewController.m │ │ │ │ │ │ └── Function/ │ │ │ │ │ │ ├── DoraemonFPSOscillogramViewController.h │ │ │ │ │ │ ├── DoraemonFPSOscillogramViewController.m │ │ │ │ │ │ ├── DoraemonFPSOscillogramWindow.h │ │ │ │ │ │ ├── DoraemonFPSOscillogramWindow.m │ │ │ │ │ │ ├── DoraemonFPSUtil.h │ │ │ │ │ │ └── DoraemonFPSUtil.m │ │ │ │ │ ├── LargeImageDetection/ │ │ │ │ │ │ ├── Detail/ │ │ │ │ │ │ │ ├── DoraemonImageDetectionCell.h │ │ │ │ │ │ │ ├── DoraemonImageDetectionCell.m │ │ │ │ │ │ │ ├── DoraemonLargeImageDetectionListViewController.h │ │ │ │ │ │ │ ├── DoraemonLargeImageDetectionListViewController.m │ │ │ │ │ │ │ ├── DoraemonResponseImageModel.h │ │ │ │ │ │ │ └── DoraemonResponseImageModel.m │ │ │ │ │ │ ├── DoraemonLargeImagePlugin.h │ │ │ │ │ │ ├── DoraemonLargeImagePlugin.m │ │ │ │ │ │ ├── DoraemonLargeImageViewController.h │ │ │ │ │ │ ├── DoraemonLargeImageViewController.m │ │ │ │ │ │ └── Function/ │ │ │ │ │ │ ├── DoraemonLargeImageDetectionManager.h │ │ │ │ │ │ ├── DoraemonLargeImageDetectionManager.m │ │ │ │ │ │ ├── UIImageView+DoraemonSDImage.h │ │ │ │ │ │ └── UIImageView+DoraemonSDImage.m │ │ │ │ │ ├── Memory/ │ │ │ │ │ │ ├── DoraemonMemoryPlugin.h │ │ │ │ │ │ ├── DoraemonMemoryPlugin.m │ │ │ │ │ │ ├── DoraemonMemoryViewController.h │ │ │ │ │ │ ├── DoraemonMemoryViewController.m │ │ │ │ │ │ └── Function/ │ │ │ │ │ │ ├── DoraemonMemoryOscillogramViewController.h │ │ │ │ │ │ ├── DoraemonMemoryOscillogramViewController.m │ │ │ │ │ │ ├── DoraemonMemoryOscillogramWindow.h │ │ │ │ │ │ ├── DoraemonMemoryOscillogramWindow.m │ │ │ │ │ │ ├── DoraemonMemoryUtil.h │ │ │ │ │ │ └── DoraemonMemoryUtil.m │ │ │ │ │ ├── NetFlow/ │ │ │ │ │ │ ├── Detail/ │ │ │ │ │ │ │ ├── DoraemonNetFlowDetailCell.h │ │ │ │ │ │ │ ├── DoraemonNetFlowDetailCell.m │ │ │ │ │ │ │ ├── DoraemonNetFlowDetailSegment.h │ │ │ │ │ │ │ ├── DoraemonNetFlowDetailSegment.m │ │ │ │ │ │ │ ├── DoraemonNetFlowDetailViewController.h │ │ │ │ │ │ │ └── DoraemonNetFlowDetailViewController.m │ │ │ │ │ │ ├── DoraemonNetFlowPlugin.h │ │ │ │ │ │ ├── DoraemonNetFlowPlugin.m │ │ │ │ │ │ ├── DoraemonNetFlowViewController.h │ │ │ │ │ │ ├── DoraemonNetFlowViewController.m │ │ │ │ │ │ ├── Function/ │ │ │ │ │ │ │ ├── DoraemonNetFlowDataSource.h │ │ │ │ │ │ │ ├── DoraemonNetFlowDataSource.m │ │ │ │ │ │ │ ├── DoraemonNetFlowHttpModel.h │ │ │ │ │ │ │ ├── DoraemonNetFlowHttpModel.m │ │ │ │ │ │ │ ├── DoraemonNetFlowManager.h │ │ │ │ │ │ │ ├── DoraemonNetFlowManager.m │ │ │ │ │ │ │ ├── Util/ │ │ │ │ │ │ │ │ ├── DoraemonUrlUtil.h │ │ │ │ │ │ │ │ ├── DoraemonUrlUtil.m │ │ │ │ │ │ │ │ ├── NSURLRequest+Doraemon.h │ │ │ │ │ │ │ │ └── NSURLRequest+Doraemon.m │ │ │ │ │ │ │ └── View/ │ │ │ │ │ │ │ ├── DoraemonNetFlowOscillogramViewController.h │ │ │ │ │ │ │ ├── DoraemonNetFlowOscillogramViewController.m │ │ │ │ │ │ │ ├── DoraemonNetFlowOscillogramWindow.h │ │ │ │ │ │ │ └── DoraemonNetFlowOscillogramWindow.m │ │ │ │ │ │ ├── List/ │ │ │ │ │ │ │ ├── DoraemonNetFlowListCell.h │ │ │ │ │ │ │ ├── DoraemonNetFlowListCell.m │ │ │ │ │ │ │ ├── DoraemonNetFlowListViewController.h │ │ │ │ │ │ │ └── DoraemonNetFlowListViewController.m │ │ │ │ │ │ └── Summary/ │ │ │ │ │ │ ├── DoraemonNetFlowSummaryViewController.h │ │ │ │ │ │ ├── DoraemonNetFlowSummaryViewController.m │ │ │ │ │ │ └── View/ │ │ │ │ │ │ ├── DoraemonNetFlowSummaryMethodDataView.h │ │ │ │ │ │ ├── DoraemonNetFlowSummaryMethodDataView.m │ │ │ │ │ │ ├── DoraemonNetFlowSummaryTotalDataView.h │ │ │ │ │ │ ├── DoraemonNetFlowSummaryTotalDataView.m │ │ │ │ │ │ ├── DoraemonNetFlowSummaryTypeDataView.h │ │ │ │ │ │ └── DoraemonNetFlowSummaryTypeDataView.m │ │ │ │ │ ├── StartTime/ │ │ │ │ │ │ ├── DoraemonStartTimePlugin.h │ │ │ │ │ │ ├── DoraemonStartTimePlugin.m │ │ │ │ │ │ ├── DoraemonStartTimeViewController.h │ │ │ │ │ │ ├── DoraemonStartTimeViewController.m │ │ │ │ │ │ └── TimeProfiler/ │ │ │ │ │ │ ├── DoraemonStartTimeProfilerViewController.h │ │ │ │ │ │ └── DoraemonStartTimeProfilerViewController.m │ │ │ │ │ ├── SubThreadUICheck/ │ │ │ │ │ │ ├── Detail/ │ │ │ │ │ │ │ ├── DoraemonSubThreadUICheckDetailViewController.h │ │ │ │ │ │ │ └── DoraemonSubThreadUICheckDetailViewController.m │ │ │ │ │ │ ├── DoraemonSubThreadUICheckPlugin.h │ │ │ │ │ │ ├── DoraemonSubThreadUICheckPlugin.m │ │ │ │ │ │ ├── DoraemonSubThreadUICheckViewController.h │ │ │ │ │ │ ├── DoraemonSubThreadUICheckViewController.m │ │ │ │ │ │ ├── Function/ │ │ │ │ │ │ │ ├── DoraemonSubThreadUICheckManager.h │ │ │ │ │ │ │ ├── DoraemonSubThreadUICheckManager.m │ │ │ │ │ │ │ ├── UIView+DoraemonSubThreadUICheck.h │ │ │ │ │ │ │ └── UIView+DoraemonSubThreadUICheck.m │ │ │ │ │ │ └── List/ │ │ │ │ │ │ ├── DoraemonSubThreadUICheckListCell.h │ │ │ │ │ │ ├── DoraemonSubThreadUICheckListCell.m │ │ │ │ │ │ ├── DoraemonSubThreadUICheckListViewController.h │ │ │ │ │ │ └── DoraemonSubThreadUICheckListViewController.m │ │ │ │ │ ├── TimeProfiler/ │ │ │ │ │ │ ├── DoraemonTimeProfilerPlugin.h │ │ │ │ │ │ ├── DoraemonTimeProfilerPlugin.m │ │ │ │ │ │ ├── DoraemonTimeProfilerViewController.h │ │ │ │ │ │ ├── DoraemonTimeProfilerViewController.m │ │ │ │ │ │ └── Function/ │ │ │ │ │ │ ├── DoraemonTimeProfiler.h │ │ │ │ │ │ ├── DoraemonTimeProfiler.m │ │ │ │ │ │ ├── DoraemonTimeProfilerCore.c │ │ │ │ │ │ ├── DoraemonTimeProfilerCore.h │ │ │ │ │ │ ├── DoraemonTimeProfilerRecord.h │ │ │ │ │ │ └── DoraemonTimeProfilerRecord.m │ │ │ │ │ ├── UIProfile/ │ │ │ │ │ │ ├── DoraemonUIProfilePlugin.h │ │ │ │ │ │ ├── DoraemonUIProfilePlugin.m │ │ │ │ │ │ ├── DoraemonUIProfileViewController.h │ │ │ │ │ │ ├── DoraemonUIProfileViewController.m │ │ │ │ │ │ └── Function/ │ │ │ │ │ │ ├── DoraemonUIProfileManager.h │ │ │ │ │ │ ├── DoraemonUIProfileManager.m │ │ │ │ │ │ ├── DoraemonUIProfileWindow.h │ │ │ │ │ │ ├── DoraemonUIProfileWindow.m │ │ │ │ │ │ ├── UIViewController+DoraemonUIProfile.h │ │ │ │ │ │ └── UIViewController+DoraemonUIProfile.m │ │ │ │ │ ├── VCProfiler/ │ │ │ │ │ │ ├── UIViewController+DoraemonVCProfiler.h │ │ │ │ │ │ ├── UIViewController+DoraemonVCProfiler.m │ │ │ │ │ │ ├── WKWebView+Doraemon.h │ │ │ │ │ │ └── WKWebView+Doraemon.m │ │ │ │ │ └── WeakNetwork/ │ │ │ │ │ ├── DoraemonWeakNetworkPlugin.h │ │ │ │ │ ├── DoraemonWeakNetworkPlugin.m │ │ │ │ │ ├── DoraemonWeakNetworkViewController.h │ │ │ │ │ ├── DoraemonWeakNetworkViewController.m │ │ │ │ │ ├── Function/ │ │ │ │ │ │ ├── DoraemonWeakNetworkHandle.h │ │ │ │ │ │ ├── DoraemonWeakNetworkHandle.m │ │ │ │ │ │ ├── DoraemonWeakNetworkManager.h │ │ │ │ │ │ └── DoraemonWeakNetworkManager.m │ │ │ │ │ └── View/ │ │ │ │ │ ├── DoraemonWeakNetworkDetailView.h │ │ │ │ │ ├── DoraemonWeakNetworkDetailView.m │ │ │ │ │ ├── DoraemonWeakNetworkInputView.h │ │ │ │ │ ├── DoraemonWeakNetworkInputView.m │ │ │ │ │ ├── DoraemonWeakNetworkLevelView.h │ │ │ │ │ ├── DoraemonWeakNetworkLevelView.m │ │ │ │ │ └── Window/ │ │ │ │ │ ├── DoraemonWeakNetworkWindow.h │ │ │ │ │ └── DoraemonWeakNetworkWindow.m │ │ │ │ ├── Platform/ │ │ │ │ │ ├── FileSync/ │ │ │ │ │ │ ├── DoraemonFileSyncPlugin.h │ │ │ │ │ │ ├── DoraemonFileSyncPlugin.m │ │ │ │ │ │ ├── DoraemonFileSyncViewController.h │ │ │ │ │ │ ├── DoraemonFileSyncViewController.m │ │ │ │ │ │ └── Function/ │ │ │ │ │ │ ├── DoraemonFileSyncManager.h │ │ │ │ │ │ └── DoraemonFileSyncManager.m │ │ │ │ │ ├── Health/ │ │ │ │ │ │ ├── Alert/ │ │ │ │ │ │ │ ├── DoraemonHealthAlertView.h │ │ │ │ │ │ │ ├── DoraemonHealthAlertView.m │ │ │ │ │ │ │ ├── DoraemonHealthEndInputView.h │ │ │ │ │ │ │ └── DoraemonHealthEndInputView.m │ │ │ │ │ │ ├── DoraemonHealthPlugin.h │ │ │ │ │ │ ├── DoraemonHealthPlugin.m │ │ │ │ │ │ ├── DoraemonHealthViewController.h │ │ │ │ │ │ ├── DoraemonHealthViewController.m │ │ │ │ │ │ ├── Function/ │ │ │ │ │ │ │ ├── DoraemonHealthManager.h │ │ │ │ │ │ │ └── DoraemonHealthManager.m │ │ │ │ │ │ └── View/ │ │ │ │ │ │ ├── DoraemonHealthFooterView.h │ │ │ │ │ │ ├── DoraemonHealthFooterView.m │ │ │ │ │ │ ├── Home/ │ │ │ │ │ │ │ ├── Detail/ │ │ │ │ │ │ │ │ ├── DoraemonHealthBgView.h │ │ │ │ │ │ │ │ ├── DoraemonHealthBgView.m │ │ │ │ │ │ │ │ ├── DoraemonHealthBtnView.h │ │ │ │ │ │ │ │ ├── DoraemonHealthBtnView.m │ │ │ │ │ │ │ │ ├── DoraemonHealthStartingTitle.h │ │ │ │ │ │ │ │ └── DoraemonHealthStartingTitle.m │ │ │ │ │ │ │ ├── DoraemonHealthHomeView.h │ │ │ │ │ │ │ └── DoraemonHealthHomeView.m │ │ │ │ │ │ ├── Instructions/ │ │ │ │ │ │ │ ├── DoraemonHealthInstructionsCell.h │ │ │ │ │ │ │ ├── DoraemonHealthInstructionsCell.m │ │ │ │ │ │ │ ├── DoraemonHealthInstructionsView.h │ │ │ │ │ │ │ └── DoraemonHealthInstructionsView.m │ │ │ │ │ │ └── Window/ │ │ │ │ │ │ ├── DoraemonHealthCountdownWindow.h │ │ │ │ │ │ └── DoraemonHealthCountdownWindow.m │ │ │ │ │ └── Mock/ │ │ │ │ │ ├── DoraemonMockPlugin.h │ │ │ │ │ ├── DoraemonMockPlugin.m │ │ │ │ │ ├── DoraemonMockViewController.h │ │ │ │ │ ├── DoraemonMockViewController.m │ │ │ │ │ ├── Function/ │ │ │ │ │ │ ├── DoraemonMockManager.h │ │ │ │ │ │ ├── DoraemonMockManager.m │ │ │ │ │ │ ├── DoraemonMockUtil.h │ │ │ │ │ │ └── DoraemonMockUtil.m │ │ │ │ │ ├── Model/ │ │ │ │ │ │ ├── DoraemonMockAPIModel.h │ │ │ │ │ │ ├── DoraemonMockAPIModel.m │ │ │ │ │ │ ├── DoraemonMockBaseModel.h │ │ │ │ │ │ ├── DoraemonMockBaseModel.m │ │ │ │ │ │ ├── DoraemonMockScene.h │ │ │ │ │ │ ├── DoraemonMockScene.m │ │ │ │ │ │ ├── DoraemonMockUpLoadModel.h │ │ │ │ │ │ └── DoraemonMockUpLoadModel.m │ │ │ │ │ ├── VC/ │ │ │ │ │ │ ├── DoraemonMockAPIViewController.h │ │ │ │ │ │ ├── DoraemonMockAPIViewController.m │ │ │ │ │ │ ├── DoraemonMockBaseViewController.h │ │ │ │ │ │ ├── DoraemonMockBaseViewController.m │ │ │ │ │ │ ├── DoraemonMockUploadViewController.h │ │ │ │ │ │ ├── DoraemonMockUploadViewController.m │ │ │ │ │ │ └── Preview/ │ │ │ │ │ │ ├── DoraemonMockDataPreviewViewController.h │ │ │ │ │ │ └── DoraemonMockDataPreviewViewController.m │ │ │ │ │ └── View/ │ │ │ │ │ ├── Filter/ │ │ │ │ │ │ ├── DoraemonMockFilterButton.h │ │ │ │ │ │ ├── DoraemonMockFilterButton.m │ │ │ │ │ │ ├── DoraemonMockFilterListView.h │ │ │ │ │ │ ├── DoraemonMockFilterListView.m │ │ │ │ │ │ ├── DoraemonMockFilterTableCell.h │ │ │ │ │ │ └── DoraemonMockFilterTableCell.m │ │ │ │ │ ├── List/ │ │ │ │ │ │ ├── Cell/ │ │ │ │ │ │ │ ├── DoraemonMockApiCell.h │ │ │ │ │ │ │ ├── DoraemonMockApiCell.m │ │ │ │ │ │ │ ├── DoraemonMockBaseCell.h │ │ │ │ │ │ │ ├── DoraemonMockBaseCell.m │ │ │ │ │ │ │ ├── DoraemonMockUploadCell.h │ │ │ │ │ │ │ └── DoraemonMockUploadCell.m │ │ │ │ │ │ ├── DoraemonMockDetailSwitch.h │ │ │ │ │ │ ├── DoraemonMockDetailSwitch.m │ │ │ │ │ │ ├── DoraemonMockSceneButton.h │ │ │ │ │ │ ├── DoraemonMockSceneButton.m │ │ │ │ │ │ └── ListView/ │ │ │ │ │ │ ├── DoraemonMockApiListView.h │ │ │ │ │ │ ├── DoraemonMockApiListView.m │ │ │ │ │ │ ├── DoraemonMockBaseListView.h │ │ │ │ │ │ ├── DoraemonMockBaseListView.m │ │ │ │ │ │ ├── DoraemonMockUploadListView.h │ │ │ │ │ │ └── DoraemonMockUploadListView.m │ │ │ │ │ └── Search/ │ │ │ │ │ ├── DoraemonMockSearchView.h │ │ │ │ │ └── DoraemonMockSearchView.m │ │ │ │ ├── Protocol/ │ │ │ │ │ ├── DoraemonPluginProtocol.h │ │ │ │ │ └── DoraemonStartPluginProtocol.h │ │ │ │ └── UI/ │ │ │ │ ├── ColorPick/ │ │ │ │ │ ├── DoraemonColorPickPlugin.h │ │ │ │ │ ├── DoraemonColorPickPlugin.m │ │ │ │ │ └── Function/ │ │ │ │ │ ├── DoraemonColorPickInfoView.h │ │ │ │ │ ├── DoraemonColorPickInfoView.m │ │ │ │ │ ├── DoraemonColorPickInfoWindow.h │ │ │ │ │ ├── DoraemonColorPickInfoWindow.m │ │ │ │ │ ├── DoraemonColorPickMagnifyLayer.h │ │ │ │ │ ├── DoraemonColorPickMagnifyLayer.m │ │ │ │ │ ├── DoraemonColorPickView.h │ │ │ │ │ ├── DoraemonColorPickView.m │ │ │ │ │ ├── DoraemonColorPickWindow.h │ │ │ │ │ └── DoraemonColorPickWindow.m │ │ │ │ ├── Hierarchy/ │ │ │ │ │ ├── DoraemonHierarchyPlugin.h │ │ │ │ │ ├── DoraemonHierarchyPlugin.m │ │ │ │ │ ├── Function/ │ │ │ │ │ │ ├── Category/ │ │ │ │ │ │ │ ├── NSObject+DoraemonHierarchy.h │ │ │ │ │ │ │ ├── NSObject+DoraemonHierarchy.m │ │ │ │ │ │ │ ├── UIColor+DoraemonHierarchy.h │ │ │ │ │ │ │ ├── UIColor+DoraemonHierarchy.m │ │ │ │ │ │ │ ├── UIViewController+DoraemonHierarchy.h │ │ │ │ │ │ │ └── UIViewController+DoraemonHierarchy.m │ │ │ │ │ │ ├── DoraemonEnumDescription.h │ │ │ │ │ │ ├── DoraemonEnumDescription.m │ │ │ │ │ │ ├── DoraemonHierarchyFormatterTool.h │ │ │ │ │ │ ├── DoraemonHierarchyFormatterTool.m │ │ │ │ │ │ ├── DoraemonHierarchyHelper.h │ │ │ │ │ │ └── DoraemonHierarchyHelper.m │ │ │ │ │ └── UserInterface/ │ │ │ │ │ ├── Cell/ │ │ │ │ │ │ ├── DoraemonHierarchyDetailTitleCell.h │ │ │ │ │ │ ├── DoraemonHierarchyDetailTitleCell.m │ │ │ │ │ │ ├── DoraemonHierarchySelectorCell.h │ │ │ │ │ │ ├── DoraemonHierarchySelectorCell.m │ │ │ │ │ │ ├── DoraemonHierarchySwitchCell.h │ │ │ │ │ │ ├── DoraemonHierarchySwitchCell.m │ │ │ │ │ │ ├── DoraemonHierarchyTitleCell.h │ │ │ │ │ │ └── DoraemonHierarchyTitleCell.m │ │ │ │ │ ├── DoraemonHierarchyDetailViewController.h │ │ │ │ │ ├── DoraemonHierarchyDetailViewController.m │ │ │ │ │ ├── DoraemonHierarchyTableViewController.h │ │ │ │ │ ├── DoraemonHierarchyTableViewController.m │ │ │ │ │ ├── DoraemonHierarchyViewController.h │ │ │ │ │ ├── DoraemonHierarchyViewController.m │ │ │ │ │ ├── DoraemonHierarchyWindow.h │ │ │ │ │ ├── DoraemonHierarchyWindow.m │ │ │ │ │ ├── Model/ │ │ │ │ │ │ ├── DoraemonHierarchyCategoryModel.h │ │ │ │ │ │ ├── DoraemonHierarchyCategoryModel.m │ │ │ │ │ │ ├── DoraemonHierarchyCellModel.h │ │ │ │ │ │ └── DoraemonHierarchyCellModel.m │ │ │ │ │ └── View/ │ │ │ │ │ ├── DKHierarchyInfoView.h │ │ │ │ │ ├── DKHierarchyInfoView.m │ │ │ │ │ ├── DKHierarchyPickerView.h │ │ │ │ │ ├── DKHierarchyPickerView.m │ │ │ │ │ ├── DKMoveView.h │ │ │ │ │ ├── DKMoveView.m │ │ │ │ │ ├── DKPickerView.h │ │ │ │ │ ├── DKPickerView.m │ │ │ │ │ ├── DoraemonHierarchyHeaderView.h │ │ │ │ │ └── DoraemonHierarchyHeaderView.m │ │ │ │ ├── ViewAlign/ │ │ │ │ │ ├── DoraemonViewAlignPlugin.h │ │ │ │ │ ├── DoraemonViewAlignPlugin.m │ │ │ │ │ └── Function/ │ │ │ │ │ ├── DoraemonViewAlignManager.h │ │ │ │ │ ├── DoraemonViewAlignManager.m │ │ │ │ │ ├── DoraemonViewAlignView.h │ │ │ │ │ └── DoraemonViewAlignView.m │ │ │ │ ├── ViewCheck/ │ │ │ │ │ ├── DoraemonViewCheckPlugin.h │ │ │ │ │ ├── DoraemonViewCheckPlugin.m │ │ │ │ │ └── Function/ │ │ │ │ │ ├── DoraemonViewCheckManager.h │ │ │ │ │ ├── DoraemonViewCheckManager.m │ │ │ │ │ ├── DoraemonViewCheckView.h │ │ │ │ │ └── DoraemonViewCheckView.m │ │ │ │ └── ViewMetrics/ │ │ │ │ ├── DoraemonMetricsViewController.h │ │ │ │ ├── DoraemonMetricsViewController.m │ │ │ │ ├── DoraemonViewMetricsPlugin.h │ │ │ │ ├── DoraemonViewMetricsPlugin.m │ │ │ │ └── Function/ │ │ │ │ ├── DoraemonViewMetricsConfig.h │ │ │ │ ├── DoraemonViewMetricsConfig.m │ │ │ │ ├── UIView+DoraemonViewMetrics.h │ │ │ │ └── UIView+DoraemonViewMetrics.m │ │ │ └── Util/ │ │ │ ├── BSBacktraceLogger/ │ │ │ │ ├── DoraemonBacktraceLogger.h │ │ │ │ └── DoraemonBacktraceLogger.m │ │ │ ├── BuriedPoint/ │ │ │ │ ├── DoraemonBuriedPointManager.h │ │ │ │ └── DoraemonBuriedPointManager.m │ │ │ ├── DoraemonNetworkUtil.h │ │ │ ├── DoraemonNetworkUtil.m │ │ │ ├── DoraemonStatisticsUtil.h │ │ │ ├── DoraemonStatisticsUtil.m │ │ │ ├── DoraemonThreadSafeMutableArray.h │ │ │ ├── DoraemonThreadSafeMutableArray.m │ │ │ ├── DoraemonThreadSafeMutableDictionary.h │ │ │ ├── DoraemonThreadSafeMutableDictionary.m │ │ │ ├── DoraemonUtil.h │ │ │ ├── DoraemonUtil.m │ │ │ ├── Doraemoni18NUtil.h │ │ │ ├── Doraemoni18NUtil.m │ │ │ └── fishhook/ │ │ │ ├── doraemon_fishhook.c │ │ │ └── doraemon_fishhook.h │ │ ├── Database/ │ │ │ ├── DoraemonDatabasePlugin.h │ │ │ ├── DoraemonDatabasePlugin.m │ │ │ ├── DoraemonDatabaseViewController.h │ │ │ └── DoraemonDatabaseViewController.m │ │ ├── GPS/ │ │ │ ├── DoraemonGPSPlugin.h │ │ │ ├── DoraemonGPSPlugin.m │ │ │ ├── DoraemonGPSViewController.h │ │ │ ├── DoraemonGPSViewController.m │ │ │ ├── Function/ │ │ │ │ ├── CLLocationManager+Doraemon.h │ │ │ │ ├── CLLocationManager+Doraemon.m │ │ │ │ ├── DoraemonGPSMocker.h │ │ │ │ └── DoraemonGPSMocker.m │ │ │ └── View/ │ │ │ ├── DoraemonMockGPSCenterView.h │ │ │ ├── DoraemonMockGPSCenterView.m │ │ │ ├── DoraemonMockGPSInputView.h │ │ │ ├── DoraemonMockGPSInputView.m │ │ │ ├── DoraemonMockGPSOperateView.h │ │ │ └── DoraemonMockGPSOperateView.m │ │ ├── Logger/ │ │ │ ├── DoraemonCocoaLumberjackPlugin.h │ │ │ ├── DoraemonCocoaLumberjackPlugin.m │ │ │ ├── DoraemonCocoaLumberjackViewController.h │ │ │ ├── DoraemonCocoaLumberjackViewController.m │ │ │ ├── Function/ │ │ │ │ ├── DoraemonCocoaLumberjackLogger.h │ │ │ │ ├── DoraemonCocoaLumberjackLogger.m │ │ │ │ ├── DoraemonDDLogMessage.h │ │ │ │ └── DoraemonDDLogMessage.m │ │ │ └── List/ │ │ │ ├── DoraemonCocoaLumberjackLevelView.h │ │ │ ├── DoraemonCocoaLumberjackLevelView.m │ │ │ ├── DoraemonCocoaLumberjackListCell.h │ │ │ ├── DoraemonCocoaLumberjackListCell.m │ │ │ ├── DoraemonCocoaLumberjackListViewController.h │ │ │ └── DoraemonCocoaLumberjackListViewController.m │ │ ├── MLeaksFinder/ │ │ │ ├── Detail/ │ │ │ │ ├── DoraemonMLeaksFinderDetailViewController.h │ │ │ │ └── DoraemonMLeaksFinderDetailViewController.m │ │ │ ├── DoraemonMLeaksFinderPlugin.h │ │ │ ├── DoraemonMLeaksFinderPlugin.m │ │ │ ├── DoraemonMLeaksFinderViewController.h │ │ │ ├── DoraemonMLeaksFinderViewController.m │ │ │ ├── Function/ │ │ │ │ ├── Extra/ │ │ │ │ │ ├── DoraemonMemoryLeakData.h │ │ │ │ │ └── DoraemonMemoryLeakData.m │ │ │ │ ├── MLeakedObjectProxy.h │ │ │ │ ├── MLeakedObjectProxy.m │ │ │ │ ├── MLeaksFinder.h │ │ │ │ ├── NSObject+MemoryLeak.h │ │ │ │ ├── NSObject+MemoryLeak.m │ │ │ │ ├── UIApplication+MemoryLeak.h │ │ │ │ ├── UIApplication+MemoryLeak.m │ │ │ │ ├── UINavigationController+MemoryLeak.h │ │ │ │ ├── UINavigationController+MemoryLeak.m │ │ │ │ ├── UIPageViewController+MemoryLeak.h │ │ │ │ ├── UIPageViewController+MemoryLeak.m │ │ │ │ ├── UISplitViewController+MemoryLeak.h │ │ │ │ ├── UISplitViewController+MemoryLeak.m │ │ │ │ ├── UITabBarController+MemoryLeak.h │ │ │ │ ├── UITabBarController+MemoryLeak.m │ │ │ │ ├── UITouch+MemoryLeak.h │ │ │ │ ├── UITouch+MemoryLeak.m │ │ │ │ ├── UIView+MemoryLeak.h │ │ │ │ ├── UIView+MemoryLeak.m │ │ │ │ ├── UIViewController+MemoryLeak.h │ │ │ │ └── UIViewController+MemoryLeak.m │ │ │ └── List/ │ │ │ ├── DoraemonMLeaksFinderListCell.h │ │ │ ├── DoraemonMLeaksFinderListCell.m │ │ │ ├── DoraemonMLeaksFinderListViewController.h │ │ │ └── DoraemonMLeaksFinderListViewController.m │ │ ├── MethodUseTime/ │ │ │ ├── DoraemonMethodUseTimePlugin.h │ │ │ ├── DoraemonMethodUseTimePlugin.m │ │ │ ├── DoraemonMethodUseTimeViewController.h │ │ │ ├── DoraemonMethodUseTimeViewController.m │ │ │ ├── Function/ │ │ │ │ ├── DoraemonMethodUseTimeManager.h │ │ │ │ └── DoraemonMethodUseTimeManager.m │ │ │ └── List/ │ │ │ ├── DoraemonMethodUseTimeListCell.h │ │ │ ├── DoraemonMethodUseTimeListCell.m │ │ │ ├── DoraemonMethodUseTimeListViewController.h │ │ │ └── DoraemonMethodUseTimeListViewController.m │ │ ├── MultiControl/ │ │ │ ├── Function/ │ │ │ │ └── EventSync/ │ │ │ │ ├── Communication/ │ │ │ │ │ ├── DoraemonMCClient.h │ │ │ │ │ ├── DoraemonMCClient.m │ │ │ │ │ ├── DoraemonMCServer.h │ │ │ │ │ └── DoraemonMCServer.m │ │ │ │ ├── Dispatcher/ │ │ │ │ │ ├── DoraemonMCCommandExcutor.h │ │ │ │ │ ├── DoraemonMCCommandExcutor.m │ │ │ │ │ ├── DoraemonMCCommandGenerator.h │ │ │ │ │ └── DoraemonMCCommandGenerator.m │ │ │ │ ├── Event/ │ │ │ │ │ ├── Capture/ │ │ │ │ │ │ ├── DoraemonMCEventCapturer.h │ │ │ │ │ │ ├── DoraemonMCEventCapturer.m │ │ │ │ │ │ ├── DoraemonMCGestureTargetActionPair.h │ │ │ │ │ │ ├── DoraemonMCGestureTargetActionPair.m │ │ │ │ │ │ ├── DoraemonMCReuseViewDelegateProxy.h │ │ │ │ │ │ └── DoraemonMCReuseViewDelegateProxy.m │ │ │ │ │ └── Excutor/ │ │ │ │ │ ├── DoraemonMCEventHandler.h │ │ │ │ │ └── DoraemonMCEventHandler.m │ │ │ │ ├── MessagePackager/ │ │ │ │ │ ├── DoraemonMCMessagePackager.h │ │ │ │ │ └── DoraemonMCMessagePackager.m │ │ │ │ ├── Serialize/ │ │ │ │ │ ├── EventInfo/ │ │ │ │ │ │ ├── DoraemonMCGustureSerializer.h │ │ │ │ │ │ ├── DoraemonMCGustureSerializer.m │ │ │ │ │ │ ├── UIControl+DoraemonMCSerializer.h │ │ │ │ │ │ ├── UIControl+DoraemonMCSerializer.m │ │ │ │ │ │ ├── UIGestureRecognizer+DoraemonMCSerializer.h │ │ │ │ │ │ ├── UIGestureRecognizer+DoraemonMCSerializer.m │ │ │ │ │ │ ├── UILongPressGestureRecognizer+DoraemonMCSerializer.h │ │ │ │ │ │ ├── UILongPressGestureRecognizer+DoraemonMCSerializer.m │ │ │ │ │ │ ├── UIPanGestureRecognizer+DoraemonMCSerializer.h │ │ │ │ │ │ ├── UIPanGestureRecognizer+DoraemonMCSerializer.m │ │ │ │ │ │ ├── UIPinchGestureRecognizer+DoraemonMCSerializer.h │ │ │ │ │ │ ├── UIPinchGestureRecognizer+DoraemonMCSerializer.m │ │ │ │ │ │ ├── UIResponder+DoraemonMCSerializer.h │ │ │ │ │ │ ├── UIResponder+DoraemonMCSerializer.m │ │ │ │ │ │ ├── UIRotationGestureRecognizer+DoraemonMCSerializer.h │ │ │ │ │ │ ├── UIRotationGestureRecognizer+DoraemonMCSerializer.m │ │ │ │ │ │ ├── UISwipeGestureRecognizer+DoraemonMCSerializer.h │ │ │ │ │ │ ├── UISwipeGestureRecognizer+DoraemonMCSerializer.m │ │ │ │ │ │ ├── UITapGestureRecognizer+DoraemonMCSerializer.h │ │ │ │ │ │ └── UITapGestureRecognizer+DoraemonMCSerializer.m │ │ │ │ │ └── XPath/ │ │ │ │ │ ├── DoraemonMCXPathSerializer.h │ │ │ │ │ └── DoraemonMCXPathSerializer.m │ │ │ │ └── Utils/ │ │ │ │ ├── NSObject+DoraemonMCSupport.h │ │ │ │ └── NSObject+DoraemonMCSupport.m │ │ │ ├── Plugin/ │ │ │ │ ├── DoraemonMultiControlPlugin.h │ │ │ │ └── DoraemonMultiControlPlugin.m │ │ │ └── UserInterface/ │ │ │ └── Main/ │ │ │ ├── DoraemonMCViewController.h │ │ │ └── DoraemonMCViewController.m │ │ └── Weex/ │ │ ├── DevTool/ │ │ │ ├── DoraemonWeexDevToolViewController.h │ │ │ ├── DoraemonWeexDevToolViewController.m │ │ │ ├── DoraemonWeexDevTooloPlugin.h │ │ │ └── DoraemonWeexDevTooloPlugin.m │ │ ├── Info/ │ │ │ ├── DoraemonWeexInfoPlugin.h │ │ │ ├── DoraemonWeexInfoPlugin.m │ │ │ ├── DoraemonWeexInfoViewController.h │ │ │ ├── DoraemonWeexInfoViewController.m │ │ │ ├── Function/ │ │ │ │ ├── DoraemonWeexInfoAnalyzer.h │ │ │ │ ├── DoraemonWeexInfoAnalyzer.m │ │ │ │ ├── DoraemonWeexInfoDataManager.h │ │ │ │ └── DoraemonWeexInfoDataManager.m │ │ │ └── View/ │ │ │ ├── DoraemonWeexInfoCell.h │ │ │ ├── DoraemonWeexInfoCell.m │ │ │ ├── DoraemonWeexInfoHeaderView.h │ │ │ └── DoraemonWeexInfoHeaderView.m │ │ ├── Log/ │ │ │ ├── DoraemonWeexLogPlugin.h │ │ │ ├── DoraemonWeexLogPlugin.m │ │ │ ├── DoraemonWeexLogViewController.h │ │ │ ├── DoraemonWeexLogViewController.m │ │ │ ├── Function/ │ │ │ │ ├── DoraemonWeexLogDataSource.h │ │ │ │ ├── DoraemonWeexLogDataSource.m │ │ │ │ ├── DoraemonWeexLogger.h │ │ │ │ └── DoraemonWeexLogger.m │ │ │ ├── Model/ │ │ │ │ ├── DoraemonWeexLogModel.h │ │ │ │ └── DoraemonWeexLogModel.m │ │ │ └── View/ │ │ │ ├── DoraemonWeexLogCell.h │ │ │ ├── DoraemonWeexLogCell.m │ │ │ ├── DoraemonWeexLogLevelView.h │ │ │ ├── DoraemonWeexLogLevelView.m │ │ │ ├── DoraemonWeexLogSearchView.h │ │ │ └── DoraemonWeexLogSearchView.m │ │ └── Storage/ │ │ ├── DoraemonWeexStoragePlugin.h │ │ ├── DoraemonWeexStoragePlugin.m │ │ ├── DoraemonWeexStorageViewController.h │ │ ├── DoraemonWeexStorageViewController.m │ │ ├── Function/ │ │ │ ├── DoraemonWeexStorageResolver.h │ │ │ └── DoraemonWeexStorageResolver.m │ │ └── View/ │ │ ├── DoraemonWeexStorageCell.h │ │ ├── DoraemonWeexStorageCell.m │ │ ├── DoraemonWeexStorageRowView.h │ │ ├── DoraemonWeexStorageRowView.m │ │ ├── DoraemonWeexStorageShowView.h │ │ └── DoraemonWeexStorageShowView.m │ ├── DoraemonKitDemo/ │ │ ├── DoraemonKitDemo/ │ │ │ ├── Assets.xcassets/ │ │ │ │ ├── AppIcon.appiconset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── Contents.json │ │ │ │ ├── emoji.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ └── zhaoliyin.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── Base.lproj/ │ │ │ │ └── LaunchScreen.storyboard │ │ │ ├── DemoVC/ │ │ │ │ ├── Base/ │ │ │ │ │ ├── DoraemonDemoBaseViewController.h │ │ │ │ │ └── DoraemonDemoBaseViewController.m │ │ │ │ ├── Common/ │ │ │ │ │ ├── DoraemonDemoCommonViewController.h │ │ │ │ │ └── DoraemonDemoCommonViewController.m │ │ │ │ ├── Crash/ │ │ │ │ │ ├── DoraemonDemoCrashMRCView.h │ │ │ │ │ ├── DoraemonDemoCrashMRCView.m │ │ │ │ │ ├── DoraemonDemoCrashViewController.h │ │ │ │ │ └── DoraemonDemoCrashViewController.m │ │ │ │ ├── Home/ │ │ │ │ │ ├── DoraemonDemoHomeViewController.h │ │ │ │ │ └── DoraemonDemoHomeViewController.m │ │ │ │ ├── Logger/ │ │ │ │ │ ├── DoraemonDemoLoggerViewController.h │ │ │ │ │ └── DoraemonDemoLoggerViewController.m │ │ │ │ ├── MemoryLeak/ │ │ │ │ │ ├── DoraemonDemoMemoryLeakModel.h │ │ │ │ │ ├── DoraemonDemoMemoryLeakModel.m │ │ │ │ │ ├── DoraemonDemoMemoryLeakView.h │ │ │ │ │ ├── DoraemonDemoMemoryLeakView.m │ │ │ │ │ ├── DoraemonDemoMemoryLeakViewController.h │ │ │ │ │ └── DoraemonDemoMemoryLeakViewController.m │ │ │ │ ├── MockGPS/ │ │ │ │ │ ├── DoraemonDemoMockGPSAnnotation.h │ │ │ │ │ ├── DoraemonDemoMockGPSAnnotation.m │ │ │ │ │ ├── DoraemonDemoMockGPSViewController.h │ │ │ │ │ └── DoraemonDemoMockGPSViewController.m │ │ │ │ ├── MultiControl/ │ │ │ │ │ ├── DoraemonDemoMultiConLongPressGesture.h │ │ │ │ │ ├── DoraemonDemoMultiConLongPressGesture.m │ │ │ │ │ ├── DoraemonDemoMultiConPinchGesture.h │ │ │ │ │ ├── DoraemonDemoMultiConPinchGesture.m │ │ │ │ │ ├── DoraemonDemoMultiConRotationGesture.h │ │ │ │ │ ├── DoraemonDemoMultiConRotationGesture.m │ │ │ │ │ ├── DoraemonDemoMultiConScreenEdgePanGesture.h │ │ │ │ │ ├── DoraemonDemoMultiConScreenEdgePanGesture.m │ │ │ │ │ ├── DoraemonDemoMultiConSwipeGesture.h │ │ │ │ │ ├── DoraemonDemoMultiConSwipeGesture.m │ │ │ │ │ ├── DoraemonDemoMultiConTapGesture.h │ │ │ │ │ ├── DoraemonDemoMultiConTapGesture.m │ │ │ │ │ ├── DoraemonDemoMultiControlViewController.h │ │ │ │ │ ├── DoraemonDemoMultiControlViewController.m │ │ │ │ │ ├── DoraemonDemoMultiSlideView.h │ │ │ │ │ └── DoraemonDemoMultiSlideView.m │ │ │ │ ├── Net/ │ │ │ │ │ ├── NSURLProtocol/ │ │ │ │ │ │ ├── DoraemonDemoURLProtocol1.h │ │ │ │ │ │ ├── DoraemonDemoURLProtocol1.m │ │ │ │ │ │ ├── DoraemonDemoURLProtocol2.h │ │ │ │ │ │ └── DoraemonDemoURLProtocol2.m │ │ │ │ │ ├── Cell/ │ │ │ │ │ │ ├── DoraemonDemoNetTableViewCell.h │ │ │ │ │ │ └── DoraemonDemoNetTableViewCell.m │ │ │ │ │ ├── DoraemonDemoNetViewController.h │ │ │ │ │ ├── DoraemonDemoNetViewController.m │ │ │ │ │ ├── Image/ │ │ │ │ │ │ ├── DoraemonDemoImageShowViewController.h │ │ │ │ │ │ ├── DoraemonDemoImageShowViewController.m │ │ │ │ │ │ ├── DoraemonDemoImageViewController.h │ │ │ │ │ │ └── DoraemonDemoImageViewController.m │ │ │ │ │ └── WebView/ │ │ │ │ │ ├── DoraemonUIWebViewViewController.h │ │ │ │ │ ├── DoraemonUIWebViewViewController.m │ │ │ │ │ ├── DoraemonWKWebViewViewController.h │ │ │ │ │ └── DoraemonWKWebViewViewController.m │ │ │ │ ├── Performance/ │ │ │ │ │ ├── DoraemonDemoPerformanceViewController.h │ │ │ │ │ └── DoraemonDemoPerformanceViewController.m │ │ │ │ ├── Sanbox/ │ │ │ │ │ ├── DoraemonDemoSanboxViewController.h │ │ │ │ │ └── DoraemonDemoSanboxViewController.m │ │ │ │ └── UI/ │ │ │ │ ├── DoraemonDemoUIViewController.h │ │ │ │ └── DoraemonDemoUIViewController.m │ │ │ ├── DoKitAppDelegate.h │ │ │ ├── DoKitAppDelegate.m │ │ │ ├── DoraemonKitDemo-PrefixHeader.pch │ │ │ ├── Info.plist │ │ │ ├── Plugin/ │ │ │ │ ├── StartPlugin.h │ │ │ │ ├── StartPlugin.m │ │ │ │ ├── TestPlugin.h │ │ │ │ └── TestPlugin.m │ │ │ ├── Resource/ │ │ │ │ ├── Doraemon.docx │ │ │ │ ├── Doraemon.xlsx │ │ │ │ └── doraemon.html │ │ │ ├── Util/ │ │ │ │ ├── DoraemonKitDemoi18Util.h │ │ │ │ ├── DoraemonKitDemoi18Util.m │ │ │ │ ├── NSObject+Runtime.h │ │ │ │ └── NSObject+Runtime.m │ │ │ ├── en.lproj/ │ │ │ │ └── DoraemonKitDemo.strings │ │ │ ├── main.m │ │ │ └── zh-Hans.lproj/ │ │ │ ├── DoraemonKitDemo.strings │ │ │ └── LaunchScreen.strings │ │ ├── DoraemonKitDemo.xcodeproj/ │ │ │ └── project.pbxproj │ │ └── Podfile │ ├── DoraemonLoadAnalyzeDemo/ │ │ ├── DoraemonLoadAnalyze/ │ │ │ ├── DoraemonLoadAnalyze.h │ │ │ ├── DoraemonLoadAnalyze.mm │ │ │ └── Info.plist │ │ ├── DoraemonLoadAnalyzeDemo/ │ │ │ ├── AppDelegate.h │ │ │ ├── AppDelegate.m │ │ │ ├── Assets.xcassets/ │ │ │ │ ├── AppIcon.appiconset/ │ │ │ │ │ └── Contents.json │ │ │ │ └── Contents.json │ │ │ ├── Base.lproj/ │ │ │ │ ├── LaunchScreen.storyboard │ │ │ │ └── Main.storyboard │ │ │ ├── Info.plist │ │ │ ├── ViewController+A.h │ │ │ ├── ViewController+A.m │ │ │ ├── ViewController+B.h │ │ │ ├── ViewController+B.m │ │ │ ├── ViewController.h │ │ │ ├── ViewController.m │ │ │ └── main.m │ │ ├── DoraemonLoadAnalyzeDemo.xcodeproj/ │ │ │ ├── project.pbxproj │ │ │ └── xcshareddata/ │ │ │ └── xcschemes/ │ │ │ ├── DoraemonLoadAnalyze.xcscheme │ │ │ └── UniversalFramework.xcscheme │ │ └── README.md │ ├── LICENSE │ ├── Podfile │ ├── README.md │ └── Swift/ │ └── DoKitSwiftDemo/ │ ├── DoKitSwiftDemo/ │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets/ │ │ │ ├── AppIcon.appiconset/ │ │ │ │ └── Contents.json │ │ │ ├── Contents.json │ │ │ ├── emoji.imageset/ │ │ │ │ └── Contents.json │ │ │ └── zhaoliyin.imageset/ │ │ │ └── Contents.json │ │ ├── Base/ │ │ │ └── DoraemonDemoBaseViewController.swift │ │ ├── Base.lproj/ │ │ │ ├── LaunchScreen.storyboard │ │ │ └── Main.storyboard │ │ ├── Common/ │ │ │ └── DoraemonDemoCommonViewController.swift │ │ ├── Crash/ │ │ │ └── DoraemonDemoCrashViewController.swift │ │ ├── DoKitSwiftDemo-Bridging-Header.h │ │ ├── Home/ │ │ │ └── DoraemonDemoHomeViewController.swift │ │ ├── Info.plist │ │ ├── Logger/ │ │ │ └── DoraemonDemoLoggerViewController.swift │ │ ├── MemoryLeak/ │ │ │ ├── DoraemonDemoMemoryLeakModel.swift │ │ │ ├── DoraemonDemoMemoryLeakView.swift │ │ │ └── DoraemonDemoMemoryLeakViewController.swift │ │ ├── MockGPS/ │ │ │ ├── DoraemonDemoGPSAnnotation.swift │ │ │ └── DoraemonDemoGPSViewController.swift │ │ ├── Net/ │ │ │ └── DoraemonDemoNetViewController.swift │ │ ├── OC/ │ │ │ ├── DoraemonDemoOCViewController.h │ │ │ ├── DoraemonDemoOCViewController.m │ │ │ └── DoraemonDemoSwiftViewController.swift │ │ ├── Performance/ │ │ │ └── DoraemonDemoPerformanceViewController.swift │ │ ├── Plugin/ │ │ │ └── TestPlugin.swift │ │ ├── Resource/ │ │ │ ├── Doraemon.docx │ │ │ ├── Doraemon.xlsx │ │ │ └── doraemon.html │ │ ├── Sanbox/ │ │ │ └── DoraemonDemoSanboxViewController.swift │ │ ├── SceneDelegate.swift │ │ ├── UI/ │ │ │ └── DoraemonDemoUIViewController.swift │ │ ├── Util/ │ │ │ ├── Dictionary+DoKit.swift │ │ │ ├── DoraemonDemoDefine.swift │ │ │ ├── DoraemonDemoi18Util.swift │ │ │ ├── UIColor+DoKit.swift │ │ │ └── UIView+DoKit.swift │ │ ├── en.lproj/ │ │ │ └── DoraemonKitDemo.strings │ │ └── zh-Hans.lproj/ │ │ ├── DoraemonKitDemo.strings │ │ ├── LaunchScreen.strings │ │ └── Main.strings │ ├── DoKitSwiftDemo.xcodeproj/ │ │ └── project.pbxproj │ └── Podfile └── miniapp/ ├── .babelrc ├── .gitignore ├── README.md ├── example/ │ ├── app.js │ ├── app.json │ ├── app.wxss │ ├── pages/ │ │ ├── index/ │ │ │ ├── index.js │ │ │ ├── index.json │ │ │ ├── index.wxml │ │ │ └── index.wxss │ │ └── logs/ │ │ ├── logs.js │ │ ├── logs.json │ │ ├── logs.wxml │ │ └── logs.wxss │ ├── project.config.json │ ├── sitemap.json │ └── utils/ │ └── util.js ├── gulpfile.babel.js ├── package.json ├── postcss.config.js └── src/ ├── components/ │ ├── apimock/ │ │ ├── apimock.js │ │ ├── apimock.json │ │ ├── apimock.wxml │ │ └── apimock.wxss │ ├── appinformation/ │ │ ├── appinformation.js │ │ ├── appinformation.json │ │ ├── appinformation.wxml │ │ ├── appinformation.wxss │ │ └── formatInfo.js │ ├── back/ │ │ ├── back.js │ │ ├── back.json │ │ ├── back.wxml │ │ └── back.wxss │ ├── debug/ │ │ ├── debug.js │ │ ├── debug.json │ │ ├── debug.wxml │ │ └── debug.wxss │ ├── h5door/ │ │ ├── h5door.js │ │ ├── h5door.json │ │ ├── h5door.wxml │ │ └── h5door.wxss │ ├── httpinjector/ │ │ ├── httpinjector.js │ │ ├── httpinjector.json │ │ ├── httpinjector.wxml │ │ └── httpinjector.wxss │ ├── looklogs/ │ │ ├── looklogs.js │ │ ├── looklogs.json │ │ ├── looklogs.wxml │ │ └── looklogs.wxss │ ├── pagedoor/ │ │ ├── pagedoor.js │ │ ├── pagedoor.json │ │ ├── pagedoor.wxml │ │ └── pagedoor.wxss │ ├── positionsimulation/ │ │ ├── positionsimulation.js │ │ ├── positionsimulation.json │ │ ├── positionsimulation.wxml │ │ └── positionsimulation.wxss │ └── storage/ │ ├── storage.js │ ├── storage.json │ ├── storage.wxml │ └── storage.wxss ├── index/ │ ├── index.js │ ├── index.json │ ├── index.wxml │ └── index.wxss ├── logs/ │ ├── logs.js │ ├── logs.json │ ├── logs.wxml │ └── logs.wxss └── utils/ ├── imgbase64.js └── util.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/ISSUE_TEMPLATE/-dokit-------------.md ================================================ --- name: "【DoKit生态场景】-描述出现的问题" about: DoKit生态场景比如Android、iOS、小程序、Flutter,后面跟你需要描述的内容 title: "【DoKit生态场景】-描述出现的问题" labels: '' assignees: jtsky --- It is recommended to refer to the following process before submitting issues:https://github.com/didi/DoraemonKit/issues/745 If you still cannot solve your problem, you can submit your issue according to the following template Please complete the following informations. > Android、iOS? OS version? Brand? > Expected behavior and actual behavior. > Steps to reproduce the problem. > More informations such as error messages and stack traces are welcomed. > 建议提issues之前可以参考一下DoKit社区答疑流程:https://github.com/didi/DoraemonKit/issues/745 假如还是无法解决你的问题,你可以按照以下模板来提交你的issue 请补充如下信息。 > Android 还是 iOS?系统版本是多少?手机品牌是什么?(如有) > 期望的表现和实际的表现。(如有) > 问题重现的步骤。(如有) > 其他的错误信息和堆栈信息如果有也可以一并提供出来。(如有) ================================================ FILE: .github/issue_template.md ================================================ Please complete the following informations. > Expected behavior and actual behavior. > Steps to reproduce the problem. > Android or iOS? OS version? Brand? > More informations such as error messages and stack traces are welcomed. 请补充如下信息。 > 期望的表现和实际的表现。(如有) > 问题重现的步骤。(如有) > Android 还是 iOS?系统版本是多少?手机品牌是什么?(如有) > 其他的错误信息和堆栈信息如果有也可以一并提供出来。(如有) > 最好给我们提供可以复现问题的Demo ================================================ FILE: .gitignore ================================================ # macOS.gitignore # General .DS_Store .AppleDouble .LSOverride # Icon must end with two \r Icon # Thumbnails ._* # Files that might appear in the root of a volume .DocumentRevisions-V100 .fseventsd .Spotlight-V100 .TemporaryItems .Trashes .VolumeIcon.icns .com.apple.timemachine.donotpresent # Directories potentially created on remote AFP share .AppleDB .AppleDesktop Network Trash Folder Temporary Items .apdisk ================================================ FILE: Android/.editorconfig ================================================ # http://editorconfig.org root = true [*] charset = utf-8 indent_style = space indent_size = 4 end_of_line = lf insert_final_newline = true [*.java] max_line_length = 140 wildcard_import_limit = 99 [*.kt] max_line_length = 160 wildcard_import_limit = 99 ================================================ FILE: Android/.gitignore ================================================ *.iml .gradle local.properties /.idea/libraries /.idea/modules.xml /.idea/workspace.xml .DS_Store /build /*/build /captures .externalNativeBuild .idea/ .project .classpath .settings/ /*/*.iml /*/*/*.iml ================================================ FILE: Android/README.md ================================================ # Android接入指南 ## DoKit Android 最新版本 **由于jcenter事件的影响,我们需要将DoKit For Android迁移到mavenCentral(),但是需要更改groupId.所以大家要注意一下,具体的更新信息如下:** **lastversion:3.5.0;kotlin编译插件为1.4.32 ;支持Gradle 6.8及以上** **lastversion:3.5.0.1; kotlin编译插件为1.3.72; 支持Gradle 6.8及以下** |DoKit|最新版本|描述| |-|-|-| |3.3.5及以后的Androidx|debugImplementation "io.github.didi.dokit:${aarName}: ${lastversion}"|(1)dokitx的library和plugin的groupId及版本号需要保持一致;(2)AGP最低版本要求3.3.0+| |3.3.5及以前的Androidx版本|debugImplementation "com.didichuxing.doraemonkit:${aarName}:3.3.5"|(1)dokitx的library和plugin的groupId及版本号需要保持一致; (2)AGP最低版本要求3.3.0+| |支持android support|debugImplementation "com.didichuxing.doraemonkit:${aarName}:3.3.5"|support放弃更新,请大家尽快升级和适配Androidx| **${aarName}需要改为指定的名称,参考如下:** ``` //核心模块 debugImplementation "io.github.didi.dokit:dokitx:${lastversion}" //文件同步模块 debugImplementation "io.github.didi.dokit:dokitx-ft:${lastversion}" //一机多控模块 debugImplementation "io.github.didi.dokit:dokitx-mc:${lastversion}" //weex模块 debugImplementation "io.github.didi.dokit:dokitx-weex:${lastversion}" //no-op 模块 releaseImplementation "io.github.didi.dokit:dokitx-no-op:${lastversion}" ``` **debugImplementation 需要根据自己的构建改成对应的productFlavor** **下面所有的例子均用dokitx举例。要使用support版本请将dokitx改为dokit即可。 v3.3.5以后的版本需要添加mavenCentral()仓库** ## 接入步骤 #### 1. Gradle 依赖 ```groovy dependencies { debugImplementation 'io.github.didi.dokit:dokitx:${lastversion}' releaseImplementation 'io.github.didi.dokit:dokitx-no-op:${lastversion}' } ``` **滴滴内部业务:** 滴滴内部业务线接入请添加模块 ``` //数据mock内部网络库支持 debugImplementation 'io.github.didi.dokit:dokitx-rpc:${lastversion}' //一机多控内部网络库支持 debugImplementation 'io.github.didi.dokit:dokitx-rpc-mc:${lastversion}' ``` 最新版本参见[这里](https://github.com/didi/DoraemonKit/blob/master/Doc/android-ReleaseNotes.md)。 #### 2. 初始化 在 App 启动的时候进行初始化。 ```kotlin overide fun onCreate() { DoKit.Builder(this) .productId("需要使用平台功能的话,需要到dokit.cn平台申请id") .build() } ``` #### 3. 流量监控以及其他AOP功能(可选) AOP包括以下几个功能: 1)百度、腾讯、高德地图的经纬度模拟 2)UrlConnection、Okhttp 抓包以及后续的接口hook功能 3)App 启动耗时统计 4)慢函数 5)大图 在项目的 `build.gradle` 中添加 classpath。 ```groovy buildscript { dependencies { classpath 'io.github.didi.dokit:dokitx-plugin:${lastversion}' } } ``` 在 app 的 `build.gradle` 中添加 plugin。 ```groovy apply plugin: 'com.didi.dokit' ``` **插件配置选项:** 添加到app module 的build.gradle文件下 与android {}处于同一级 ```groovy dokitExt { //通用设置 comm { //地图经纬度开关 gpsSwitch true //网络开关 networkSwitch true //大图开关 bigImgSwitch true //webView js 抓包 webViewSwitch true } slowMethod { //调用栈模式配置 对应gradle.properties中DOKIT_METHOD_STRATEGY=0 stackMethod { //默认值为 5ms 小于该值的函数在调用栈中不显示 thresholdTime 10 //调用栈函数入口 千万不要用我默认的配置 如果有特殊需求修改成项目中自己的入口 假如不需要可以去掉该字段 enterMethods = ["com.didichuxing.doraemondemo.MainDebugActivity.test1"] //黑名单 粒度最小到类 暂不支持到方法 千万不要用我默认的配置 如果有特殊需求修改成项目中自己的入口 假如不需要可以去掉该字段 methodBlacklist = ["com.facebook.drawee.backends.pipeline.Fresco"] } //普通模式配置 对应gradle.properties中DOKIT_METHOD_STRATEGY=1 normalMethod { //默认值为 500ms 小于该值的函数在运行时不会在控制台中被打印 thresholdTime 500 //需要针对函数插装的包名 千万不要用我默认的配置 如果有特殊需求修改成项目中自己的项目包名 假如不需要可以去掉该字段 packageNames = ["com.didichuxing.doraemondemo"] //不需要针对函数插装的包名&类名 千万不要用我默认的配置 如果有特殊需求修改成项目中自己的项目包名 假如不需要可以去掉该字段 methodBlacklist = ["com.didichuxing.doraemondemo.dokit"] } } } ``` 其中**strategy**和**methodSwitch**配置项已经弃用。新的配置开关位于项目根目录下的**gradle.properties**中。 具体的配置如下所示: ``` // dokit全局配置 // 插件开关 DOKIT_PLUGIN_SWITCH=true // DOKIT读取三方库会和booster冲突 如果你的项目中也集成了booster 建议将开关改成false DOKIT_THIRD_LIB_SWITCH=true // 插件日志 DOKIT_LOG_SWITCH=true // 自定义Webview的全限定名 主要是作用于h5 js抓包和数据mock DOKIT_WEBVIEW_CLASS_NAME=com/didichuxing/doraemonkit/widget/webview/MyWebView // dokit 慢函数开关 DOKIT_METHOD_SWITCH=true // dokit 函数调用栈层级 DOKIT_METHOD_STACK_LEVEL=4 // 0:默认模式 打印函数调用栈 需添加指定入口 默认为application onCreate 和attachBaseContext // 1:普通模式 运行时打印某个函数的耗时 全局业务代码函数插入 DOKIT_METHOD_STRATEGY=0 ``` **理由**: 为了减少项目的编译时间,所以慢函数的默认开关为false。再加上plugin的transform注册必须早于project.afterEvaluate。所以无法通过原先的配置项拿到配置信息,只能通过在全局的gradle.properties中的配置可以拿到。 **tips:** 当修改完DoKit插件的相关配置以后一定要clean一下重新编译才能生效。这是AS的缓存增量编译导致的,暂时没有其他好的解决方案。 #### 4. 自定义功能组件(可选) 自定义组件需要实现 IKit 接口,该接口对应哆啦A梦功能面板中的组件。 以代驾乘客端为例,实现环境切换组件如下。 ```kotlin class DemoKit : AbstractKit() { override val category: Int get() = Category.BIZ override val name: Int get() = R.string.dk_kit_demo override val icon: Int get() = R.mipmap.dk_sys_info override fun onClickWithReturn(activity: Activity): Boolean { SimpleDoKitStarter.startFloating(DemoDokitView::class.java) return true } override fun onAppInit(context: Context?) { } } ``` 在初始化的时候注册自定义组件。 ```kotlin override fun onCreate() { DoKit.Builder(this) .productId("需要使用平台功能的话,需要到dokit.cn平台申请id") .customKits(mapKits) .build() } ``` **DoKit入口api** ```kotlin public class DoKit private constructor() { companion object { /** * 主icon是否处于显示状态 */ @JvmStatic val isMainIconShow: Boolean get() = false /** * 显示主icon */ @JvmStatic fun show() { } /** * 直接显示工具面板页面 */ @JvmStatic fun showToolPanel() { } /** * 直接隐藏工具面板 */ @JvmStatic fun hideToolPanel() { } /** * 隐藏主icon */ @JvmStatic fun hide() { } /** * 启动悬浮窗 * @JvmStatic:允许使用java的静态方法的方式调用 * @JvmOverloads :在有默认参数值的方法中使用@JvmOverloads注解,则Kotlin就会暴露多个重载方法。 */ @JvmStatic @JvmOverloads fun launchFloating( targetClass: Class, mode: DoKitViewLaunchMode = DoKitViewLaunchMode.SINGLE_INSTANCE, bundle: Bundle? = null ) { } /** * 启动悬浮窗 * @JvmStatic:允许使用java的静态方法的方式调用 * @JvmOverloads :在有默认参数值的方法中使用@JvmOverloads注解,则Kotlin就会暴露多个重载方法。 */ @JvmStatic @JvmOverloads fun launchFloating( targetClass: KClass, mode: DoKitViewLaunchMode = DoKitViewLaunchMode.SINGLE_INSTANCE, bundle: Bundle? = null ) { } /** * 移除悬浮窗 * @JvmStatic:允许使用java的静态方法的方式调用 * @JvmOverloads :在有默认参数值的方法中使用@JvmOverloads注解,则Kotlin就会暴露多个重载方法。 */ @JvmStatic fun removeFloating(targetClass: Class) { } /** * 移除悬浮窗 * @JvmStatic:允许使用java的静态方法的方式调用 * @JvmOverloads :在有默认参数值的方法中使用@JvmOverloads注解,则Kotlin就会暴露多个重载方法。 */ @JvmStatic fun removeFloating(targetClass: KClass) { } /** * 移除悬浮窗 * @JvmStatic:允许使用java的静态方法的方式调用 * @JvmOverloads :在有默认参数值的方法中使用@JvmOverloads注解,则Kotlin就会暴露多个重载方法。 */ @JvmStatic fun removeFloating(dokitView: AbsDokitView) { } /** * 启动全屏页面 * @JvmStatic:允许使用java的静态方法的方式调用 * @JvmOverloads :在有默认参数值的方法中使用@JvmOverloads注解,则Kotlin就会暴露多个重载方法。 */ @JvmStatic @JvmOverloads fun launchFullScreen( targetClass: Class, context: Context? = null, bundle: Bundle? = null, isSystemFragment: Boolean = false ) { } /** * 启动全屏页面 * @JvmStatic:允许使用java的静态方法的方式调用 * @JvmOverloads :在有默认参数值的方法中使用@JvmOverloads注解,则Kotlin就会暴露多个重载方法。 */ @JvmStatic @JvmOverloads fun launchFullScreen( targetClass: KClass, context: Context? = null, bundle: Bundle? = null, isSystemFragment: Boolean = false ) { } @JvmStatic fun getDoKitView( activity: Activity?, clazz: Class ): T? { return null } @JvmStatic fun getDoKitView( activity: Activity?, clazz: KClass ): T? { return null } /** * 发送自定义一机多控事件 */ @JvmStatic fun sendCustomEvent( eventType: String, view: View? = null, param: Map? = null ) { } /** * 获取一机多控类型 */ @JvmStatic fun mcMode(): WSMode { return WSMode.UNKNOW } } class Builder(private val app: Application) { private var productId: String = "" private var mapKits: LinkedHashMap> = linkedMapOf() private var listKits: List = arrayListOf() init { } fun productId(productId: String): Builder { return this } /** * mapKits & listKits 二选一 */ fun customKits(mapKits: LinkedHashMap>): Builder { return this } /** * mapKits & listKits 二选一 */ fun customKits(listKits: List): Builder { return this } /** * H5任意门全局回调 */ fun webDoorCallback(callback: WebDoorManager.WebDoorCallback): Builder { return this } /** * 禁用app信息上传开关,该上传信息只为做DoKit接入量的统计,如果用户需要保护app隐私,可调用该方法进行禁用 */ fun disableUpload(): Builder { return this } fun debug(debug: Boolean): Builder { return this } /** * 是否显示主入口icon */ fun alwaysShowMainIcon(alwaysShow: Boolean): Builder { return this } /** * 设置加密数据库密码 */ fun databasePass(map: Map): Builder { return this } /** * 设置文件管理助手http端口号 */ fun fileManagerHttpPort(port: Int): Builder { return this } /** * 一机多控端口号 */ fun mcWSPort(port: Int): Builder { return this } /** * 一机多控自定义拦截器 */ fun mcClientProcess(interceptor: McClientProcessor): Builder { return this } /** *设置dokit的性能监控全局回调 */ fun callBack(callback: DoKitCallBack): Builder { return this } /** * 设置扩展网络拦截器的代理对象 */ fun netExtInterceptor(extInterceptorProxy: DokitExtInterceptor.DokitExtInterceptorProxy): Builder { return this } fun build() { } } } ``` 开启插件调试 ./gradlew :app:assembleDebug -Dorg.gradle.daemon=false -Dorg.gradle.debug=true #### 5. FAQ 参考[这里](http://xingyun.xiaojukeji.com/docs/dokit/#/SDKProblems) ================================================ FILE: Android/app/.gitignore ================================================ /build ================================================ FILE: Android/app/build.gradle ================================================ apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin: 'kotlin-kapt' apply from: 'doraemonkit.gradle' //apply plugin: 'com.didiglobal.booster' android { compileSdkVersion rootProject.ext.android["compileSdkVersion"] defaultConfig { applicationId rootProject.ext.android["applicationId"] minSdkVersion rootProject.ext.android["minSdkVersion_21"] targetSdkVersion rootProject.ext.android["targetSdkVersion"] versionCode rootProject.ext.android["versionCode"] versionName rootProject.ext.android["versionName"] ndk { abiFilters "armeabi", "arm64-v8a" } multiDexEnabled true } signingConfigs { release { storeFile file("keystore/test.keystore") storePassword "test123456" keyAlias "test" keyPassword "test123456" } } buildTypes { debug { resValue("string", "dokit_db_Person.db", "a_password") debuggable true minifyEnabled false shrinkResources false signingConfig signingConfigs.release proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } release { // debuggable true minifyEnabled false shrinkResources false signingConfig signingConfigs.release proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } // flavorDimensions "default" // productFlavors { // product0 { // applicationId "com.didichuxing.doraemondemo0" //// resValue "string", "app_name", "DoKitDemo0" //// manifestPlaceholders = [CHANNEL_VALUE: "product0" //// , app_icon : "@mipmap/logo"] // } // product1 { // applicationId "com.didichuxing.doraemondemo1" //// resValue "string", "app_name", "DoKitDemo1" //// manifestPlaceholders = [CHANNEL_VALUE: "product1" //// , app_icon : "@mipmap/logo"] // } // } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } //指定资源和代码目录 sourceSets { debug { manifest.srcFile 'src/debug/java/AndroidManifest.xml' jniLibs.srcDir 'libs' } release { manifest.srcFile 'src/release/java/AndroidManifest.xml' jniLibs.srcDir 'libs' } } kotlinOptions { jvmTarget = "1.8" } lintOptions { abortOnError false } /** * 支持ViewBinding */ buildFeatures { viewBinding = true } packagingOptions { //dokit ktor pickFirst // pickFirst 'META-INF/io.netty.versions.properties' // pickFirst 'META-INF/INDEX.LIST' pickFirst 'lib/x86_64/libc++_shared.so' pickFirst 'lib/arm64-v8a/libc++_shared.so' pickFirst 'lib/armeabi-v7a/libc++_shared.so' pickFirst 'lib/x86/libc++_shared.so' pickFirst 'lib/armeabi/libc++_shared.so' } } //dokit 扩展 dokit { gpsEnable true bigImageEnable true webView { network true dokitWeb true vConsole true } gps { didi true baidu true } } //dokit { // // slowMethod { // //调用栈模式配置 // stackMethod { // //默认值为 5ms 小于该值的函数在调用栈中不显示 // thresholdTime 5 // //调用栈函数入口 // enterMethods = ["com.didichuxing.doraemondemo.MainDebugActivity.test1"] // //黑名单 粒度最小到类 暂不支持到方法 // methodBlacklist = ["com.facebook.drawee.backends.pipeline.Fresco"] // } // //普通模式配置 // normalMethod { // //默认值为 500ms 小于该值的函数在运行时不会在控制台中被打印 // thresholdTime 100 // //需要针对函数插装的包名 // packageNames = ["com.didichuxing.doraemondemo"] // //不需要针对函数插装的包名和类名 // methodBlacklist = ["com.didichuxing.doraemondemo.dokit"] // } // } //} dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation project(path: ':dokit-util') implementation project(path: ':dokit-mc') implementation project(path: ':dokit-autotest') implementation project(path: ':dokit-test') testImplementation rootProject.ext.dependencies["junit"] implementation rootProject.ext.dependencies["constraintLayout"] implementation rootProject.ext.dependencies["multidex"] implementation rootProject.ext.dependencies["flexbox"] implementation rootProject.ext.dependencies["lifecycle-viewmodel-savedstate"] implementation rootProject.ext.dependencies["appcompat"] implementation rootProject.ext.dependencies["material"] if (needKotlinV14()) { implementation rootProject.ext.dependencies["kotlin_v14"] implementation rootProject.ext.dependencies["coroutines-core_v14"] implementation rootProject.ext.dependencies["coroutines-android_v14"] implementation rootProject.ext.dependencies["coil_v13"] } else { implementation rootProject.ext.dependencies["kotlin_v13"] implementation rootProject.ext.dependencies["coroutines-core_v13"] implementation rootProject.ext.dependencies["coroutines-android_v13"] implementation rootProject.ext.dependencies["coil_v11"] } implementation rootProject.ext.dependencies["fragment"] implementation rootProject.ext.dependencies["fragment-ktx"] implementation rootProject.ext.dependencies["retrofit2"] implementation rootProject.ext.dependencies["retrofit2_gson"] implementation rootProject.ext.dependencies["retrofit2_scalars"] implementation rootProject.ext.dependencies["retrofit2_rxjava2"] implementation rootProject.ext.dependencies["rxAndroid"] implementation rootProject.ext.dependencies["okhttp_v3"] implementation rootProject.ext.dependencies["okgo"] implementation rootProject.ext.dependencies["glide"] // implementation rootProject.ext.dependencies["glide_okhttp3"] kapt rootProject.ext.dependencies["glide_compiler"] implementation rootProject.ext.dependencies["picasso"] implementation rootProject.ext.dependencies["fresco"] implementation rootProject.ext.dependencies["fresco-processors"] implementation rootProject.ext.dependencies["image-loader"] implementation rootProject.ext.dependencies["room_runtime"] implementation rootProject.ext.dependencies["wcdb"] implementation rootProject.ext.dependencies["jsoup"] //weex相关 implementation rootProject.ext.dependencies["weex_inspector"] implementation rootProject.ext.dependencies["weex_sdk"] implementation rootProject.ext.dependencies["utilcode"] implementation rootProject.ext.dependencies["brvah"] implementation rootProject.ext.dependencies["easypermissions"] releaseImplementation rootProject.ext.dependencies["okgo"] //高德地图定位 implementation rootProject.ext.dependencies["amap_location"] //高德搜索 implementation rootProject.ext.dependencies["amap_search"] //高德地图 implementation rootProject.ext.dependencies["amap_navi"] //腾讯地图定位 // implementation rootProject.ext.dependencies["tencent_location"] // implementation rootProject.ext.dependencies["tencent_map"] // implementation rootProject.ext.dependencies["tencent_map_util"] // debugImplementation rootProject.ext.dependencies["leakcanary-android"] //百度地图定位 // implementation files('libs/BaiduLBS_Android.jar') //腾讯x5 implementation rootProject.ext.dependencies["tbs"] debugImplementation rootProject.ext.dependencies["leakcanary_android"] implementation 'io.reactivex.rxjava2:rxandroid:2.0.1' implementation project(":dokit-pthread-hook") implementation rootProject.ext.dependencies["epic"] } //configurations.all { // //循环每个依赖库 // resolutionStrategy.eachDependency { DependencyResolveDetails details -> // //获取当前循环到的依赖库 // def requested = details.requested // //如果这个依赖库群组的名字是com.android.support // if (requested.group == 'com.squareup.okhttp3') { // //且其名字不是以multidex开头的 // if (requested.name == "okhttp") { // //这里指定需要统一的依赖版本 比如我的需要配置成27.1.1 // //details.useVersion '3.14.7' // details.useVersion rootProject.ext.android["okhttp_v4"] // } // } // } //} ================================================ FILE: Android/app/doraemonkit.gradle ================================================ def runType = rootProject.ext.publish_config["run_type"] if (runType == 0) { // 引用插件 apply plugin: 'com.didi.dokit.debug' // 这里引用正常库 dependencies { //外部平台依赖 debugImplementation project(":dokit") debugImplementation project(":dokit-mc") debugImplementation project(":dokit-ft") // debugImplementation project(":dokit-weex") // debugImplementation project(":dokit-rpc") // debugImplementation project(":dokit-rpc-mc") debugImplementation project(":dokit-gps-mock") releaseImplementation project(":dokit-no-op") } } else if (runType == 1) { apply plugin: 'com.didi.dokit' // 引用no-op的库 dependencies { //新版线上包 debugImplementation "io.github.didi.dokit:dokitx:${rootProject.ext.publish_config["version"]}" debugImplementation "io.github.didi.dokit:dokitx-ft:${rootProject.ext.publish_config["version"]}" debugImplementation "io.github.didi.dokit:dokitx-mc:${rootProject.ext.publish_config["version"]}" debugImplementation "io.github.didi.dokit:dokitx-weex:${rootProject.ext.publish_config["version"]}" debugImplementation "io.github.didi.dokit:dokit-gps-mock:${rootProject.ext.publish_config["version"]}" releaseImplementation "io.github.didi.dokit:dokitx-no-op:${rootProject.ext.publish_config["version"]}" } } ================================================ FILE: Android/app/proguard-rules.pro ================================================ # Add project specific ProGuard rules here. # You can control the set of applied configuration files using the # proguardFiles setting in build.gradle. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html # If your project uses WebView with JS, uncomment the following # and specify the fully qualified class name to the JavaScript interface # class: #-keepclassmembers class fqcn.of.javascript.interface.for.webview { # public *; #} # Uncomment this to preserve the line number information for # debugging stack traces. #-keepattributes SourceFile,LineNumberTable # If you keep the line number information, uncomment this to # hide the original from file name. #-renamesourcefileattribute SourceFile # start 腾讯地图 SDK -keep class com.tencent.tencentmap.**{*;} -keep class com.tencent.map.**{*;} -keep class com.tencent.beacontmap.**{*;} -keep class navsns.**{*;} -dontwarn com.qq.** -dontwarn com.tencent.** # end 腾讯地图 SDK ================================================ FILE: Android/app/src/androidTest/java/com/didichuxing/doraemondemo/ExampleInstrumentedTest.java ================================================ //package com.didichuxing.doraemondemo; // //import android.content.Context; // //import org.junit.Test; //import org.junit.runner.RunWith; // //import static org.junit.Assert.*; // ///** // * Instrumented test, which will execute on an Android device. // * // * @see Testing documentation // */ //@RunWith(AndroidJUnit4.class) //public class ExampleInstrumentedTest { // @Test // public void useAppContext() throws Exception { // // Context of the app under test. // Context appContext = InstrumentationRegistry.getTargetContext(); // // assertEquals("com.didichuxing.doraemondemo", appContext.getPackageName()); // } //} ================================================ FILE: Android/app/src/debug/java/AndroidManifest.xml ================================================ ================================================ FILE: Android/app/src/debug/java/com/didichuxing/doraemondemo/AopApp.java ================================================ package com.didichuxing.doraemondemo; import android.app.Application; import android.content.Context; import androidx.multidex.MultiDex; import com.didichuxing.doraemondemo.dokit.DemoKit; import com.didichuxing.doraemonkit.DoKit; import com.didichuxing.doraemonkit.DoKitCallBack; import com.didichuxing.doraemonkit.kit.AbstractKit; import com.didichuxing.doraemonkit.kit.network.bean.NetworkRecord; import com.facebook.drawee.backends.pipeline.Fresco; import com.facebook.imagepipeline.core.ImagePipelineConfig; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.List; /** * ================================================ * 作 者:jint(金台) * 版 本:1.0 * 创建日期:2020/4/22-17:03 * 描 述: * 修订历史: * ================================================ */ public class AopApp extends Application { @Override public void onCreate() { super.onCreate(); List kits = new ArrayList<>(); kits.add(new DemoKit()); //测试环境:a49842eeebeb1989b3f9565eb12c276b //线上环境:749a0600b5e48dd77cf8ee680be7b1b7 //new AopTest().test(); ImagePipelineConfig config = ImagePipelineConfig.newBuilder(this) .setDiskCacheEnabled(false) .build(); Fresco.initialize(this, config); //是否显示入口icon // DoraemonKit.setAwaysShowMainIcon(false); new DoKit.Builder(this) .productId("749a0600b5e48dd77cf8ee680be7b1b7") .disableUpload() .fileManagerHttpPort(9001) .mcWSPort(5555) .alwaysShowMainIcon(true) .callBack(new DoKitCallBack() { @Override public void onNetworkCallBack(@NotNull NetworkRecord record) { } @Override public void onCpuCallBack(float value, @NotNull String filePath) { } @Override public void onFpsCallBack(float value, @NotNull String filePath) { } @Override public void onMemoryCallBack(float value, @NotNull String filePath) { } }) .build(); //DoraemonKit.install(this, kits, "70e78c27f9174d68668d8a66a2b66483") } @Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); MultiDex.install(base); } } ================================================ FILE: Android/app/src/debug/java/com/didichuxing/doraemondemo/App.kt ================================================ package com.didichuxing.doraemondemo import android.app.Activity import android.app.Application import android.content.Context import android.view.View import androidx.multidex.MultiDex import com.baidu.mapapi.CoordType import com.baidu.mapapi.SDKInitializer import com.blankj.utilcode.util.PathUtils import com.blankj.utilcode.util.ToastUtils import com.didichuxing.doraemondemo.dokit.DemoKit import com.didichuxing.doraemondemo.dokit.TestSimpleDokitFloatViewKit import com.didichuxing.doraemondemo.dokit.TestSimpleDokitFragmentKit import com.didichuxing.doraemondemo.mc.SlideBar import com.didichuxing.doraemondemo.module.http.CustomInterceptor import com.didichuxing.doraemonkit.DoKit import com.didichuxing.doraemonkit.DoKitCallBack import com.didichuxing.doraemonkit.kit.AbstractKit import com.didichuxing.doraemonkit.kit.core.McClientProcessor import com.didichuxing.doraemonkit.kit.network.bean.NetworkRecord import com.didichuxing.doraemonkit.kit.network.okhttp.interceptor.DokitExtInterceptor import com.didichuxing.doraemonkit.util.LogUtils import com.facebook.drawee.backends.pipeline.Fresco import com.facebook.imagepipeline.core.ImagePipelineConfig import com.lzy.okgo.OkGo import okhttp3.Cache import okhttp3.Interceptor import okhttp3.OkHttpClient import okhttp3.Response import java.io.File /** * @author jint * @mail 704167880@qq.com */ class App : Application() { override fun onCreate() { super.onCreate() //百度地图初始化 SDKInitializer.initialize(this) SDKInitializer.setCoordType(CoordType.BD09LL) //测试环境:a49842eeebeb1989b3f9565eb12c276b //线上环境:749a0600b5e48dd77cf8ee680be7b1b7 //DoraemonKit.disableUpload() //是否显示入口icon // DoraemonKit.setAwaysShowMainIcon(false); val kits: MutableList = ArrayList() kits.add(DemoKit()) kits.add(TestSimpleDokitFloatViewKit()) kits.add(TestSimpleDokitFragmentKit()) val mapKits: LinkedHashMap> = linkedMapOf() mapKits["业务专区1"] = mutableListOf().apply { add(DemoKit()) add(TestSimpleDokitFloatViewKit()) add(TestSimpleDokitFragmentKit()) } mapKits["业务专区2"] = mutableListOf(DemoKit()) DoKit.Builder(this) .productId("749a0600b5e48dd77cf8ee680be7b1b7") //测试环境pid // .productId("277016abcc33bff1e6a4f1afdf14b8e1") .disableUpload() .customKits(mapKits) .fileManagerHttpPort(9001) .databasePass(mapOf("Person.db" to "a_password")) .mcWSPort(5555) .alwaysShowMainIcon(true) .callBack(object : DoKitCallBack { override fun onCpuCallBack(value: Float, filePath: String) { super.onCpuCallBack(value, filePath) } override fun onFpsCallBack(value: Float, filePath: String) { super.onFpsCallBack(value, filePath) } override fun onMemoryCallBack(value: Float, filePath: String) { super.onMemoryCallBack(value, filePath) } override fun onNetworkCallBack(record: NetworkRecord) { super.onNetworkCallBack(record) } }) .netExtInterceptor(object : DokitExtInterceptor.DokitExtInterceptorProxy { override fun intercept(chain: Interceptor.Chain): Response { return chain.proceed(chain.request()) } }) .mcClientProcess(object : McClientProcessor { override fun process( activity: Activity?, view: View?, eventType: String, params: Map ) { when (eventType) { "un_lock" -> { ToastUtils.showShort(params["unlock"]) } "lock_process" -> { val leftMargin = params["progress"]?.toInt() leftMargin?.let { if (view is SlideBar) { view.setMarginLeftExtra(it) } } } else -> { } } } }) .build() val client: OkHttpClient = OkHttpClient.Builder() .addInterceptor(CustomInterceptor()) .cache(Cache(File("${PathUtils.getInternalAppCachePath()}/dokit"), 1024 * 1024 * 100)) .build() OkGo.getInstance().init(this).okHttpClient = client val config = ImagePipelineConfig.newBuilder(this) .setDiskCacheEnabled(false) .build() Fresco.initialize(this, config) // PaymentConfiguration.init( // this, // "pk_test_TYooMQauvdEDq54NiTphI7jx" // ) //严格检查模式 //StrictMode.enableDefaults(); com.didichuxing.doraemonkit.util.LogUtils.getConfig() .setLogSwitch(true) // 设置是否输出到控制台开关,默认开 .setConsoleSwitch(true) // 设置 log 全局标签,默认为空,当全局标签不为空时,我们输出的 log 全部为该 tag, 为空时,如果传入的 tag 为空那就显示类名,否则显示 tag .setGlobalTag("Dokit") // 设置 log 头信息开关,默认为开 .setLogHeadSwitch(true) // 打印 log 时是否存到文件的开关,默认关 .setLog2FileSwitch(false) // 当自定义路径为空时,写入应用的/cache/log/目录中 .setDir("") // 当文件前缀为空时,默认为"util",即写入文件为"util-MM-dd.txt" .setFilePrefix("djx-table-log") // 输出日志是否带边框开关,默认开 .setBorderSwitch(true) // 一条日志仅输出一条,默认开,为美化 AS 3.1 的 Logcat .setSingleTagSwitch(false) // log 的控制台过滤器,和 logcat 过滤器同理,默认 Verbose .setConsoleFilter(LogUtils.V) // log 文件过滤器,和 logcat 过滤器同理,默认 Verbose .setFileFilter(LogUtils.E) // log 栈深度,默认为 1 .setStackDeep(1) .stackOffset = 1 } override fun attachBaseContext(base: Context) { super.attachBaseContext(base) MultiDex.install(this) } companion object { private const val TAG = "App" var leakActivity: Activity? = null } } ================================================ FILE: Android/app/src/debug/java/com/didichuxing/doraemondemo/amap/AMapUtil.java ================================================ /** * */ package com.didichuxing.doraemondemo.amap; import android.text.Html; import android.text.Spanned; import android.widget.EditText; import com.amap.api.maps.model.LatLng; import com.amap.api.services.core.LatLonPoint; import com.amap.api.services.route.BusPath; import com.amap.api.services.route.BusStep; import com.amap.api.services.route.RouteBusLineItem; import com.amap.api.services.route.RouteRailwayItem; import com.didichuxing.doraemondemo.R; import java.text.DecimalFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; public class AMapUtil { /** * 判断edittext是否null */ public static String checkEditText(EditText editText) { if (editText != null && editText.getText() != null && !(editText.getText().toString().trim().equals(""))) { return editText.getText().toString().trim(); } else { return ""; } } public static Spanned stringToSpan(String src) { return src == null ? null : Html.fromHtml(src.replace("\n", "
")); } public static String colorFont(String src, String color) { StringBuffer strBuf = new StringBuffer(); strBuf.append("").append(src) .append(""); return strBuf.toString(); } public static String makeHtmlNewLine() { return "
"; } public static String makeHtmlSpace(int number) { final String space = " "; StringBuilder result = new StringBuilder(); for (int i = 0; i < number; i++) { result.append(space); } return result.toString(); } public static String getFriendlyLength(int lenMeter) { if (lenMeter > 10000) // 10 km { int dis = lenMeter / 1000; return dis + ChString.Kilometer; } if (lenMeter > 1000) { float dis = (float) lenMeter / 1000; DecimalFormat fnum = new DecimalFormat("##0.0"); String dstr = fnum.format(dis); return dstr + ChString.Kilometer; } if (lenMeter > 100) { int dis = lenMeter / 50 * 50; return dis + ChString.Meter; } int dis = lenMeter / 10 * 10; if (dis == 0) { dis = 10; } return dis + ChString.Meter; } public static boolean IsEmptyOrNullString(String s) { return (s == null) || (s.trim().length() == 0); } /** * 把LatLng对象转化为LatLonPoint对象 */ public static LatLonPoint convertToLatLonPoint(LatLng latlon) { return new LatLonPoint(latlon.latitude, latlon.longitude); } /** * 把LatLonPoint对象转化为LatLon对象 */ public static LatLng convertToLatLng(LatLonPoint latLonPoint) { return new LatLng(latLonPoint.getLatitude(), latLonPoint.getLongitude()); } /** * 把集合体的LatLonPoint转化为集合体的LatLng */ public static ArrayList convertArrList(List shapes) { ArrayList lineShapes = new ArrayList(); for (LatLonPoint point : shapes) { LatLng latLngTemp = convertToLatLng(point); lineShapes.add(latLngTemp); } return lineShapes; } /** * long类型时间格式化 */ public static String convertToTime(long time) { SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Date date = new Date(time); return df.format(date); } public static final String HtmlBlack = "#000000"; public static final String HtmlGray = "#808080"; public static String getFriendlyTime(int second) { if (second > 3600) { int hour = second / 3600; int miniate = (second % 3600) / 60; return hour + "小时" + miniate + "分钟"; } if (second >= 60) { int miniate = second / 60; return miniate + "分钟"; } return second + "秒"; } //路径规划方向指示和图片对应 public static int getDriveActionID(String actionName) { if (actionName == null || actionName.equals("")) { return R.mipmap.dir3; } if ("左转".equals(actionName)) { return R.mipmap.dir2; } if ("右转".equals(actionName)) { return R.mipmap.dir1; } if ("向左前方行驶".equals(actionName) || "靠左".equals(actionName)) { return R.mipmap.dir6; } if ("向右前方行驶".equals(actionName) || "靠右".equals(actionName)) { return R.mipmap.dir5; } if ("向左后方行驶".equals(actionName) || "左转调头".equals(actionName)) { return R.mipmap.dir7; } if ("向右后方行驶".equals(actionName)) { return R.mipmap.dir8; } if ("直行".equals(actionName)) { return R.mipmap.dir3; } if ("减速行驶".equals(actionName)) { return R.mipmap.dir4; } return R.mipmap.dir3; } public static int getWalkActionID(String actionName) { if (actionName == null || actionName.equals("")) { return R.mipmap.dir13; } if ("左转".equals(actionName)) { return R.mipmap.dir2; } if ("右转".equals(actionName)) { return R.mipmap.dir1; } if ("向左前方".equals(actionName) || "靠左".equals(actionName)) { return R.mipmap.dir6; } if ("向右前方".equals(actionName) || "靠右".equals(actionName)) { return R.mipmap.dir5; } if ("向左后方".equals(actionName)) { return R.mipmap.dir7; } if ("向右后方".equals(actionName)) { return R.mipmap.dir8; } if ("直行".equals(actionName)) { return R.mipmap.dir3; } if ("通过人行横道".equals(actionName)) { return R.mipmap.dir9; } if ("通过过街天桥".equals(actionName)) { return R.mipmap.dir11; } if ("通过地下通道".equals(actionName)) { return R.mipmap.dir10; } return R.mipmap.dir13; } public static String getBusPathTitle(BusPath busPath) { if (busPath == null) { return String.valueOf(""); } List busSetps = busPath.getSteps(); if (busSetps == null) { return String.valueOf(""); } StringBuffer sb = new StringBuffer(); for (BusStep busStep : busSetps) { StringBuffer title = new StringBuffer(); if (busStep.getBusLines().size() > 0) { for (RouteBusLineItem busline : busStep.getBusLines()) { if (busline == null) { continue; } String buslineName = getSimpleBusLineName(busline.getBusLineName()); title.append(buslineName); title.append(" / "); } // RouteBusLineItem busline = busStep.getBusLines().get(0); sb.append(title.substring(0, title.length() - 3)); sb.append(" > "); } if (busStep.getRailway() != null) { RouteRailwayItem railway = busStep.getRailway(); sb.append(railway.getTrip()+"("+railway.getDeparturestop().getName() +" - "+railway.getArrivalstop().getName()+")"); sb.append(" > "); } } return sb.substring(0, sb.length() - 3); } public static String getBusPathDes(BusPath busPath) { if (busPath == null) { return String.valueOf(""); } long second = busPath.getDuration(); String time = getFriendlyTime((int) second); float subDistance = busPath.getDistance(); String subDis = getFriendlyLength((int) subDistance); float walkDistance = busPath.getWalkDistance(); String walkDis = getFriendlyLength((int) walkDistance); return String.valueOf(time + " | " + subDis + " | 步行" + walkDis); } public static String getSimpleBusLineName(String busLineName) { if (busLineName == null) { return String.valueOf(""); } return busLineName.replaceAll("\\(.*?\\)", ""); } } ================================================ FILE: Android/app/src/debug/java/com/didichuxing/doraemondemo/amap/ChString.java ================================================ package com.didichuxing.doraemondemo.amap; public class ChString { public static final String Kilometer = "\u516c\u91cc";// "公里"; public static final String Meter = "\u7c73";// "米"; public static final String ByFoot = "\u6b65\u884c";// "步行"; public static final String To = "\u53bb\u5f80";// "去往"; public static final String Station = "\u8f66\u7ad9";// "车站"; public static final String TargetPlace = "\u76ee\u7684\u5730";// "目的地"; public static final String StartPlace = "\u51fa\u53d1\u5730";// "出发地"; public static final String About = "\u5927\u7ea6";// "大约"; public static final String Direction = "\u65b9\u5411";// "方向"; public static final String GetOn = "\u4e0a\u8f66";// "上车"; public static final String GetOff = "\u4e0b\u8f66";// "下车"; public static final String Zhan = "\u7ad9";// "站"; public static final String cross = "\u4ea4\u53c9\u8def\u53e3"; // 交叉路口 public static final String type = "\u7c7b\u522b"; // 类别 public static final String address = "\u5730\u5740"; // 地址 public static final String PrevStep = "\u4e0a\u4e00\u6b65"; public static final String NextStep = "\u4e0b\u4e00\u6b65"; public static final String Gong = "\u516c\u4ea4"; public static final String ByBus = "\u4e58\u8f66"; public static final String Arrive = "\u5230\u8FBE";// 到达 } ================================================ FILE: Android/app/src/debug/java/com/didichuxing/doraemondemo/amap/DrivingRouteOverLay.java ================================================ package com.didichuxing.doraemondemo.amap; import android.content.Context; import android.graphics.Color; import com.amap.api.maps.AMap; import com.amap.api.maps.model.BitmapDescriptor; import com.amap.api.maps.model.BitmapDescriptorFactory; import com.amap.api.maps.model.LatLng; import com.amap.api.maps.model.LatLngBounds; import com.amap.api.maps.model.Marker; import com.amap.api.maps.model.MarkerOptions; import com.amap.api.maps.model.PolylineOptions; import com.amap.api.services.core.LatLonPoint; import com.amap.api.services.route.DrivePath; import com.amap.api.services.route.DriveStep; import com.amap.api.services.route.TMC; import com.didichuxing.doraemondemo.R; import java.util.ArrayList; import java.util.List; /** * 导航路线图层类。 */ public class DrivingRouteOverLay extends RouteOverlay{ private DrivePath drivePath; private List throughPointList; private List throughPointMarkerList = new ArrayList(); private boolean throughPointMarkerVisible = true; private List tmcs; private PolylineOptions mPolylineOptions; private PolylineOptions mPolylineOptionscolor = null; private Context mContext; private boolean isColorfulline = true; private float mWidth = 25; private List mLatLngsOfPath; public void setIsColorfulline(boolean iscolorfulline) { this.isColorfulline = iscolorfulline; } /** * 根据给定的参数,构造一个导航路线图层类对象。 * * @param amap 地图对象。 * @param path 导航路线规划方案。 * @param context 当前的activity对象。 */ public DrivingRouteOverLay(Context context, AMap amap, DrivePath path, LatLonPoint start, LatLonPoint end, List throughPointList) { super(context); mContext = context; mAMap = amap; this.drivePath = path; startPoint = AMapUtil.convertToLatLng(start); endPoint = AMapUtil.convertToLatLng(end); this.throughPointList = throughPointList; } public float getRouteWidth() { return mWidth; } /** * 设置路线宽度 * * @param mWidth 路线宽度,取值范围:大于0 */ public void setRouteWidth(float mWidth) { this.mWidth = mWidth; } /** * 添加驾车路线添加到地图上显示。 */ public void addToMap() { initPolylineOptions(); try { if (mAMap == null) { return; } if (mWidth == 0 || drivePath == null) { return; } mLatLngsOfPath = new ArrayList(); tmcs = new ArrayList(); List drivePaths = drivePath.getSteps(); mPolylineOptions.add(startPoint); for (DriveStep step : drivePaths) { List latlonPoints = step.getPolyline(); List tmclist = step.getTMCs(); tmcs.addAll(tmclist); addDrivingStationMarkers(step, convertToLatLng(latlonPoints.get(0))); for (LatLonPoint latlonpoint : latlonPoints) { mPolylineOptions.add(convertToLatLng(latlonpoint)); mLatLngsOfPath.add(convertToLatLng(latlonpoint)); } } mPolylineOptions.add(endPoint); if (startMarker != null) { startMarker.remove(); startMarker = null; } if (endMarker != null) { endMarker.remove(); endMarker = null; } addStartAndEndMarker(); addThroughPointMarker(); if (isColorfulline && tmcs.size()>0 ) { colorWayUpdate(tmcs); }else { showPolyline(); } } catch (Throwable e) { e.printStackTrace(); } } /** * 初始化线段属性 */ private void initPolylineOptions() { mPolylineOptions = null; mPolylineOptions = new PolylineOptions(); mPolylineOptions.color(getDriveColor()).width(getRouteWidth()); } private void showPolyline() { addPolyLine(mPolylineOptions); } /** * 根据不同的路段拥堵情况展示不同的颜色 * * @param tmcSection */ private void colorWayUpdate(List tmcSection) { if (mAMap == null) { return; } if (tmcSection == null || tmcSection.size() <= 0) { return; } TMC segmentTrafficStatus; addPolyLine(new PolylineOptions().add(startPoint, AMapUtil.convertToLatLng(tmcSection.get(0).getPolyline().get(0))) .setDottedLine(true)); String status = ""; for (int i = 0; i < tmcSection.size(); i++) { segmentTrafficStatus = tmcSection.get(i); List mployline = segmentTrafficStatus.getPolyline(); if (status.equals(segmentTrafficStatus.getStatus())) { for (int j = 1; j < mployline.size(); j++) {//第一个点和上一段最后一个点重复,这个不重复添加 mPolylineOptionscolor.add(AMapUtil.convertToLatLng(mployline.get(j))); } }else { if (mPolylineOptionscolor != null) { addPolyLine(mPolylineOptionscolor.color(getcolor(status))); } mPolylineOptionscolor = null; mPolylineOptionscolor = new PolylineOptions().width(getRouteWidth()); status = segmentTrafficStatus.getStatus(); for (int j = 0; j < mployline.size(); j++) { mPolylineOptionscolor.add(AMapUtil.convertToLatLng(mployline.get(j))); } } if (i == tmcSection.size()-1 && mPolylineOptionscolor != null) { addPolyLine(mPolylineOptionscolor.color(getcolor(status))); addPolyLine(new PolylineOptions().add( AMapUtil.convertToLatLng(mployline.get(mployline.size()-1)), endPoint) .setDottedLine(true)); } } } private int getcolor(String status) { if (status.equals("畅通")) { return Color.GREEN; } else if (status.equals("缓行")) { return Color.YELLOW; } else if (status.equals("拥堵")) { return Color.RED; } else if (status.equals("严重拥堵")) { return Color.parseColor("#990033"); } else { return Color.parseColor("#537edc"); } } public LatLng convertToLatLng(LatLonPoint point) { return new LatLng(point.getLatitude(),point.getLongitude()); } /** * @param driveStep * @param latLng */ private void addDrivingStationMarkers(DriveStep driveStep, LatLng latLng) { addStationMarker(new MarkerOptions() .position(latLng) .title("\u65B9\u5411:" + driveStep.getAction() + "\n\u9053\u8DEF:" + driveStep.getRoad()) .snippet(driveStep.getInstruction()).visible(nodeIconVisible) .anchor(0.5f, 0.5f).icon(getDriveBitmapDescriptor())); } @Override protected LatLngBounds getLatLngBounds() { LatLngBounds.Builder b = LatLngBounds.builder(); b.include(new LatLng(startPoint.latitude, startPoint.longitude)); b.include(new LatLng(endPoint.latitude, endPoint.longitude)); if (this.throughPointList != null && this.throughPointList.size() > 0) { for (int i = 0; i < this.throughPointList.size(); i++) { b.include(new LatLng( this.throughPointList.get(i).getLatitude(), this.throughPointList.get(i).getLongitude())); } } return b.build(); } public void setThroughPointIconVisibility(boolean visible) { try { throughPointMarkerVisible = visible; if (this.throughPointMarkerList != null && this.throughPointMarkerList.size() > 0) { for (int i = 0; i < this.throughPointMarkerList.size(); i++) { this.throughPointMarkerList.get(i).setVisible(visible); } } } catch (Throwable e) { e.printStackTrace(); } } private void addThroughPointMarker() { if (this.throughPointList != null && this.throughPointList.size() > 0) { LatLonPoint latLonPoint = null; for (int i = 0; i < this.throughPointList.size(); i++) { latLonPoint = this.throughPointList.get(i); if (latLonPoint != null) { throughPointMarkerList.add(mAMap .addMarker((new MarkerOptions()) .position( new LatLng(latLonPoint .getLatitude(), latLonPoint .getLongitude())) .visible(throughPointMarkerVisible) .icon(getThroughPointBitDes()) .title("\u9014\u7ECF\u70B9"))); } } } } private BitmapDescriptor getThroughPointBitDes() { return BitmapDescriptorFactory.fromResource(R.mipmap.amap_through); } /** * 获取两点间距离 * * @param start * @param end * @return */ public static int calculateDistance(LatLng start, LatLng end) { double x1 = start.longitude; double y1 = start.latitude; double x2 = end.longitude; double y2 = end.latitude; return calculateDistance(x1, y1, x2, y2); } public static int calculateDistance(double x1, double y1, double x2, double y2) { final double NF_pi = 0.01745329251994329; // 弧度 PI/180 x1 *= NF_pi; y1 *= NF_pi; x2 *= NF_pi; y2 *= NF_pi; double sinx1 = Math.sin(x1); double siny1 = Math.sin(y1); double cosx1 = Math.cos(x1); double cosy1 = Math.cos(y1); double sinx2 = Math.sin(x2); double siny2 = Math.sin(y2); double cosx2 = Math.cos(x2); double cosy2 = Math.cos(y2); double[] v1 = new double[3]; v1[0] = cosy1 * cosx1 - cosy2 * cosx2; v1[1] = cosy1 * sinx1 - cosy2 * sinx2; v1[2] = siny1 - siny2; double dist = Math.sqrt(v1[0] * v1[0] + v1[1] * v1[1] + v1[2] * v1[2]); return (int) (Math.asin(dist / 2) * 12742001.5798544); } //获取指定两点之间固定距离点 public static LatLng getPointForDis(LatLng sPt, LatLng ePt, double dis) { double lSegLength = calculateDistance(sPt, ePt); double preResult = dis / lSegLength; return new LatLng((ePt.latitude - sPt.latitude) * preResult + sPt.latitude, (ePt.longitude - sPt.longitude) * preResult + sPt.longitude); } /** * 去掉DriveLineOverlay上的线段和标记。 */ @Override public void removeFromMap() { try { super.removeFromMap(); if (this.throughPointMarkerList != null && this.throughPointMarkerList.size() > 0) { for (int i = 0; i < this.throughPointMarkerList.size(); i++) { this.throughPointMarkerList.get(i).remove(); } this.throughPointMarkerList.clear(); } } catch (Throwable e) { e.printStackTrace(); } } } ================================================ FILE: Android/app/src/debug/java/com/didichuxing/doraemondemo/amap/FloatGpsMockRouteKitView.java ================================================ package com.didichuxing.doraemondemo.amap; import android.content.Context; import android.os.Handler; import android.os.Looper; import android.util.Log; import android.view.View; import android.widget.FrameLayout; import android.widget.SeekBar; import android.widget.TextView; import com.didichuxing.doraemondemo.R; import com.didichuxing.doraemondemo.dokit.SimpleDoKitView; import com.didichuxing.doraemonkit.gps_mock.lbs.route.FloatGpsRouteMockCache; public class FloatGpsMockRouteKitView extends SimpleDoKitView { public static final String TAG = "FloatGpsMockRoutKitView"; private View mRootView; private static int sMockSpeed = 10; private TextView mMockSpeedTv; private SeekBar mSpeedSeekBar; private SeekBar routeSeekBar; private TextView mockRouteTv; private Handler handler = new Handler(Looper.getMainLooper()); private Runnable updateRouteUiRunnable = new Runnable() { @Override public void run() { routeSeekBar.setMax(FloatGpsRouteMockCache.getRouteCount()); routeSeekBar.setProgress(FloatGpsRouteMockCache.getMockRouteProgress()); mockRouteTv.setText("模拟路线进度:" + FloatGpsRouteMockCache.getMockRouteProgress() + " / " + FloatGpsRouteMockCache.getRouteCount()); if (FloatGpsRouteMockCache.getRouteCount() == 0) { mockRouteTv.setText("使用 FloatGpsRouteMockCache.mockGpsRoute 设置模拟线路"); } } }; @Override protected int getLayoutId() { return R.layout.layout_mock_route; } @Override public void onViewCreated(FrameLayout rootView) { super.onViewCreated(rootView); mRootView = rootView; setMockLocationConfig(); } private void setMockLocationConfig() { // 路线模拟工具 FloatGpsRouteMockCache.setRouteChangeListener(new FloatGpsRouteMockCache.IOnRouteChange() { @Override public void onRouteChange() { updateRouteUI(0); } public void onIndexChange(int index) { Log.d(TAG, "⚠️ onIndexChange() called with: index = [" + index + "]"); updateRouteUI(index); } }); mockRouteTv = findViewById(R.id.tv_mock_route); routeSeekBar = findViewById(R.id.dk_sb_mock_route_seekBar); routeSeekBar.setMax(FloatGpsRouteMockCache.getRouteCount()); routeSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar routeSeekBar, int progress, boolean fromUser) { if (fromUser) { updateRoute(getContext().getApplicationContext(), progress); } } @Override public void onStartTrackingTouch(SeekBar routeSeekBar) { } @Override public void onStopTrackingTouch(SeekBar routeSeekBar) { } }); findViewById(R.id.btn_route_back).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { updateRoute(v.getContext().getApplicationContext(), FloatGpsRouteMockCache.getMockRouteProgress() - 1); } }); findViewById(R.id.btn_route_forward).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { updateRoute(v.getContext().getApplicationContext(), FloatGpsRouteMockCache.getMockRouteProgress() + 1); } }); findViewById(R.id.btn_route_pause).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { FloatGpsRouteMockCache.pausePlayMockRoute(); } }); findViewById(R.id.btn_route_resume).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { FloatGpsRouteMockCache.resumePlayMockRoute(getContext()); } }); } private void updateRoute(Context context, int index) { index = FloatGpsRouteMockCache.setMockRouteProgress(context, index); updateRouteUI(index); } private void updateRouteUI(int index) { handler.post(updateRouteUiRunnable); } } ================================================ FILE: Android/app/src/debug/java/com/didichuxing/doraemondemo/amap/FloatGpsPresetMockKit.java ================================================ package com.didichuxing.doraemondemo.amap; import android.app.Activity; import android.content.Context; import com.blankj.utilcode.util.ToastUtils; import com.didichuxing.doraemondemo.R; import com.didichuxing.doraemonkit.DoKit; import com.didichuxing.doraemonkit.aop.DokitPluginConfig; import com.didichuxing.doraemonkit.kit.AbstractKit; import com.didichuxing.doraemonkit.util.DoKitCommUtil; import org.jetbrains.annotations.NotNull; /** * Created by changzuozhen on 2021年1月22日 */ public class FloatGpsPresetMockKit extends AbstractKit { @Override public int getName() { return R.string.dk_kit_gps_mock_preset; } @Override public int getIcon() { return R.mipmap.dk_mock_location_preset; } @Override public boolean onClickWithReturn(@NotNull Activity activity) { if (!DokitPluginConfig.SWITCH_DOKIT_PLUGIN) { ToastUtils.showShort(DoKitCommUtil.getString(R.string.dk_plugin_close_tip)); return false; } if (!DokitPluginConfig.SWITCH_GPS) { ToastUtils.showShort(DoKitCommUtil.getString(R.string.dk_plugin_gps_close_tip)); return false; } DoKit.launchFloating(FloatGpsPresetMockKitView.class); return true; } @Override public void onAppInit(Context context) { } @Override public boolean isInnerKit() { return false; } } ================================================ FILE: Android/app/src/debug/java/com/didichuxing/doraemondemo/amap/FloatGpsPresetMockKitView.java ================================================ package com.didichuxing.doraemondemo.amap; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.FrameLayout; import android.widget.Switch; import android.widget.TextView; import com.didichuxing.doraemondemo.R; import com.didichuxing.doraemondemo.dokit.SimpleDoKitView; import com.didichuxing.doraemonkit.gps_mock.lbs.common.LocInfo; import com.didichuxing.doraemonkit.gps_mock.lbs.manual.FloatGpsMockCache; import com.didichuxing.doraemonkit.gps_mock.lbs.preset.FloatGpsPresetMockCache; import com.didichuxing.doraemonkit.gps_mock.lbs.preset.MockLocList; import com.google.android.flexbox.FlexboxLayout; import java.util.ArrayList; public class FloatGpsPresetMockKitView extends SimpleDoKitView { public static final String TAG = "FloatGpsPresetMockKitView"; private View mRootView; @Override protected int getLayoutId() { return R.layout.layout_mock_location_preset; } @Override public void onViewCreated(FrameLayout rootView) { super.onViewCreated(rootView); mRootView = rootView; setMockLocationConfig(); } private void setMockLocationConfig() { // 模拟位置预 单点 final MockLocList locationList = FloatGpsPresetMockCache.sMockLocationList; ArrayList configString = new ArrayList<>(); for (LocInfo locInfo : locationList) { configString.add(locInfo.locName); } LocInfo config = FloatGpsPresetMockCache.getMockLocConfig(); if (config != null) { Log.i(getTAG(), "⚠️setMockLocationConfig() setSelection called" + config.locName); updateCurrentLocConfig(config); } FlexboxLayout flexboxLayout = findViewById(R.id.cl_mock_gps_flexbox_container); for (final LocInfo locInfo : FloatGpsPresetMockCache.sMockLocationList) { Button button = new Button(getContext()); button.setText(locInfo.locName); button.setMinWidth(0); button.setMinHeight(0); button.setMinimumWidth(0);//必须同时设置这个 button.setMinimumHeight(0);//必须同时设置这个 button.setTextSize(9); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { try { Log.d(getTAG(), "⚠️onNewItemSelected() called with: locInfo = [" + locInfo + "]"); FloatGpsMockCache.mockToLocation(locInfo.lat, locInfo.lng); ((Switch) findViewById(R.id.env_switch3)).setChecked(true); updateCurrentLocConfig(locInfo); } catch (Exception e) { } } }); flexboxLayout.addView(button); } } private void updateCurrentLocConfig(LocInfo currentConfig) { TextView envInfo = this.findViewById(R.id.env_info3); updateGsonInfo(currentConfig.toString(), envInfo); } private void updateGsonInfo(String currentConfig, TextView envInfo) { if (currentConfig == null) { envInfo.setVisibility(View.GONE); } else { envInfo.setVisibility(View.VISIBLE); envInfo.setText(currentConfig); } } } ================================================ FILE: Android/app/src/debug/java/com/didichuxing/doraemondemo/amap/RouteOverlay.java ================================================ package com.didichuxing.doraemondemo.amap; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Color; import com.amap.api.maps.AMap; import com.amap.api.maps.CameraUpdateFactory; import com.amap.api.maps.model.BitmapDescriptor; import com.amap.api.maps.model.BitmapDescriptorFactory; import com.amap.api.maps.model.LatLng; import com.amap.api.maps.model.LatLngBounds; import com.amap.api.maps.model.Marker; import com.amap.api.maps.model.MarkerOptions; import com.amap.api.maps.model.Polyline; import com.amap.api.maps.model.PolylineOptions; import com.didichuxing.doraemondemo.R; import java.util.ArrayList; import java.util.List; public class RouteOverlay { protected List stationMarkers = new ArrayList(); protected List allPolyLines = new ArrayList(); protected Marker startMarker; protected Marker endMarker; protected LatLng startPoint; protected LatLng endPoint; protected AMap mAMap; private Context mContext; private Bitmap startBit, endBit, busBit, walkBit, driveBit; protected boolean nodeIconVisible = true; public RouteOverlay(Context context) { mContext = context; } /** * 去掉BusRouteOverlay上所有的Marker。 * @since V2.1.0 */ public void removeFromMap() { if (startMarker != null) { startMarker.remove(); } if (endMarker != null) { endMarker.remove(); } for (Marker marker : stationMarkers) { marker.remove(); } for (Polyline line : allPolyLines) { line.remove(); } destroyBit(); } private void destroyBit() { if (startBit != null) { startBit.recycle(); startBit = null; } if (endBit != null) { endBit.recycle(); endBit = null; } if (busBit != null) { busBit.recycle(); busBit = null; } if (walkBit != null) { walkBit.recycle(); walkBit = null; } if (driveBit != null) { driveBit.recycle(); driveBit = null; } } /** * 给起点Marker设置图标,并返回更换图标的图片。如不用默认图片,需要重写此方法。 * @return 更换的Marker图片。 * @since V2.1.0 */ protected BitmapDescriptor getStartBitmapDescriptor() { return BitmapDescriptorFactory.fromResource(R.mipmap.amap_start); } /** * 给终点Marker设置图标,并返回更换图标的图片。如不用默认图片,需要重写此方法。 * @return 更换的Marker图片。 * @since V2.1.0 */ protected BitmapDescriptor getEndBitmapDescriptor() { return BitmapDescriptorFactory.fromResource(R.mipmap.amap_end); } /** * 给公交Marker设置图标,并返回更换图标的图片。如不用默认图片,需要重写此方法。 * @return 更换的Marker图片。 * @since V2.1.0 */ protected BitmapDescriptor getBusBitmapDescriptor() { return BitmapDescriptorFactory.fromResource(R.mipmap.dk_lbs_bus); } /** * 给步行Marker设置图标,并返回更换图标的图片。如不用默认图片,需要重写此方法。 * @return 更换的Marker图片。 * @since V2.1.0 */ protected BitmapDescriptor getWalkBitmapDescriptor() { return BitmapDescriptorFactory.fromResource(R.mipmap.dk_lbs_man); } protected BitmapDescriptor getDriveBitmapDescriptor() { return BitmapDescriptorFactory.fromResource(R.mipmap.dk_lbs_car); } protected void addStartAndEndMarker() { startMarker = mAMap.addMarker((new MarkerOptions()) .position(startPoint).icon(getStartBitmapDescriptor()) .title("\u8D77\u70B9")); // startMarker.showInfoWindow(); endMarker = mAMap.addMarker((new MarkerOptions()).position(endPoint) .icon(getEndBitmapDescriptor()).title("\u7EC8\u70B9")); // mAMap.moveCamera(CameraUpdateFactory.newLatLngZoom(startPoint, // getShowRouteZoom())); } /** * 移动镜头到当前的视角。 * @since V2.1.0 */ public void zoomToSpan() { if (startPoint != null) { if (mAMap == null) return; try { LatLngBounds bounds = getLatLngBounds(); mAMap.animateCamera(CameraUpdateFactory .newLatLngBounds(bounds, 50)); } catch (Throwable e) { e.printStackTrace(); } } } protected LatLngBounds getLatLngBounds() { LatLngBounds.Builder b = LatLngBounds.builder(); b.include(new LatLng(startPoint.latitude, startPoint.longitude)); b.include(new LatLng(endPoint.latitude, endPoint.longitude)); return b.build(); } /** * 路段节点图标控制显示接口。 * @param visible true为显示节点图标,false为不显示。 * @since V2.3.1 */ public void setNodeIconVisibility(boolean visible) { try { nodeIconVisible = visible; if (this.stationMarkers != null && this.stationMarkers.size() > 0) { for (int i = 0; i < this.stationMarkers.size(); i++) { this.stationMarkers.get(i).setVisible(visible); } } } catch (Throwable e) { e.printStackTrace(); } } protected void addStationMarker(MarkerOptions options) { if(options == null) { return; } Marker marker = mAMap.addMarker(options); if(marker != null) { stationMarkers.add(marker); } } protected void addPolyLine(PolylineOptions options) { if(options == null) { return; } Polyline polyline = mAMap.addPolyline(options); if(polyline != null) { allPolyLines.add(polyline); } } protected float getRouteWidth() { return 18f; } protected int getWalkColor() { return Color.parseColor("#6db74d"); } /** * 自定义路线颜色。 * return 自定义路线颜色。 * @since V2.2.1 */ protected int getBusColor() { return Color.parseColor("#537edc"); } protected int getDriveColor() { return Color.parseColor("#537edc"); } // protected int getShowRouteZoom() { // return 15; // } } ================================================ FILE: Android/app/src/debug/java/com/didichuxing/doraemondemo/amap/mockroute/BearingUtils.java ================================================ package com.didichuxing.doraemondemo.amap.mockroute; /** * changzuozhen * 2021年 04月 20日 */ public final class BearingUtils { private BearingUtils() { } public static double bearing(double lat1, double lon1, double lat2, double lon2) { double longitude1 = lon1; double longitude2 = lon2; double latitude1 = Math.toRadians(lat1); double latitude2 = Math.toRadians(lat2); double longDiff = Math.toRadians(longitude2 - longitude1); double y = Math.sin(longDiff) * Math.cos(latitude2); double x = Math.cos(latitude1) * Math.sin(latitude2) - Math.sin(latitude1) * Math.cos(latitude2) * Math.cos(longDiff); return (Math.toDegrees(Math.atan2(y, x)) + 360) % 360; } } ================================================ FILE: Android/app/src/debug/java/com/didichuxing/doraemondemo/amap/mockroute/LogUtils.java ================================================ package com.didichuxing.doraemondemo.amap.mockroute; import android.os.Handler; import android.os.HandlerThread; import android.util.Log; /** * 日志控制 */ public final class LogUtils { public static boolean S_OPEN_LOG = true; static volatile long sCurrentForSpendTimeLog = System.currentTimeMillis(); static volatile int sSpendTimeIndex = 0; private static final String TAG = "_AndyTest_"; private static HandlerThread mBackThread; private static Handler sHandler; static HandlerThread getBackThread() { if (mBackThread == null) { synchronized (LogUtils.class) { if (mBackThread == null) { mBackThread = new HandlerThread("LogUtils Thread"); mBackThread.start(); } } } return mBackThread; } static Handler getHandler() { if (sHandler == null) { synchronized (LogUtils.class) { if (sHandler == null) { sHandler = new Handler(getBackThread().getLooper()); } } } return sHandler; } private LogUtils() { } public static void d(String logKey, CharSequence msg) { d(logKey, msg, 2); } public static void d(String logKey, final CharSequence msg, final Object... args) { d(logKey, msg.toString() + args, 2); } /** * @param stackIndex 1:当前位置,2:上级栈位置,0:logcat 的位置(没有意义) */ public static void d(String logKey, CharSequence msg, int stackIndex) { if (S_OPEN_LOG) { StackTraceElement ste = new Throwable().getStackTrace()[stackIndex]; String log = build(msg, ste); Log.d(logKey, log); } } public static void d(CharSequence msg) { if (S_OPEN_LOG) { StackTraceElement ste = new Throwable().getStackTrace()[1]; String log = build(msg, ste); Log.d(TAG, log); } } public static void i(String logKey, CharSequence msg, int stackIndex) { if (S_OPEN_LOG) { StackTraceElement ste = new Throwable().getStackTrace()[stackIndex]; String log = build(msg, ste); Log.i(logKey, log); // Log.i(TAG, "[" + logKey + "]" + log); } } public static void i(String logKey, CharSequence msg) { i(logKey, msg, 2); } public static void v(String logKey, CharSequence msg, int stackIndex) { if (S_OPEN_LOG) { StackTraceElement ste = new Throwable().getStackTrace()[stackIndex]; String log = build(msg, ste); Log.v(logKey, log); // Log.v(TAG, "[" + logKey + "]" + log); } } public static void v(String logKey, CharSequence msg) { v(logKey, msg, 2); } public static void w(String logKey, CharSequence msg) { if (S_OPEN_LOG) { StackTraceElement ste = new Throwable().getStackTrace()[1]; String log = build(logKey, msg, ste); Log.w(logKey, log); // Log.w(TAG, "[" + logKey + "]" + log); } } /** * 打印error级别的log * * @param tag tag标签 */ public static void e(String tag, Throwable tr) { if (S_OPEN_LOG) { StackTraceElement ste = new Throwable().getStackTrace()[1]; String log = build(tag, "", ste, tr); Log.e(tag, log, tr); // Log.e(tag, "[" + Thread.currentThread().getId() + "]" + tr.getMessage(), tr); } } public static void e(String logKey, CharSequence msg) { if (S_OPEN_LOG) { StackTraceElement ste = new Throwable().getStackTrace()[1]; String log = build(logKey, msg, ste); Log.e(logKey, log); } } public static void e(String logKey, CharSequence msg, Throwable e) { if (S_OPEN_LOG) { StackTraceElement ste = new Throwable().getStackTrace()[1]; String log = build(logKey, msg, ste); Log.d(logKey, log, e); // Log.e(TAG, log, e); } } /** * 打印调用栈信息 */ public static void t(String logKey, CharSequence msg) { if (S_OPEN_LOG) { StackTraceElement[] stackTrace = new Throwable().getStackTrace(); StringBuilder sb = new StringBuilder(); if (stackTrace.length > 0) { sb.append(build(msg, stackTrace[1])); sb.append('\n'); } else { sb.append(msg); } for (int i = 2; i < stackTrace.length; i++) { sb.append(stackTrace[i]); sb.append('\n'); } Log.i(logKey, sb.toString()); } } public static void timeSinceLast() { LogUtils.d(TAG, "⚠️" + (sSpendTimeIndex++) + "spend:" + (System.currentTimeMillis() - sCurrentForSpendTimeLog) + " ", 2); sCurrentForSpendTimeLog = System.currentTimeMillis(); } public static String getLineText(String msg, int stackIndex) { if (S_OPEN_LOG) { StackTraceElement ste = new Throwable().getStackTrace()[stackIndex]; String log = build(msg, ste); return log; } else { return msg; } } public static String getLineText(String msg) { if (S_OPEN_LOG) { int stackIndex = 1; StackTraceElement ste = new Throwable().getStackTrace()[stackIndex]; String log = build(msg, ste); return log; } else { return msg; } } /** * 制作打log位置的文件名与文件行号详细信息 */ private static String build(CharSequence log, StackTraceElement ste) { StringBuilder buf = new StringBuilder(); buf.append("[").append(Thread.currentThread().getId()).append("]"); if (ste.isNativeMethod()) { buf.append("(Native Method)"); } else { CharSequence fileName = ste.getFileName(); if (fileName == null) { buf.append("(Unknown Source)"); } else { int lineNum = ste.getLineNumber(); buf.append('('); buf.append(fileName); if (lineNum >= 0) { buf.append(':'); buf.append(lineNum); } buf.append("):"); } } buf.append(log); return buf.toString(); } private static String build(String logKey, CharSequence msg, StackTraceElement ste) { StringBuilder sb = new StringBuilder(); sb.append("[").append(logKey).append("]").append(build(msg, ste)); return sb.toString(); } private static String build(String logKey, CharSequence msg, StackTraceElement ste, Throwable e) { StringBuilder sb = new StringBuilder(); sb.append("[").append(logKey).append("]").append(ste.toString()).append(":").append(msg).append("\r\n").append("e:").append(e.getMessage()); return sb.toString(); } } ================================================ FILE: Android/app/src/debug/java/com/didichuxing/doraemondemo/amap/mockroute/MockGPSTaskData.java ================================================ package com.didichuxing.doraemondemo.amap.mockroute; import java.util.ArrayList; /** * changzuozhen * 2021年 04月 20日 */ public class MockGPSTaskData { private static final String TAG = "TaskInfoData"; public double start_lng; public double start_lat; public double end_lng; public double end_lat; public ArrayList mockGPSItems; @Override public String toString() { return "任务 轨迹点=" + mockGPSItems.size() + ", start_lat,lng= " + start_lat + "," + start_lng + ", end_lat,lng= " + end_lat + "," + end_lng; } public static class MockGPSItem { public Double lng; public Double lat; public Long time; public Float accuracy; public Double speed; public Long bearing; @Override public String toString() { return "TraceItem{" + "lng=" + lng + ", lat=" + lat + ", time=" + time + ", accuracy=" + accuracy + ", speed=" + speed + ", bearing=" + bearing + '}'; } public MockGPSItem setLng(Double lng) { this.lng = lng; return this; } public MockGPSItem setLat(Double lat) { this.lat = lat; return this; } public MockGPSItem setTime(Long time) { this.time = time; return this; } public MockGPSItem setAccuracy(Float accuracy) { this.accuracy = accuracy; return this; } public MockGPSItem setSpeed(Double speed) { this.speed = speed; return this; } public MockGPSItem setBearing(Long bearing) { this.bearing = bearing; return this; } } public static void modifyBearing(ArrayList mockGPSItems) { int compairStep = 5; if (mockGPSItems != null && mockGPSItems.size() > compairStep) { // LogUtils.d(TAG, "⚠️modifyBearing() called with: mockGPSItems = [" + mockGPSItems.size() + "]"); for (int i = 0; i < mockGPSItems.size() - compairStep; i++) { MockGPSItem a = mockGPSItems.get(i); MockGPSItem b = mockGPSItems.get(i + compairStep); // LogUtils.d(TAG, "⚠️modifyBearing() called with:" + // "bearing = [" + a.bearing + "] " + // "bearing = [" + (long) BearingUtils.bearing(a.lat, a.lng, b.lat, b.lng) + "]"); a.bearing = (long) BearingUtils.bearing(a.lat, a.lng, b.lat, b.lng); } } } } ================================================ FILE: Android/app/src/debug/java/com/didichuxing/doraemondemo/amap/mockroute/MockGPSTaskManager.java ================================================ package com.didichuxing.doraemondemo.amap.mockroute; import android.location.Location; import android.location.LocationManager; import androidx.annotation.NonNull; import com.amap.api.maps.AMapUtils; import com.amap.api.maps.model.LatLng; import com.amap.api.navi.model.AMapNaviPath; import com.amap.api.navi.model.NaviLatLng; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Iterator; import java.util.concurrent.TimeUnit; import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.functions.Consumer; import io.reactivex.functions.Function; import io.reactivex.functions.Predicate; import io.reactivex.schedulers.Schedulers; /** * 使用Dokit位置mock * changzuozhen * 2021年 04月 20日 */ public class MockGPSTaskManager { private static final String TAG = "TaskMockManager"; public static int PERIOD_LEVEL_MIN = 1; public static int PERIOD_LEVEL_MAX = 20; public static int PERIOD_UNIT = 50; private static int mPeriodLevel = PERIOD_LEVEL_MAX; private Long mProgressIndex = 0L; private Long mProgressIndexMax = 0L; private boolean mPause = false; private boolean mSkip = false; MockGPSTaskData taskInfoData; public static MockGPSTaskManager sMockGPSTaskManager; public MockGPSTaskManager(MockGPSTaskData mockGPSTaskData) { this.taskInfoData = mockGPSTaskData; mProgressIndexMax = Long.valueOf(mockGPSTaskData.mockGPSItems.size() - 1); } public Observable startGpsMockTask() { return Observable.interval(0, PERIOD_UNIT, TimeUnit.MILLISECONDS, Schedulers.io()) // Observable.interval(0, PERIOD_UNIT * getPeriodLevel(), TimeUnit.MILLISECONDS, Schedulers.io()) // Observable.timer(PERIOD_UNIT * getPeriodLevel(), TimeUnit.MICROSECONDS) // .subscribeOn(Schedulers.io()) .filter(new Predicate() { @Override public boolean test(Long aLong) throws Exception { boolean periodMatched = (aLong % getPeriodLevel()) == 0; return checkMockLocationState() && periodMatched; // return checkMockLocationState(); } }) .map(new Function() { @Override public Long apply(@NonNull Long aLong) throws Exception { return mProgressIndex++; } }) .takeWhile(new Predicate() { @Override public boolean test(Long aLong) throws Exception { return !mSkip && taskInfoData != null && aLong < taskInfoData.mockGPSItems.size(); } }) .observeOn(AndroidSchedulers.mainThread()) .map(new Function() { private Location mLastLocation; @Override public Location apply(Long aLong) throws Exception { MockGPSTaskData.MockGPSItem mockGPSItem = taskInfoData.mockGPSItems.get(aLong.intValue()); // 轨迹模拟 Location location = new Location(LocationManager.GPS_PROVIDER); location.setLatitude(mockGPSItem.lat); location.setLongitude(mockGPSItem.lng); location.setAccuracy(mockGPSItem.accuracy.floatValue()); location.setSpeed(mockGPSItem.speed.floatValue()); location.setBearing(mockGPSItem.bearing); LogUtils.v(TAG, "⚠️模拟定位:" + location + " index: " + aLong + " TaskInfoData: " + taskInfoData); float calculateLineDistance = (location == null || mLastLocation == null) ? -1 : AMapUtils.calculateLineDistance(new LatLng(mLastLocation.getLatitude(), mLastLocation.getLongitude()), new LatLng(location.getLatitude(), location.getLongitude())); if (calculateLineDistance < 0) { LogUtils.v(TAG, "⚠️模拟定位:" + location + " index: " + aLong + " TaskInfoData: " + taskInfoData); } else if (calculateLineDistance > 100) { LogUtils.w(TAG, "⚠️模拟定位: 跳动距离:" + calculateLineDistance + " " + location + " index: " + aLong + " TaskInfoData: " + taskInfoData); } else { LogUtils.v(TAG, "⚠️模拟定位: 跳动距离:" + calculateLineDistance + " " + location + " index: " + aLong + " TaskInfoData: " + taskInfoData); } mLastLocation = location; //GpsMockManager.getInstance().mockLocationWithNotify(location); return location; } }) .doOnError(new Consumer() { @Override public void accept(Throwable throwable) throws Exception { LogUtils.d(TAG, "mock 子任务异常 " + taskInfoData + " " + throwable + "]"); } }); } /** * 根据高德导航的轨迹数据进行轨迹模拟 * * @param naviRouteInfo * @return */ @Nullable public static Observable startGpsMockTask(@Nullable AMapNaviPath naviRouteInfo) { if (naviRouteInfo != null && naviRouteInfo.getCoordList() != null) { MockGPSTaskData mockGPSTaskData = new MockGPSTaskData(); mockGPSTaskData.mockGPSItems = new ArrayList(); Iterator cordListIt = naviRouteInfo.getCoordList().iterator(); while (cordListIt.hasNext()) { NaviLatLng latLng = (NaviLatLng) cordListIt.next(); MockGPSTaskData.MockGPSItem mockGPSItem = new MockGPSTaskData.MockGPSItem(); mockGPSTaskData.mockGPSItems.add(mockGPSItem.setLat(latLng.getLatitude()).setLng(latLng.getLongitude()).setTime(System.currentTimeMillis()).setAccuracy(10.0F).setBearing(0L).setSpeed(15.0D)); } MockGPSTaskData.modifyBearing(mockGPSTaskData.mockGPSItems); MockGPSTaskManager mockGPSTaskManager = new MockGPSTaskManager(mockGPSTaskData); if (sMockGPSTaskManager != null) { sMockGPSTaskManager.setSkip(true); } sMockGPSTaskManager = mockGPSTaskManager; return mockGPSTaskManager.startGpsMockTask(); } else { return null; } } /** * 暂停当前任务 */ public void pause() { mPause = true; } /** * 恢复执行当前任务 */ public void resume() { mPause = false; } /** * 跳过当前任务 * * @param skip * @return */ public MockGPSTaskManager setSkip(boolean skip) { this.mSkip = skip; return this; } /** * 获取时间间隔等级 * * @return */ public static int getPeriodLevel() { return mPeriodLevel; } /** * 时间间隔等级 * 1 --- PERIOD_UNIT * 1 = 50 * 10 --- PERIOD_UNIT * 10 = 500 * 20 --- PERIOD_UNIT * 20 = 1000 */ public static void setPeriodLevel(int periodLevel) { if (periodLevel < 1) { mPeriodLevel = 1; } else { mPeriodLevel = periodLevel; } LogUtils.v(TAG, "setPeriodLevel() called with: periodLevel = [" + mPeriodLevel + "]"); } public boolean checkMockLocationState() { // return !mPause && mState > 5; return !mPause; } public Long getSeekProgress() { return mProgressIndex; } public MockGPSTaskManager seekProgress(Long progress) { if (progress < 0) { mProgressIndex = 0L; } else if (progress > mProgressIndexMax) { this.mProgressIndex = mProgressIndexMax; } else { this.mProgressIndex = progress; } return this; } public boolean completed() { return mProgressIndex >= mProgressIndexMax; } } ================================================ FILE: Android/app/src/main/AndroidManifest.xml ================================================ ================================================ FILE: Android/app/src/main/java/com/didichuxing/doraemondemo/AopTest.java ================================================ package com.didichuxing.doraemondemo; /** * ================================================ * 作 者:jint(金台) * 版 本:1.0 * 创建日期:2020/4/22-11:38 * 描 述: * 修订历史: * ================================================ */ public class AopTest { public void getDoKit() { // OkHttpHook.addDoKitIntercept(this); } } ================================================ FILE: Android/app/src/main/java/com/didichuxing/doraemondemo/BaseStatusBarActivity.java ================================================ package com.didichuxing.doraemondemo; import android.app.Activity; import android.os.Build; import android.os.Bundle; import android.view.View; import android.view.WindowManager; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; /** * didi Create on 2022/5/25 . *

* Copyright (c) 2022/5/25 by didiglobal.com. * * @author zhangjun * @version 1.0 * @Date 2022/5/25 5:55 下午 * @Description 用一句话说明文件功能 */ public class BaseStatusBarActivity extends AppCompatActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); preUpdateStatusBar(this); } private void preUpdateStatusBar(Activity activity) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); activity.getWindow().setStatusBarColor(getResources().getColor(R.color.colorPrimary)); } } private void setLightStatusBar() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { getWindow().getDecorView().setSystemUiVisibility( View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); } } private void setNotLightStatusBar() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { getWindow().getDecorView().setSystemUiVisibility( View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN ); } } } ================================================ FILE: Android/app/src/main/java/com/didichuxing/doraemondemo/DataBaseActivity.kt ================================================ package com.didichuxing.doraemondemo import android.os.Bundle import android.view.View import androidx.appcompat.app.AppCompatActivity import com.blankj.utilcode.util.ToastUtils import com.didichuxing.doraemondemo.db.PersonDBHelper import kotlinx.coroutines.* class DataBaseActivity : AppCompatActivity() { companion object { val TAG = "SecondActivity" } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_second) findViewById(R.id.tv).setOnClickListener { // Create Person encrypted database CoroutineScope(Dispatchers.Main).launch { val job = async(Dispatchers.IO) { insertPersonDB() } val success = job.await() ToastUtils.showShort("插入数据成功") } ToastUtils.showShort("开始插入数据") } } /** * 只非ui编程中执行操作 */ private fun insertPersonDB(): Boolean { val personDBHelper = PersonDBHelper(applicationContext) if (personDBHelper.count() == 0) { for (i in 0..99) { val firstName = "${PersonDBHelper.PERSON_COLUMN_FIRST_NAME}_$i" val lastName = "${PersonDBHelper.PERSON_COLUMN_LAST_NAME}_$i" val address = "${PersonDBHelper.PERSON_COLUMN_ADDRESS}_$i" personDBHelper.insertPerson(firstName, lastName, address) } } return true } } ================================================ FILE: Android/app/src/main/java/com/didichuxing/doraemondemo/EmptyActivity.kt ================================================ package com.didichuxing.doraemondemo import android.os.Bundle import android.view.View import androidx.appcompat.app.AppCompatActivity import com.blankj.utilcode.util.ToastUtils import com.didichuxing.doraemondemo.db.PersonDBHelper import kotlinx.coroutines.* class EmptyActivity : AppCompatActivity() { companion object { val TAG = "EmptyActivity" } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_empty) } } ================================================ FILE: Android/app/src/main/java/com/didichuxing/doraemondemo/MainDoKitActivity.kt ================================================ package com.didichuxing.doraemondemo import android.app.Activity import android.content.Intent import android.os.Bundle import android.util.Log import android.view.ViewGroup import androidx.core.view.children import com.didichuxing.doraemondemo.mc.MCActivity import com.didichuxing.doraemondemo.module.CrashTest import com.didichuxing.doraemondemo.module.DoKitItemView import com.didichuxing.doraemondemo.module.MethodCostTest import com.didichuxing.doraemondemo.module.bigbitmap.BigBitmapActivity import com.didichuxing.doraemondemo.module.db.DataBaseTest import com.didichuxing.doraemondemo.module.http.FileUploadTest import com.didichuxing.doraemondemo.module.http.OkHttpMock import com.didichuxing.doraemondemo.module.http.RetrofitMock import com.didichuxing.doraemondemo.module.http.URLConnectionMock import com.didichuxing.doraemondemo.module.leak.LeakActivity import com.didichuxing.doraemondemo.old.MainDebugActivityOkhttpV3 import com.didichuxing.doraemonkit.DoKit import com.didichuxing.doraemonkit.util.ToastUtils /** * didi Create on 2022/5/25 . * * Copyright (c) 2022/5/25 by didiglobal.com. * * @author zhangjun * @version 1.0 * @Date 2022/5/25 6:05 下午 * @Description 用一句话说明文件功能 */ class MainDoKitActivity : BaseStatusBarActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_dokit_main) val all: ViewGroup = findViewById(R.id.all) all.children.forEach { if (it is DoKitItemView) { val item = it as DoKitItemView item.setOnClickListener { onItemClick(item, item.itemText) } } } } private fun onItemClick(itemView: DoKitItemView, text: String) { Log.i("TEST", "onItemClick :$text") when (text) { //工具入口 "显示/隐藏快捷入口" -> { showHideDoKit(); } "打开工具窗口" -> { DoKit.showToolPanel() } //平台工具 "数据Mock测试" -> { startActivity(Intent(this, EmptyActivity::class.java)) } "OkHttp 模拟请求" -> { OkHttpMock.test() } "UrlConnection 模拟请求" -> { URLConnectionMock.get("https://wanandroid.com/user_article/list/0/json") } "retrofit 模拟请求" -> { RetrofitMock.test() } "一机多控测试" -> { startActivity(Intent(this, MCActivity::class.java)) } "自动化测试" -> { startActivity(Intent(this, MCActivity::class.java)) } //常用工具 "日志测试" -> { startActivity(Intent(this, EmptyActivity::class.java)) } "跳转其他Activity" -> { startActivity(Intent(this, EmptyActivity::class.java)) } "系统:WebView" -> { startActivity(Intent(this, WebViewSystemActivity::class.java)) } "X5:WebView" -> { startActivity(Intent(this, WebViewX5Activity::class.java)) } //LBS "位置模拟" -> { startActivity(Intent(this, EmptyActivity::class.java)) } "路径模拟" -> { startActivity(Intent(this, EmptyActivity::class.java)) } //性能工具 "模拟内存泄漏" -> { startActivity(Intent(this, LeakActivity::class.java)) } "模拟耗时函数调用" -> { MethodCostTest.test() } "崩溃模拟" -> { CrashTest.test() } "创建数据库" -> { DataBaseTest.test() } "文件上传模拟" -> { FileUploadTest.requestByFile(getActivity(), filesDir, true) } "文件下载模拟" -> { FileUploadTest.requestByFile(getActivity(), filesDir, false) } "大图检测模拟" -> { startActivity(Intent(this, BigBitmapActivity::class.java)) } //视觉工具 "取色器测试" -> { startActivity(Intent(this, EmptyActivity::class.java)) } "标尺对齐测试" -> { startActivity(Intent(this, EmptyActivity::class.java)) } //其他工具 "旧版页面入口" -> { startActivity(Intent(this, MainDebugActivityOkhttpV3::class.java)) } else -> { ToastUtils.showShort("$text") } } } private fun getActivity(): Activity { return this } private fun showHideDoKit() { if (DoKit.isMainIconShow) { DoKit.hide() } else { DoKit.show() } } } ================================================ FILE: Android/app/src/main/java/com/didichuxing/doraemondemo/WebViewSystemActivity.kt ================================================ package com.didichuxing.doraemondemo import android.annotation.SuppressLint import android.os.Build import android.os.Bundle import android.util.Log import android.webkit.* import androidx.annotation.RequiresApi import androidx.appcompat.app.AppCompatActivity /** * Created by jintai on 2018/11/13. */ class WebViewSystemActivity : AppCompatActivity() { val TAG = "WebViewActivity" lateinit var mWebView: WebView val url = "https://xingyun.xiaojukeji.com/docs/dokit" override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_normal_webview) mWebView = findViewById(R.id.normal_web_view) initWebView(mWebView) mWebView.loadUrl(url) } @SuppressLint("JavascriptInterface") private fun initWebView(webView: WebView) { val webSettings: WebSettings = webView.settings webSettings.pluginState = WebSettings.PluginState.ON webSettings.javaScriptEnabled = true webSettings.allowFileAccess = false webSettings.loadsImagesAutomatically = true webSettings.useWideViewPort = true webSettings.builtInZoomControls = false webSettings.defaultTextEncodingName = "UTF-8" webSettings.domStorageEnabled = true webSettings.cacheMode = WebSettings.LOAD_DEFAULT webSettings.javaScriptCanOpenWindowsAutomatically = false webSettings.allowFileAccessFromFileURLs = true webSettings.allowUniversalAccessFromFileURLs = true if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) { webSettings.setRenderPriority(WebSettings.RenderPriority.HIGH) } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { WebView.setWebContentsDebuggingEnabled(true) } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { webSettings.mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW } if (Build.VERSION.SDK_INT > Build.VERSION_CODES.GINGERBREAD_MR1 && Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) { webView.removeJavascriptInterface("searchBoxJavaBridge_") webView.removeJavascriptInterface("accessibilityTraversal") webView.removeJavascriptInterface("accessibility") } webView.webViewClient = object : WebViewClient() { override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean { view.loadUrl(url) return true } @RequiresApi(Build.VERSION_CODES.LOLLIPOP) override fun shouldInterceptRequest( view: WebView?, request: WebResourceRequest? ): WebResourceResponse? { return super.shouldInterceptRequest(view, request) } } webView.webChromeClient = object : WebChromeClient() { override fun onConsoleMessage(consoleMessage: ConsoleMessage?): Boolean { val message = consoleMessage!!.message() val lineNumber = consoleMessage.lineNumber() val sourceID = consoleMessage.sourceId() val messageLevel = consoleMessage.message() Log.i( TAG, String.format( "[%s] sourceID: %s lineNumber: %n message: %s", messageLevel, sourceID, lineNumber, message ) ) //Log.i(TAG, "consoleMessage===>${consoleMessage?.message()}") return super.onConsoleMessage(consoleMessage) } } } override fun onBackPressed() { if (mWebView.canGoBack()) { mWebView.goBack() } else { super.onBackPressed() } } } ================================================ FILE: Android/app/src/main/java/com/didichuxing/doraemondemo/WebViewX5Activity.kt ================================================ package com.didichuxing.doraemondemo import android.annotation.SuppressLint import android.os.Build import android.os.Bundle import androidx.annotation.RequiresApi import androidx.appcompat.app.AppCompatActivity import com.didichuxing.doraemonkit.util.LogHelper import com.tencent.smtt.export.external.interfaces.ConsoleMessage import com.tencent.smtt.export.external.interfaces.WebResourceRequest import com.tencent.smtt.export.external.interfaces.WebResourceResponse import com.tencent.smtt.sdk.WebChromeClient import com.tencent.smtt.sdk.WebSettings import com.tencent.smtt.sdk.WebView import com.tencent.smtt.sdk.WebViewClient /** * Created by jintai on 2018/11/13. */ class WebViewX5Activity : AppCompatActivity() { val TAG = "WebViewActivity" lateinit var mWebView: WebView val url = "https://xingyun.xiaojukeji.com/docs/dokit" override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_x5_webview) mWebView = findViewById(R.id.x5_web_view) initWebView(mWebView) mWebView.loadUrl(url) } @SuppressLint("JavascriptInterface") private fun initWebView(webView: WebView) { val webSettings: WebSettings = webView.settings webSettings.pluginState = WebSettings.PluginState.ON webSettings.javaScriptEnabled = true webSettings.allowFileAccess = false webSettings.loadsImagesAutomatically = true webSettings.useWideViewPort = true webSettings.builtInZoomControls = false webSettings.defaultTextEncodingName = "UTF-8" webSettings.domStorageEnabled = true webSettings.cacheMode = WebSettings.LOAD_DEFAULT webSettings.javaScriptCanOpenWindowsAutomatically = false webSettings.setAllowFileAccessFromFileURLs(true) webSettings.setAllowUniversalAccessFromFileURLs(true) if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) { webSettings.setRenderPriority(WebSettings.RenderPriority.HIGH) } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { WebView.setWebContentsDebuggingEnabled(true) } if (Build.VERSION.SDK_INT > Build.VERSION_CODES.GINGERBREAD_MR1 && Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) { webView.removeJavascriptInterface("searchBoxJavaBridge_") webView.removeJavascriptInterface("accessibilityTraversal") webView.removeJavascriptInterface("accessibility") } webView.webViewClient = object : WebViewClient() { override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean { view.loadUrl(url) return true } @RequiresApi(Build.VERSION_CODES.LOLLIPOP) override fun shouldInterceptRequest( view: WebView?, request: WebResourceRequest? ): WebResourceResponse? { return super.shouldInterceptRequest(view, request) } } webView.webChromeClient = object : WebChromeClient() { override fun onConsoleMessage(consoleMessage: ConsoleMessage?): Boolean { LogHelper.i(TAG, "consoleMessage===>${consoleMessage?.message()}") return super.onConsoleMessage(consoleMessage) } } } override fun onBackPressed() { if (mWebView.canGoBack()) { mWebView.goBack() } else { super.onBackPressed() } } } ================================================ FILE: Android/app/src/main/java/com/didichuxing/doraemondemo/amap/AMapRouterFragment.kt ================================================ package com.didichuxing.doraemondemo.amap import android.os.Bundle import android.view.View import android.widget.TextView import com.amap.api.maps.AMap import com.amap.api.maps.MapView import com.amap.api.maps.model.LatLng import com.amap.api.maps.model.MyLocationStyle import com.amap.api.maps.model.Poi import com.amap.api.navi.* import com.amap.api.navi.enums.PathPlanningStrategy import com.amap.api.navi.model.AMapNaviLocation import com.amap.api.navi.model.NaviLatLng import com.didichuxing.doraemondemo.R import com.didichuxing.doraemondemo.comm.CommBaseFragment /** * ================================================ * 作 者:jint(金台) * 版 本:1.0 * 创建日期:3/11/21-14:51 * 描 述: * 修订历史: * ================================================ */ class AMapRouterFragment : CommBaseFragment() { private var mDefaultNaviListener: DefaultNaviListener? = null private lateinit var mAmap: AMap private lateinit var mapView: MapView private lateinit var mAMapNavi: AMapNavi private val mStartPoint = NaviLatLng(30.29659, 120.081127) private val mEndPoint = NaviLatLng(30.296793, 121.07527) override fun initActivityTitle(): String { return "高德路径规划" } override fun getLayoutId(): Int { return R.layout.fragment_amap } override fun initView(savedInstanceState: Bundle?) { mapView = findViewById(R.id.amap_view) mapView.onCreate(savedInstanceState) mAmap = mapView.map val startNavi = findViewById(R.id.tv_start) startNavi.setOnClickListener { val params = AmapNaviParams( Poi("西溪谷", LatLng(mStartPoint.latitude, mStartPoint.longitude), ""), null, Poi("管理学院创新大楼", LatLng(mEndPoint.latitude, mEndPoint.longitude), ""), AmapNaviType.DRIVER ) params.setUseInnerVoice(true) AmapNaviPage.getInstance().showRouteActivity(activity, params, object : INaviInfoCallback { override fun onInitNaviFailure() { } override fun onGetNavigationText(p0: String?) { } override fun onLocationChange(p0: AMapNaviLocation?) { } override fun onArriveDestination(p0: Boolean) { } override fun onStartNavi(p0: Int) { } override fun onCalculateRouteSuccess(p0: IntArray?) { } override fun onCalculateRouteFailure(p0: Int) { } override fun onStopSpeaking() { } override fun onReCalculateRoute(p0: Int) { } override fun onExitPage(p0: Int) { } override fun onStrategyChanged(p0: Int) { } override fun onArrivedWayPoint(p0: Int) { } override fun onMapTypeChanged(p0: Int) { } override fun onNaviDirectionChanged(p0: Int) { } override fun onDayAndNightModeChanged(p0: Int) { } override fun onBroadcastModeChanged(p0: Int) { } override fun onScaleAutoChanged(p0: Boolean) { } override fun getCustomMiddleView(): View? { return null } override fun getCustomNaviView(): View? { return null } override fun getCustomNaviBottomView(): View? { return null } }) } initAMapLocation() } /** * 初始化高德地图的定位 */ private fun initAMapLocation() { mAmap.minZoomLevel = 10.0f //初始化定位蓝点样式类myLocationStyle.myLocationType(MyLocationStyle.LOCATION_TYPE_LOCATION_ROTATE);//连续定位、且将视角移动到地图中心点,定位点依照设备方向旋转,并且会跟随设备移动。(1秒1次定位)如果不设置myLocationType,默认也会执行此种模式。 val myLocationStyle = MyLocationStyle() //设置连续定位模式下的定位间隔,只在连续定位模式下生效,单次定位模式下不会生效。单位为毫秒 myLocationStyle.interval(1000L) // myLocationStyle.myLocationType(MyLocationStyle.LOCATION_TYPE_LOCATION_ROTATE_NO_CENTER) myLocationStyle.myLocationType(MyLocationStyle.LOCATION_TYPE_FOLLOW) myLocationStyle.myLocationIcon( com.amap.api.maps.model.BitmapDescriptorFactory.fromResource( R.mipmap.ic_navi_map_gps_locked ) ) ////设置是否显示定位小蓝点,用于满足只想使用定位,不想使用定位小蓝点的场景,设置false以后图面上不再有定位蓝点的概念,但是会持续回调位置信息。 myLocationStyle.showMyLocation(true) mAmap.myLocationStyle = myLocationStyle //设置定位蓝点的Style //aMap.getUiSettings().setMyLocationButtonEnabled(true);设置默认定位按钮是否显示,非必需设置。 // 设置为true表示启动显示定位蓝点,false表示隐藏定位蓝点并不进行定位,默认是false。 mAmap.isMyLocationEnabled = true //规划路径 mAMapNavi = AMapNavi.getInstance(activity?.application) val startList = mutableListOf() startList.add(mStartPoint) val endList = mutableListOf() endList.add(mEndPoint) mAMapNavi.calculateDriveRoute( startList, endList, null, PathPlanningStrategy.DRIVING_MULTIPLE_ROUTES_DEFAULT ) mAMapNavi.addAMapNaviListener(activity?.application?.let { mDefaultNaviListener = DefaultNaviListener(mAmap, mAMapNavi, it) mDefaultNaviListener }) } override fun onResume() { super.onResume() mapView.onResume() } override fun onPause() { super.onPause() mapView.onPause() } override fun onDestroy() { super.onDestroy() mDefaultNaviListener?.onDestroy() mapView.onDestroy() } override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) mapView.onSaveInstanceState(outState) } } ================================================ FILE: Android/app/src/main/java/com/didichuxing/doraemondemo/amap/DefaultNaviListener.kt ================================================ package com.didichuxing.doraemondemo.amap import android.content.Context import android.util.Log import com.amap.api.maps.AMap import com.amap.api.maps.AMapUtils import com.amap.api.maps.model.LatLng import com.amap.api.navi.AMapNavi import com.amap.api.navi.AMapNaviListener import com.amap.api.navi.enums.NaviType import com.amap.api.navi.model.* import io.reactivex.disposables.Disposable /** * ================================================ * 作 者:jint(金台) * 版 本:1.0 * 创建日期:2/25/21-20:00 * 描 述: * 修订历史: * ================================================ */ class DefaultNaviListener(val mAMap: AMap, val mAMapNavi: AMapNavi, val context: Context) : AMapNaviListener { // private var mNaviRouteOverlay: NaviRouteOverlay? = null // // init { // mNaviRouteOverlay = NaviRouteOverlay(mAMap, null) // } private var disp: Disposable? = null private var mNaviLocation: AMapNaviLocation? = null companion object { const val TAG = "DefaultNaviListener" } override fun onInitNaviFailure() { } override fun onInitNaviSuccess() { } override fun onStartNavi(p0: Int) { } override fun onTrafficStatusUpdate() { } /** * 改变位置时自动回调 */ override fun onLocationChange(location: AMapNaviLocation?) { val calculateLineDistance: Float = if (mNaviLocation == null || location == null) -1f else AMapUtils.calculateLineDistance( LatLng(mNaviLocation!!.coord.latitude, mNaviLocation!!.coord.longitude), LatLng(location.coord.latitude, location.coord.longitude) ) if (calculateLineDistance < 0) { Log.v(TAG, "⚠️高德定位:" + location.getString()) } else if (calculateLineDistance > 50) { Log.w(TAG, "⚠️高德定位:跳动距离:" + calculateLineDistance + " " + location.getString()) } else { Log.v(TAG, "⚠️高德定位:跳动距离:" + calculateLineDistance + " " + location.getString()) } mNaviLocation = location } private fun AMapNaviLocation?.getString(): String { return if (this != null) "lat,lng:${coord?.latitude},:${coord?.longitude}, 精度:$accuracy, 速度:$speed, 方向:$bearing, 海拔:$altitude, 时间:$time" else "null" } override fun onGetNavigationText(p0: Int, p1: String?) { } override fun onGetNavigationText(p0: String?) { } override fun onEndEmulatorNavi() { } override fun onArriveDestination() { } override fun onCalculateRouteFailure(p0: Int) { } override fun onCalculateRouteFailure(p0: AMapCalcRouteResult?) { } override fun onReCalculateRouteForYaw() { } override fun onReCalculateRouteForTrafficJam() { } override fun onArrivedWayPoint(p0: Int) { } override fun onGpsOpenStatus(p0: Boolean) { } override fun onNaviInfoUpdate(p0: NaviInfo?) { } override fun updateCameraInfo(p0: Array?) { } override fun updateIntervalCameraInfo( p0: AMapNaviCameraInfo?, p1: AMapNaviCameraInfo?, p2: Int ) { } override fun onServiceAreaUpdate(p0: Array?) { } override fun showCross(p0: AMapNaviCross?) { } override fun hideCross() { } override fun showModeCross(p0: AMapModelCross?) { } override fun hideModeCross() { } override fun showLaneInfo(p0: Array?, p1: ByteArray?, p2: ByteArray?) { } override fun showLaneInfo(p0: AMapLaneInfo?) { } override fun hideLaneInfo() { } override fun onCalculateRouteSuccess(p0: IntArray?) { } /** * 线路规划成功 */ override fun onCalculateRouteSuccess(result: AMapCalcRouteResult?) { // LogHelper.i(TAG, "mAMapNavi.naviPath.coordList===>${mAMapNavi.naviPath.coordList.size}") // RouterManager.mCoordList = mAMapNavi.naviPath.coordList val naviRouteOverlay = NaviRouteOverlay(mAMap, mAMapNavi.naviPath, context) naviRouteOverlay.setShowDefaultLineArrow(true) naviRouteOverlay.addToMap() // naviRouteOverlay.removeFromMap() mAMapNavi.setEmulatorNaviSpeed(10) /** * CRUISE 巡航模式(数值:3) EMULATOR 模拟导航(数值:2) GPS 实时导航(数值:1) NONE 未开始导航(数值:-1) */ mAMapNavi.startNavi(NaviType.GPS) // disp = MockGPSTaskManager.startGpsMockTask(mAMapNavi.naviPath)?.subscribe() } override fun notifyParallelRoad(p0: Int) { } override fun OnUpdateTrafficFacility(p0: Array?) { } override fun OnUpdateTrafficFacility(p0: AMapNaviTrafficFacilityInfo?) { } override fun updateAimlessModeStatistics(p0: AimLessModeStat?) { } override fun updateAimlessModeCongestionInfo(p0: AimLessModeCongestionInfo?) { } override fun onPlayRing(p0: Int) { } override fun onNaviRouteNotify(p0: AMapNaviRouteNotifyData?) { } override fun onGpsSignalWeak(p0: Boolean) { } fun onDestroy() { if (disp != null && !disp!!.isDisposed()) { disp!!.dispose() } } } ================================================ FILE: Android/app/src/main/java/com/didichuxing/doraemondemo/amap/NaviRouteOverlay.kt ================================================ package com.didichuxing.doraemondemo.amap import android.content.Context import android.graphics.BitmapFactory import com.amap.api.maps.AMap import com.amap.api.maps.model.BitmapDescriptorFactory import com.amap.api.maps.model.LatLng import com.amap.api.maps.model.Polyline import com.amap.api.maps.model.PolylineOptions import com.amap.api.navi.model.AMapNaviPath import com.amap.api.navi.model.RouteOverlayOptions import com.amap.api.navi.view.RouteOverLay import com.blankj.utilcode.util.ConvertUtils import com.blankj.utilcode.util.ReflectUtils /** * 地图上的导航路径 * * @author vinda * @since 2017/12/27 */ class NaviRouteOverlay( private val aMap: AMap, aMapNaviPath: AMapNaviPath?, val mContext: Context ) : RouteOverLay( aMap, aMapNaviPath, mContext ) { var routeId = 0 //显示默认路线箭头 private var showDefaultLineArrow = false /** * 非选中状态,使用比较浅的颜色,需要重新调用addToMap生效 * * @param shadow */ private fun setupOptions(shadow: Boolean) { val options: RouteOverlayOptions if (shadow) { options = customShadowRouteTexture() //setZindex(ROUTE_SHADOW_Z_INDEX) } else { options = customRouteTexture() //setZindex(ROUTE_NORMAL_Z_INDEX) } options.setOnRouteCameShow(false) routeOverlayOptions = options } /** * 设置是否显示默认路线(非路况)上的箭头,在调用addToMap之前调用 */ fun setShowDefaultLineArrow(visible: Boolean) { showDefaultLineArrow = visible } /** * 复写父类方法 */ override fun addToMap() { super.addToMap() //支持默认路线时显示箭头 if (showDefaultLineArrow) { //显示箭头 addDefaultArrowLine() } } /** * 默认路线加入箭头 */ private fun addDefaultArrowLine() { //通过反射的方式向父类加入箭头路线 val path = ReflectUtils.reflect(this).field("mPathPoints").get>() val lines = ReflectUtils.reflect(this).field("mCustomPolyLines").get>() if (path != null && path is List<*> && lines != null && lines is List<*>) { val mLatLngsOfPath = path as List val mCustomPolylines = lines as MutableList if (mLatLngsOfPath.isNotEmpty()) { val arrowOnRoute = BitmapDescriptorFactory.fromBitmap( routeOverlayOptions.arrowOnTrafficRoute ) val mDefaultArrowline = this.aMap.addPolyline( PolylineOptions().addAll(mLatLngsOfPath).setCustomTexture(arrowOnRoute).width( width / 1.5f ) ) mCustomPolylines.add(mDefaultArrowline) } } } private fun customShadowRouteTexture(): RouteOverlayOptions { val routeOverlayOptions = RouteOverlayOptions() var fis = BitmapDescriptorFactory::class.java.getResourceAsStream("/assets/img/dk_custtexture_aolr.png") val custtexture_aolr = BitmapFactory.decodeStream(fis) fis = BitmapDescriptorFactory::class.java.getResourceAsStream("/assets/img/dk_custtexture_b.png") val custtexture = BitmapFactory.decodeStream(fis) fis = BitmapDescriptorFactory::class.java.getResourceAsStream("/assets/img/dk_custtexture_no_b.png") val custtexture_no = BitmapFactory.decodeStream(fis) fis = BitmapDescriptorFactory::class.java.getResourceAsStream("/assets/img/dk_custtexture_green_b.png") val custtexture_green = BitmapFactory.decodeStream(fis) fis = BitmapDescriptorFactory::class.java.getResourceAsStream("/assets/img/dk_custtexture_slow_b.png") val custtexture_slow = BitmapFactory.decodeStream(fis) fis = BitmapDescriptorFactory::class.java.getResourceAsStream("/assets/img/dk_custtexture_bad_b.png") val custtexture_bad = BitmapFactory.decodeStream(fis) fis = BitmapDescriptorFactory::class.java.getResourceAsStream("/assets/img/dk_custtexture_grayred_b.png") val custtexture_grayred = BitmapFactory.decodeStream(fis) routeOverlayOptions.arrowOnTrafficRoute = custtexture_aolr routeOverlayOptions.normalRoute = custtexture routeOverlayOptions.unknownTraffic = custtexture_no routeOverlayOptions.smoothTraffic = custtexture_green routeOverlayOptions.slowTraffic = custtexture_slow routeOverlayOptions.jamTraffic = custtexture_bad routeOverlayOptions.veryJamTraffic = custtexture_grayred //设置导航线路的宽度, 单位:像素 routeOverlayOptions.lineWidth = ConvertUtils.dp2px(LINE_WIDTH_DP.toFloat()).toFloat() return routeOverlayOptions } companion object { private const val LINE_WIDTH_DP = 14 fun customRouteTexture(): RouteOverlayOptions { val routeOverlayOptions = RouteOverlayOptions() var fis = BitmapDescriptorFactory::class.java.getResourceAsStream("/assets/img/dk_custtexture_aolr.png") val custtexture_aolr = BitmapFactory.decodeStream(fis) fis = BitmapDescriptorFactory::class.java.getResourceAsStream("/assets/img/dk_custtexture.png") val custtexture = BitmapFactory.decodeStream(fis) fis = BitmapDescriptorFactory::class.java.getResourceAsStream("/assets/img/dk_custtexture_no.png") val custtexture_no = BitmapFactory.decodeStream(fis) fis = BitmapDescriptorFactory::class.java.getResourceAsStream("/assets/img/dk_custtexture_green.png") val custtexture_green = BitmapFactory.decodeStream(fis) fis = BitmapDescriptorFactory::class.java.getResourceAsStream("/assets/img/dk_custtexture_slow.png") val custtexture_slow = BitmapFactory.decodeStream(fis) fis = BitmapDescriptorFactory::class.java.getResourceAsStream("/assets/img/dk_custtexture_bad.png") val custtexture_bad = BitmapFactory.decodeStream(fis) fis = BitmapDescriptorFactory::class.java.getResourceAsStream("/assets/img/dk_custtexture_grayred.png") val custtexture_grayred = BitmapFactory.decodeStream(fis) routeOverlayOptions.arrowOnTrafficRoute = custtexture_aolr routeOverlayOptions.normalRoute = custtexture routeOverlayOptions.unknownTraffic = custtexture_no routeOverlayOptions.smoothTraffic = custtexture_green routeOverlayOptions.slowTraffic = custtexture_slow routeOverlayOptions.jamTraffic = custtexture_bad routeOverlayOptions.veryJamTraffic = custtexture_grayred //设置导航线路的宽度, 单位:像素 routeOverlayOptions.lineWidth = ConvertUtils.dp2px(LINE_WIDTH_DP.toFloat()).toFloat() return routeOverlayOptions } } init { setupOptions(false) //不显示起点、终点 // setStartPointBitmap(Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8)) // setEndPointBitmap(Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8)) } } ================================================ FILE: Android/app/src/main/java/com/didichuxing/doraemondemo/comm/CommBaseFragment.kt ================================================ package com.didichuxing.doraemondemo.comm import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.annotation.IdRes import androidx.annotation.LayoutRes import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels import androidx.lifecycle.ViewModelProvider /** * ================================================ * 作 者:jint(金台) * 版 本:1.0 * 创建日期:3/11/21-16:14 * 描 述: * 修订历史: * ================================================ */ abstract class CommBaseFragment : Fragment() { private val viewModel: CommViewModel by activityViewModels() private lateinit var mRootView: ViewGroup abstract fun initActivityTitle(): String @LayoutRes abstract fun getLayoutId(): Int private fun bindViewModel() { viewModel.title = initActivityTitle() } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) bindViewModel() } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { mRootView = inflater.inflate(getLayoutId(), container, false) as ViewGroup return mRootView } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) initView(savedInstanceState) } abstract fun initView(savedInstanceState: Bundle?) fun findViewById(@IdRes id: Int): T { return mRootView.findViewById(id) } } ================================================ FILE: Android/app/src/main/java/com/didichuxing/doraemondemo/comm/CommFragmentActivity.kt ================================================ package com.didichuxing.doraemondemo.comm import android.os.Bundle import android.view.View import android.view.Window import android.widget.TextView import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.viewModels import androidx.lifecycle.Observer import androidx.lifecycle.SavedStateViewModelFactory import com.didichuxing.doraemondemo.R /** * ================================================ * 作 者:jint(金台) * 版 本:1.0 * 创建日期:3/11/21-15:14 * 描 述: * 修订历史: * ================================================ */ class CommFragmentActivity : AppCompatActivity() { var fragmentClass: Class? = null private val viewModel: CommViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_comm) initTitleBar() initFragment() } private fun initFragment() { val bundle = intent.extras bundle?.let { if (bundle.get(CommLauncher.FRAGMENT_CLASS) != null) { fragmentClass = bundle[CommLauncher.FRAGMENT_CLASS] as Class? fragmentClass?.let { supportFragmentManager .beginTransaction() .add(R.id.container_view, it.newInstance()) .commit() } } } } private fun initTitleBar() { findViewById(R.id.iv_back).setOnClickListener { finish() } viewModel.getTitle().observe(this, Observer { findViewById(R.id.tv_title).text = it }) } } ================================================ FILE: Android/app/src/main/java/com/didichuxing/doraemondemo/comm/CommLauncher.kt ================================================ package com.didichuxing.doraemondemo.comm import android.content.Context import android.content.Intent import android.os.Bundle import com.didichuxing.doraemonkit.DoKit /** * ================================================ * 作 者:jint(金台) * 版 本:1.0 * 创建日期:3/11/21-17:40 * 描 述: * 修订历史: * ================================================ */ object CommLauncher { const val FRAGMENT_CLASS: String = "FRAGMENT_CLASS" fun startActivity( targetClass: Class, context: Context, bundle: Bundle? = null ) { context?.startActivity(Intent(context, CommFragmentActivity::class.java).apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK putExtra(FRAGMENT_CLASS, targetClass) if (bundle != null) { putExtras(bundle) } }) } } ================================================ FILE: Android/app/src/main/java/com/didichuxing/doraemondemo/comm/CommViewModel.kt ================================================ package com.didichuxing.doraemondemo.comm import androidx.lifecycle.* /** * ================================================ * 作 者:jint(金台) * 版 本:1.0 * 创建日期:3/11/21-16:12 * 描 述: * 修订历史: * ================================================ */ class CommViewModel(private val state: SavedStateHandle) : ViewModel() { companion object { const val KEY_TITLE = "title" } var title: String? get() { return state.get(KEY_TITLE) } set(value) { state.set(KEY_TITLE, value) } fun getTitle(): LiveData { return state.getLiveData(KEY_TITLE, "DoKit") } fun saveTitle(title: String) { state.set(KEY_TITLE, title) } } ================================================ FILE: Android/app/src/main/java/com/didichuxing/doraemondemo/db/DatabaseHelper.kt ================================================ package com.didichuxing.doraemondemo.db import android.content.Context import android.database.sqlite.SQLiteDatabase import android.database.sqlite.SQLiteDatabase.CursorFactory import android.database.sqlite.SQLiteOpenHelper import android.widget.Toast /** * Created by wanglikun on 2019/5/4 */ class DatabaseHelper(private val mContext: Context, name: String?, factory: CursorFactory?, version: Int) : SQLiteOpenHelper(mContext, name, factory, version) { override fun onCreate(db: SQLiteDatabase) { db.execSQL(CREATE_BOOK) db.execSQL(INSERT_BOOK) Toast.makeText(mContext, "创建成功", Toast.LENGTH_SHORT).show() } override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {} companion object { const val CREATE_BOOK = "create table Book (" + "id integer primary key autoincrement, " + "author text, " + "price real, " + "page integer, " + "name text)" const val INSERT_BOOK = "insert into Book (" + "author," + "price," + "page," + "name" + ")" + "values (" + "'jint'," + "100," + "1000," + "'从入门到放弃')" } } ================================================ FILE: Android/app/src/main/java/com/didichuxing/doraemondemo/db/PersonDBHelper.java ================================================ /* * * * Copyright (C) 2019 Amit Shekhar * * Copyright (C) 2011 Android Open Source Project * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * http://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * */ package com.didichuxing.doraemondemo.db; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import com.tencent.wcdb.DatabaseUtils; import com.tencent.wcdb.database.SQLiteDatabase; import com.tencent.wcdb.database.SQLiteOpenHelper; import java.util.ArrayList; public class PersonDBHelper extends SQLiteOpenHelper { public static final String DATABASE_NAME = "Person.db"; public static final String PERSON_TABLE_NAME = "person"; public static final String PERSON_COLUMN_ID = "id"; public static final String PERSON_COLUMN_FIRST_NAME = "first_name"; public static final String PERSON_COLUMN_LAST_NAME = "last_name"; public static final String PERSON_COLUMN_ADDRESS = "address"; private static final String DB_PASSWORD = "a_password"; public PersonDBHelper(Context context) { super(context, DATABASE_NAME, DB_PASSWORD.getBytes(), null, 3, null); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL( "create table person " + "(id integer primary key, first_name text, last_name text, address text)" ); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL("DROP TABLE IF EXISTS person"); onCreate(db); } public boolean insertPerson(String firstName, String lastName, String address) { SQLiteDatabase db = this.getWritableDatabase(); ContentValues contentValues = new ContentValues(); contentValues.put("first_name", firstName); contentValues.put("last_name", lastName); contentValues.put("address", address); db.insert("person", null, contentValues); db.close(); return true; } public Cursor getData(int id) { SQLiteDatabase db = this.getReadableDatabase(); Cursor res = db.rawQuery("select * from person where id=" + id + "", null); return res; } public int numberOfRows() { SQLiteDatabase db = this.getReadableDatabase(); int numRows = (int) DatabaseUtils.queryNumEntries(db, PERSON_TABLE_NAME); return numRows; } public boolean updatePerson(Integer id, String firstName, String lastName, String address, float mileage) { SQLiteDatabase db = this.getWritableDatabase(); ContentValues contentValues = new ContentValues(); contentValues.put("first_name", firstName); contentValues.put("last_name", lastName); contentValues.put("address", address); db.update("person", contentValues, "id = ? ", new String[]{Integer.toString(id)}); db.close(); return true; } public Integer deletePerson(Integer id) { SQLiteDatabase db = this.getWritableDatabase(); return db.delete("person", "id = ? ", new String[]{Integer.toString(id)}); } public ArrayList getAllPerson() { ArrayList arrayList = new ArrayList<>(); SQLiteDatabase db = this.getReadableDatabase(); Cursor res = db.rawQuery("select * from person", null); res.moveToFirst(); while (!res.isAfterLast()) { arrayList.add( res.getString(res.getColumnIndex(PERSON_COLUMN_FIRST_NAME)) + " " + res.getString(res.getColumnIndex(PERSON_COLUMN_LAST_NAME))); res.moveToNext(); } res.close(); db.close(); return arrayList; } public int count() { SQLiteDatabase db = getReadableDatabase(); Cursor cursor = db.rawQuery("select * from person", null); try { if (cursor != null && cursor.getCount() > 0) { cursor.moveToFirst(); return cursor.getInt(0); } else { return 0; } } finally { cursor.close(); db.close(); } } } ================================================ FILE: Android/app/src/main/java/com/didichuxing/doraemondemo/dokit/BorderDoKitView.java ================================================ //package com.didichuxing.doraemondemo.dokit; // //import android.content.Context; //import android.view.LayoutInflater; //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.viewcheck.LayoutBorderView; //import com.didichuxing.doraemonkit.model.ViewInfo; // ///** // * Created by jintai on 2019/09/26. // * 在改布局上绘制相应的View // */ //public class BorderDoKitView extends AbsDokitView { // private LayoutBorderView mLayoutBorderView = null; // // @Override // public void onCreate(Context context) { // // } // // @Override // public void onDestroy() { // super.onDestroy(); // // } // // @Override // public View onCreateView(Context context, FrameLayout view) { // return LayoutInflater.from(context).inflate(R.layout.dk_float_view_check_draw, null); // } // // // @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; // } // // // @Override // public void onViewCreated(FrameLayout view) { // mLayoutBorderView = findViewById(R.id.rect_view); // setDoKitViewNotResponseTouchEvent(getDoKitView()); // } // // // /** // * 解决ViewCheckDrawDokitView的margin被改变的bug // */ // @Override // public void onResume() { // super.onResume(); // if (getNormalLayoutParams() != null) { // FrameLayout.LayoutParams params = getNormalLayoutParams(); // params.setMargins(0, 0, 0, 0); // params.width = FrameLayout.LayoutParams.MATCH_PARENT; // params.height = FrameLayout.LayoutParams.MATCH_PARENT; // immInvalidate(); // } // } // // @Override // public boolean canDrag() { // return false; // } // // public void showBorder(View target) { // if (target == null) { // mLayoutBorderView.showViewLayoutBorder((ViewInfo) null); // } else { // mLayoutBorderView.showViewLayoutBorder(new ViewInfo(target)); // } // } // //} ================================================ FILE: Android/app/src/main/java/com/didichuxing/doraemondemo/dokit/CustomDokitFragment.kt ================================================ package com.didichuxing.doraemondemo.dokit import android.os.Bundle 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 /** * @Author: changzuozhen * @Date: 2020-12-22 * 切换全屏与否只需要调整继承关系即可 * @see CustomDokitFragment * * @see TestSimpleDoKitFloatView * * * 悬浮窗,支持折叠 * * 启动工具函数 * * * * 全屏页面 * * @see com.didichuxing.doraemonkit.kit.core.AbsDoKitFragment * 启动工具函数 * */ class CustomDokitFragment : AbsDoKitFragment() { override fun onViewCreated(rootView: View?) { super.onViewCreated(view) ViewSetupHelper.setupButton(rootView, R.id.test1, "TestSimpleDokitFragment") { v: View? -> val bundle = Bundle() bundle.putString("test", "test") DoKit.launchFullScreen(CustomDokitFragment::class.java, context, isSystemFragment = false) } // 隐藏 ViewSetupHelper.setupButton(rootView, R.id.test2, "") { v: View? -> } ViewSetupHelper.setupToggleButton( rootView, R.id.tb_test1, "TB", false ) { buttonView: CompoundButton?, isChecked: Boolean -> } // 隐藏 ViewSetupHelper.setupToggleButton( rootView, R.id.tb_test2, "", false ) { buttonView: CompoundButton?, isChecked: Boolean -> } } override fun layoutId(): Int { return R.layout.layout_demo_custom } override fun initTitle(): String { return "我是自定义页面" } } ================================================ FILE: Android/app/src/main/java/com/didichuxing/doraemondemo/dokit/DemoDoKitView.kt ================================================ package com.didichuxing.doraemondemo.dokit import android.content.Context import android.view.Gravity import android.view.LayoutInflater import android.view.View 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 /** * ================================================ * 作 者:jint(金台) * 版 本:1.0 * 创建日期:2019-09-23-21:02 * 描 述: * 修订历史: * ================================================ */ 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) } override fun onViewCreated(rootView: FrameLayout) { val tvClose = findViewById(R.id.tv_close) tvClose?.setOnClickListener { DoKit.removeFloating(DemoDoKitView::class) } } 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 } } ================================================ FILE: Android/app/src/main/java/com/didichuxing/doraemondemo/dokit/DemoKit.kt ================================================ package com.didichuxing.doraemondemo.dokit import android.app.Activity import android.content.Context import com.didichuxing.doraemondemo.R import com.didichuxing.doraemonkit.DoKit import com.didichuxing.doraemonkit.kit.AbstractKit import com.didichuxing.doraemonkit.kit.Category /** * ================================================ * 作 者:jint(金台) * 版 本:1.0 * 创建日期:2019-09-24-15:48 * 描 述:kit demo * 修订历史: * ================================================ */ class DemoKit : AbstractKit() { override val category: Int get() = Category.BIZ override val name: Int get() = R.string.dk_kit_demo override val icon: Int get() = R.mipmap.dk_sys_info override fun onClickWithReturn(activity: Activity): Boolean { DoKit.launchFloating(DemoDoKitView::class.java) return true } override fun onAppInit(context: Context?) { } } ================================================ FILE: Android/app/src/main/java/com/didichuxing/doraemondemo/dokit/SimpleDoKitView.java ================================================ package com.didichuxing.doraemondemo.dokit; import android.content.Context; import android.util.DisplayMetrics; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.WindowManager; import android.widget.CompoundButton; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.Switch; 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; /** * @Author: changzuozhen * @Date: 2020-12-22 *

* 悬浮窗,支持折叠 * @see SimpleDoKitView * 启动工具函数 */ public abstract class SimpleDoKitView extends AbsDoKitView { private static final String TAG = "SimpleBaseFloatPage"; int mWidth; int mHeight; int mDp50InPx; private WindowManager mWindowManager; private FrameLayout mFloatContainer; private Switch mShowSwitch; private Context mContext; @Override public void onEnterForeground() { super.onEnterForeground(); getParentView().setVisibility(View.VISIBLE); } @Override public void onEnterBackground() { super.onEnterBackground(); getParentView().setVisibility(View.GONE); } public void showContainer(boolean isChecked) { mFloatContainer.setVisibility(isChecked ? View.VISIBLE : View.GONE); immInvalidate(); } @Override public void onCreate(Context context) { mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); DisplayMetrics outMetrics = new DisplayMetrics(); mWindowManager.getDefaultDisplay().getMetrics(outMetrics); mDp50InPx = ConvertUtils.dp2px(50); mWidth = outMetrics.widthPixels - mDp50InPx; mHeight = outMetrics.heightPixels - mDp50InPx; } @Override public View onCreateView(Context context, FrameLayout rootView) { mContext = context; return LayoutInflater.from(context).inflate(R.layout.dk_layout_simple_dokit_float_view, rootView, false); } @Override public void onViewCreated(FrameLayout rootView) { mFloatContainer = findViewById(R.id.floatContainer); LayoutInflater.from(mContext).inflate(getLayoutId(), mFloatContainer); mShowSwitch = findViewById(R.id.showHideSwitch); TextView title = findViewById(R.id.floatPageTitle); ImageView close = findViewById(R.id.floatClose); close.setOnClickListener(v -> DoKit.removeFloating(this)); title.setText(getTag()); mShowSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { showContainer(isChecked); } }); initView(); } protected abstract int getLayoutId(); @Override 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; } @Override public boolean onBackPressed() { mShowSwitch.setChecked(false); return false; } @Override public boolean shouldDealBackKey() { return true; } protected void initView() { } } ================================================ FILE: Android/app/src/main/java/com/didichuxing/doraemondemo/dokit/TestSimpleDoKitFloatView.kt ================================================ package com.didichuxing.doraemondemo.dokit import android.os.Bundle import android.util.Log import android.view.View import android.widget.CompoundButton import android.widget.FrameLayout import com.didichuxing.doraemondemo.R import com.didichuxing.doraemonkit.DoKit /** * @Author: changzuozhen * @Date: 2020-12-22 * 切换全屏与否只需要调整继承关系即可 * @see CustomDokitFragment * @see TestSimpleDoKitFloatView * * 悬浮窗,支持折叠 * @see com.didichuxing.doraemonkit.kit.core.SimpleDokitView * 启动工具函数 * * 全屏页面 * @see com.didichuxing.doraemonkit.kit.core.AbsDoKitFragment * 启动工具函数 * */ class TestSimpleDoKitFloatView : SimpleDoKitView() { override fun getLayoutId(): Int { return R.layout.layout_demo_custom } override fun onViewCreated(rootView: FrameLayout?) { super.onViewCreated(rootView) ViewSetupHelper.setupButton(rootView, R.id.test1, "TestSimpleDokitFragment", View.OnClickListener { val bundle = Bundle() bundle.putString("test", "test") DoKit.launchFullScreen(CustomDokitFragment::class.java, context) }) // 隐藏 ViewSetupHelper.setupButton(rootView, R.id.test2, "", null) ViewSetupHelper.setupToggleButton(rootView, R.id.tb_test1, "TB", false, CompoundButton.OnCheckedChangeListener { buttonView, isChecked -> Log.d("TEST", "TB $isChecked") }) // 隐藏 ViewSetupHelper.setupToggleButton(rootView, R.id.tb_test2, "", false, null) } } ================================================ FILE: Android/app/src/main/java/com/didichuxing/doraemondemo/dokit/TestSimpleDokitFloatViewKit.kt ================================================ package com.didichuxing.doraemondemo.dokit import android.app.Activity import android.content.Context import com.didichuxing.doraemondemo.R import com.didichuxing.doraemonkit.DoKit import com.didichuxing.doraemonkit.kit.AbstractKit import com.didichuxing.doraemonkit.kit.Category /** * @Author: changzuozhen * @Date: 2020-12-22 */ class TestSimpleDokitFloatViewKit : AbstractKit() { override val category: Int get() = Category.BIZ override val name: Int get() = R.string.dk_kit_simple_float override val icon: Int get() = R.mipmap.dk_sys_info override fun onClick(context: Context?) { } override fun onClickWithReturn(activity: Activity): Boolean { DoKit.launchFloating(TestSimpleDoKitFloatView::class.java) return true } override fun onAppInit(context: Context?) { } } ================================================ FILE: Android/app/src/main/java/com/didichuxing/doraemondemo/dokit/TestSimpleDokitFragmentKit.kt ================================================ package com.didichuxing.doraemondemo.dokit import android.app.Activity import android.content.Context import android.os.Bundle import com.didichuxing.doraemondemo.R import com.didichuxing.doraemonkit.DoKit import com.didichuxing.doraemonkit.kit.AbstractKit import com.didichuxing.doraemonkit.kit.Category /** * @Author: changzuozhen * @Date: 2020-12-22 */ class TestSimpleDokitFragmentKit : AbstractKit() { override val category: Int get() = Category.BIZ override val name: Int get() = R.string.dk_kit_fullscreen override val icon: Int get() = R.mipmap.dk_sys_info override fun onClickWithReturn(activity: Activity): Boolean { val bundle = Bundle() bundle.putString("test", "test") DoKit.launchFullScreen(CustomDokitFragment::class, activity, bundle) return true } override fun onAppInit(context: Context?) { } } ================================================ FILE: Android/app/src/main/java/com/didichuxing/doraemondemo/dokit/ViewSetupHelper.java ================================================ package com.didichuxing.doraemondemo.dokit; import android.text.TextUtils; import android.view.View; import android.widget.CompoundButton; import android.widget.TextView; import android.widget.ToggleButton; public final class ViewSetupHelper { private ViewSetupHelper() { } public static void setupButton(View container, int viewId, String title, View.OnClickListener onBtnClick) { TextView button = (TextView) container.findViewById(viewId); if (TextUtils.isEmpty(title)) { button.setVisibility(View.GONE); } else { button.setText(title); button.setVisibility(View.VISIBLE); button.setOnClickListener(onBtnClick); } } public static void setVisible(View container, int viewId, boolean visible) { container.findViewById(viewId).setVisibility(visible ? View.VISIBLE : View.GONE); } public static void setupToggleButton(View container, int viewId, String title, boolean checked, CompoundButton.OnCheckedChangeListener onCheckedChangeListener) { ToggleButton toggleButton = (ToggleButton) container.findViewById(viewId); if (TextUtils.isEmpty(title)) { toggleButton.setVisibility(View.GONE); } else { toggleButton.setVisibility(View.VISIBLE); toggleButton.setTextOn(title + " ON"); toggleButton.setTextOff(title + " OFF"); toggleButton.setChecked(checked); toggleButton.setOnCheckedChangeListener(onCheckedChangeListener); onCheckedChangeListener.onCheckedChanged(toggleButton, checked); } } } ================================================ FILE: Android/app/src/main/java/com/didichuxing/doraemondemo/mc/DoKitButton.kt ================================================ package com.didichuxing.doraemondemo.mc import android.content.Context import android.util.AttributeSet import android.view.MotionEvent import androidx.appcompat.widget.AppCompatButton /** * ================================================ * 作 者:jint(金台) * 版 本:1.0 * 创建日期:2020/12/7-19:27 * 描 述: * 修订历史: * ================================================ */ class DoKitButton : AppCompatButton { companion object { const val TAG = "DoKitButton" } constructor(context: Context) : super(context) constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super( context, attrs, defStyleAttr ) // private val POPULATING_ACCESSIBILITY_EVENT_TYPES = (AccessibilityEvent.TYPE_VIEW_CLICKED // or AccessibilityEvent.TYPE_VIEW_LONG_CLICKED // or AccessibilityEvent.TYPE_VIEW_SELECTED // or AccessibilityEvent.TYPE_VIEW_FOCUSED // or AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED // or AccessibilityEvent.TYPE_VIEW_HOVER_ENTER // or AccessibilityEvent.TYPE_VIEW_HOVER_EXIT // or AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED // or AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED // or AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED // or AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY) // // // override fun sendAccessibilityEvent(eventType: Int) { // super.sendAccessibilityEvent(eventType) // } // // override fun sendAccessibilityEventUnchecked(event: AccessibilityEvent?) { // // // // Do not send scroll events since first they are not interesting for // // accessibility and second such events a generated too frequently. // // For details see the implementation of bringTextIntoView(). // if (event!!.eventType == AccessibilityEvent.TYPE_VIEW_SCROLLED) { // return // } // // if (!isShown) { // return // } // onInitializeAccessibilityEvent(event) // LogHelper.i(TAG, "event1===>$event") // // Only a subset of accessibility events populates text content. // // Only a subset of accessibility events populates text content. // if (event!!.eventType and POPULATING_ACCESSIBILITY_EVENT_TYPES != 0) { // dispatchPopulateAccessibilityEvent(event) // } // LogHelper.i(TAG, "event2===>$event") // // // Android 9.0以下的系统会在requestSendAccessibilityEvent 方法中对于event做一定的处理 导致event 中的有些字段会被置空 // this.parent?.requestSendAccessibilityEvent(this, event) // LogHelper.i(TAG, "event3===>$event") // } override fun onTouchEvent(event: MotionEvent?): Boolean { return super.onTouchEvent(event) } } ================================================ FILE: Android/app/src/main/java/com/didichuxing/doraemondemo/mc/DoKitRecycleView.kt ================================================ package com.didichuxing.doraemondemo.mc import android.content.Context import android.util.AttributeSet import androidx.recyclerview.widget.RecyclerView /** * ================================================ * 作 者:jint(金台) * 版 本:1.0 * 创建日期:2020/12/7-19:17 * 描 述: * 修订历史: * ================================================ */ class DoKitRecycleView : RecyclerView { constructor(context: Context) : super(context) constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super( context, attrs, defStyleAttr ) } ================================================ FILE: Android/app/src/main/java/com/didichuxing/doraemondemo/mc/DoKitWebView.kt ================================================ package com.didichuxing.doraemondemo.mc import android.content.Context import android.util.AttributeSet import android.webkit.WebView /** * ================================================ * 作 者:jint(金台) * 版 本:1.0 * 创建日期:2020/12/7-19:27 * 描 述: * 修订历史: * ================================================ */ class DoKitWebView : WebView { companion object { const val TAG = "DoKitWebView" } constructor(context: Context) : super(context) constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super( context, attrs, defStyleAttr ) override fun getAccessibilityClassName(): CharSequence { return super.getAccessibilityClassName() } override fun getAccessibilityTraversalBefore(): Int { return super.getAccessibilityTraversalBefore() } } ================================================ FILE: Android/app/src/main/java/com/didichuxing/doraemondemo/mc/FileUtils.java ================================================ package com.didichuxing.doraemondemo.mc; import java.io.File; import java.io.InputStream; /** * didi Create on 2022/3/17 . *

* Copyright (c) 2022/3/17 by didiglobal.com. * * @author zhangjun * @version 1.0 * @Date 2022/3/17 4:54 下午 * @Description 用一句话说明文件功能 */ public class FileUtils { /** * 获取assets目录下的单个文件 * 这种方式只能用于webview加载 * 读取文件夹,直接取路径是不行的 * * @param fileName 文件夹名 * @return File */ public static File getFileFromAssetsFile(String fileName) { String path = "file:///android_asset/" + fileName; File file = new File(path); return file; } public static String readString(InputStream inputStream) throws Exception { int size = inputStream.available(); byte[] buffer = new byte[size]; inputStream.read(buffer, 0, size); return new String(buffer); } } ================================================ FILE: Android/app/src/main/java/com/didichuxing/doraemondemo/mc/MCActivity.kt ================================================ package com.didichuxing.doraemondemo.mc import android.content.Intent import android.os.Bundle import android.view.View import android.widget.* import android.widget.AdapterView.OnItemSelectedListener import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.Fragment import androidx.recyclerview.widget.LinearLayoutManager 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() { 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) findViewById