├── .gitignore
├── .idea
├── .gitignore
├── compiler.xml
├── deploymentTargetSelector.xml
├── gradle.xml
├── inspectionProfiles
│ └── Project_Default.xml
├── jarRepositories.xml
├── kotlinc.xml
├── migrations.xml
├── misc.xml
└── vcs.xml
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── peakmain
│ │ └── debugtools
│ │ └── ExampleInstrumentedTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── peakmain
│ │ │ └── debugtools
│ │ │ ├── App.kt
│ │ │ ├── MainActivity.kt
│ │ │ ├── RetrofitUtils.java
│ │ │ └── WanAndroidService.kt
│ └── res
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable
│ │ └── ic_launcher_background.xml
│ │ ├── layout
│ │ └── activity_main.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── values-night
│ │ └── themes.xml
│ │ ├── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── themes.xml
│ │ └── xml
│ │ └── network_security_config.xml
│ └── test
│ └── java
│ └── com
│ └── peakmain
│ └── debugtools
│ └── ExampleUnitTest.kt
├── build.gradle
├── config.gradle
├── debug
├── .gitignore
├── build.gradle
├── libs
│ └── libbreakpad.aar
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── peakmain
│ │ └── debug
│ │ └── ExampleInstrumentedTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── peakmain
│ │ │ └── debug
│ │ │ ├── DebugFileProvider.java
│ │ │ ├── DebugToolDialogFragment.kt
│ │ │ ├── DebugTools.kt
│ │ │ ├── activity
│ │ │ ├── CrashLogActivity.kt
│ │ │ └── EnvironmentExchangeActivity.kt
│ │ │ ├── adapter
│ │ │ ├── CrashLogAdapter.kt
│ │ │ ├── EnvironmentExchangeAdapter.kt
│ │ │ ├── H5EnvironmentExchangeAdapter.kt
│ │ │ └── HttpLoggingAdapter.kt
│ │ │ ├── annotation
│ │ │ └── PDebug.kt
│ │ │ ├── base
│ │ │ ├── BaseDebugActivity.kt
│ │ │ └── EnvironmentExchangeBean.kt
│ │ │ ├── bean
│ │ │ └── HttpLoggingBean.kt
│ │ │ ├── ext
│ │ │ └── DebugViewExt.kt
│ │ │ ├── log
│ │ │ ├── HttpLoggingActivity.kt
│ │ │ ├── HttpLoggingDetailActivity.kt
│ │ │ └── HttpLoggingInterceptor.kt
│ │ │ ├── manager
│ │ │ ├── DebugToolsManager.kt
│ │ │ ├── H5PreferenceManager.kt
│ │ │ └── HttpLoggingManager.kt
│ │ │ ├── utils
│ │ │ ├── CrashHelper.kt
│ │ │ ├── CrashUtils.kt
│ │ │ └── FormatJson.java
│ │ │ └── viewmodel
│ │ │ ├── CrashLogViewModel.kt
│ │ │ ├── EnvironmentExchangeViewModel.kt
│ │ │ └── HttpLoggingViewModel.kt
│ └── res
│ │ ├── drawable
│ │ ├── ic_debug_clear_all_24.xml
│ │ ├── ic_descending_order.xml
│ │ ├── ic_launcher_background.xml
│ │ ├── ic_positive_sequence.xml
│ │ ├── ic_share_24.xml
│ │ ├── shape_background_debug_tool.xml
│ │ └── shape_divider_debug_tool.xml
│ │ ├── layout
│ │ ├── activity_debug_http_logging_detail.xml
│ │ ├── debug_activity_crash_log.xml
│ │ ├── debug_activity_environment_exchange.xml
│ │ ├── debug_dialog_tool.xml
│ │ ├── debug_http_logging.xml
│ │ ├── debug_recycler_crash_log_item.xml
│ │ ├── debug_recycler_environment_item.xml
│ │ ├── debug_recycler_http_logging.xml
│ │ └── debug_recycler_tool_item.xml
│ │ ├── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ │ └── xml
│ │ └── debug_provider_paths.xml
│ └── test
│ └── java
│ └── com
│ └── peakmain
│ └── debug
│ └── ExampleUnitTest.kt
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── jitpack.yml
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 | .cxx
15 | local.properties
16 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/deploymentTargetSelector.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
19 |
20 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/.idea/jarRepositories.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/.idea/kotlinc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/migrations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # DebugTools
2 | DebugTools是一个设计开发者支撑工具库
3 |
4 | - 查看崩溃日志
5 | - 接口抓包工具
6 | - 打开/关闭FPS
7 | - 环境切换
8 | - 项目地址:[https://github.com/Peakmain/DebugTools](https://github.com/Peakmain/DebugTools)
9 | - 打开DebugToolDialogFragment
10 | ```kotlin
11 | findViewById(R.id.tv_name).setOnClickListener {
12 | //方法一
13 | var clazz: Class<*>? = null
14 | try {
15 | clazz = Class.forName("com.peakmain.debug.DebugToolDialogFragment")
16 | val target: DebugToolDialogFragment =
17 | clazz.getConstructor().newInstance() as DebugToolDialogFragment
18 | target.show(activity.getSupportFragmentManager(), "debug_tool")
19 | } catch (e: Exception) {
20 | throw RuntimeException(e)
21 | }
22 | //方法二:使用DebugToolsManager,只支持FragmentActivity
23 | DebugToolsManager.instance.show(this)
24 | }
25 | ```
26 | #### How To
27 | - Step 1. Add the JitPack repository to your build file
28 | Add it in your root build.gradle at the end of repositories:
29 | ```gradle
30 | allprojects {
31 | repositories {
32 | ...
33 | maven { url 'https://jitpack.io' }
34 | }
35 | }
36 | ```
37 | - Step 2. Add the dependency
38 | ```gradle
39 | dependencies {
40 | implementation "com.github.Peakmain:DebugTools:+"
41 | }
42 | ```
43 | #### 日志捕获分享框架
44 | 
45 | - App中初始化
46 | ```kotlin
47 | CrashUtils.init(this)
48 | ```
49 | - 如果对native异常进行捕获,还需要拷贝[libbreakpad.aar](https://github.com/Peakmain/DebugTools/tree/master/debug/libs)到libs目录下
50 | #### 网络抓包工具
51 | 
52 |
53 | - 网络请求添加拦截器
54 | ```kotlin
55 | OkHttpClient.Builder builder = new OkHttpClient.Builder();
56 | builder.addInterceptor(new com.peakmain.debug.log.HttpLoggingInterceptor());
57 | ```
58 | ##### 一、DebugTools功能
59 | 1. 支持查看最新接口前100条数据
60 | 2. 支持正序和倒序排序
61 | 3. 可查看每个接口的Header,请求参数与返回结果
62 | 4. 支持分享给开发
63 |
64 | ##### 二、所有Activity显示悬浮按钮点击显示网络抓包工具
65 | - 可以使用我的另一个第三方库:https://github.com/Peakmain/BasicUI 的SuspensionView
66 | - demo代码如下,在自己的基本Activity中调用下方代码即可
67 | ```kotlin
68 | fun addSuspensionView(activity: AppCompatActivity) {
69 | val suspensionView = SuspensionView(
70 | activity, com.atour.atourlife.R.drawable.ui_ic_suspension_setting,
71 | 56f, 60f, 20f, null, 0
72 | )
73 | activity.addContentView(
74 | suspensionView,
75 | FrameLayout.LayoutParams(
76 | FrameLayout.LayoutParams.MATCH_PARENT,
77 | FrameLayout.LayoutParams.MATCH_PARENT
78 | )
79 | )
80 | suspensionView.setSuspensionViewClick {
81 | //方法一
82 | var clazz: Class<*>? = null
83 | try {
84 | clazz = Class.forName("com.peakmain.debug.DebugToolDialogFragment")
85 | val target: DebugToolDialogFragment =
86 | clazz.getConstructor().newInstance() as DebugToolDialogFragment
87 | target.show(activity.getSupportFragmentManager(), "debug_tool")
88 | } catch (e: Exception) {
89 | throw RuntimeException(e)
90 | }
91 | //方法二:使用DebugToolsManager,只支持FragmentActivity
92 | DebugToolsManager.instance.show(this)
93 | }
94 | }
95 | ```
96 | ##### 三、 Jenkins智能控制开关
97 | 1. Android在项目的build.gradle(一般都是app/build.gradle),利用project.property获取属性,比如我这里属性名是IS_LOG_CONSONLE_ENABLE
98 | ```gradle
99 | def releaseLogConsoleEnable = project.property('IS_LOG_CONSONLE_ENABLE')
100 | ```
101 |
102 | 2. buildTypes中通过buildConfigField方法,将属性添加BuildConfig
103 | ```gradle
104 | buildTypes {
105 | release {
106 | buildConfigField "boolean", "releaseLogConsoleEnable", releaseLogConsoleEnable
107 | }
108 | debug {
109 | buildConfigField "boolean", "releaseLogConsoleEnable", releaseLogConsoleEnable
110 |
111 | }
112 | }
113 | ```
114 | 3. 显示开关按钮的地方,添加代码开关
115 | ```kotlin
116 | if(BuildConfig.releaseLogConsoleEnable) {
117 | addSuspensionView(this);
118 | }
119 | ```
120 | 至此Android相关代码配置完成,接下来是Jenkins
121 |
122 | 4. Jenkins添加选项设置属性为IS_LOG_CONSONLE_ENABLE
123 |
124 |
125 | 5. Jenkins gradle配置代码-PIS_LOG_CONSONLE_ENABLE=$IS_LOG_CONSONLE_ENABLE
126 | ```gradle
127 | ./gradlew -Dgradle.user.home=$GRADLE_HOME clean assemble$buildType -b ${WORKSPACE}/app/build.gradle -PIS_LOG_CONSONLE_ENABLE=$IS_LOG_CONSONLE_ENABLE
128 | ```
129 |
130 |
131 | #### fps监控
132 | 
133 | - App的AndroidManifest.xml需要添加悬浮窗权限
134 | ```kotlin
135 |
136 | ```
137 | - 开启悬浮窗权限即可
138 |
139 | #### 环境切换
140 | 
141 | ##### 一、initEnvironmentExchangeBeanList:初始化http环境列表
142 | ```kotlin
143 | fun initEnvironmentExchangeBeanList(
144 | environmentExchangeBeans: MutableList,
145 | selectEnvironmentCallback: ((EnvironmentExchangeBean) -> Unit)? = null,
146 | )
147 | ```
148 | - 第一个参数environmentExchangeBeans表示http环境列表
149 | - 第二个参数表示选中某一个http环境的回调
150 |
151 | ##### 二、initH5EnvironmentExchangeBeanList:初始化http环境列表
152 | ```kotlin
153 | fun initH5EnvironmentExchangeBeanList(
154 | environmentExchangeBeans: MutableList,
155 | selectH5EnvironmentCallback: ((EnvironmentExchangeBean) -> Unit)? = null,
156 | )
157 | ```
158 | - 第一个参数environmentExchangeBeans表示H5环境列表
159 | - 第二个参数表示选中某一个H5环境的回调
160 |
161 | ##### demo如下
162 | ```kotlin
163 | var mEnvironmentExchangeBeans: MutableList = ArrayList()//初始化原生环境列表
164 | var mH5EnvironmentExchangeBeans: MutableList = ArrayList()//初始化H5环境列表
165 | findViewById(R.id.tv_name).setOnClickListener {
166 | DebugToolsManager.instance
167 | .initEnvironmentExchangeBeanList(mEnvironmentExchangeBeans) {
168 | ToastUtils.showLong("当前选中的环境是:${it.title},url是:${it.url}")
169 | }.initH5EnvironmentExchangeBeanList(mH5EnvironmentExchangeBeans){
170 | LogUtils.e("当前选中的H5环境是:${it.title},url是:${it.url}")
171 | ToastUtils.showLong("当前选中的H5环境是:${it.title},url是:${it.url}")
172 | }
173 | .show(this)
174 | }
175 | ```
176 | - EnvironmentExchangeBean有三个参数:title(标题)、url(http或者H5链接)、isSelected(是否被选中)
177 | ```kotlin
178 | data class EnvironmentExchangeBean(
179 | val title: String,
180 | val url: String,
181 | var isSelected: Boolean = false
182 | )
183 | ```
184 | - 当http环境列表有多个环境的isSelected被设置为true,则只有第一个默认是被设置为true,其他则会被设置为false。H5环境列表也是同理。
185 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.application'
3 | id 'kotlin-android'
4 | }
5 | def androidId = rootProject.ext.android
6 | android {
7 | compileSdkVersion 31
8 | compileSdkVersion androidId.androidCompileSdkVersion
9 |
10 |
11 | defaultConfig {
12 | applicationId "com.peakmain.debugtools"
13 | minSdkVersion androidId.androidMinSdkVersion
14 | targetSdkVersion androidId.androidTargetSdkVersion
15 | versionCode 1
16 | versionName "1.0"
17 |
18 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
19 | }
20 |
21 | buildTypes {
22 | release {
23 | minifyEnabled false
24 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
25 | }
26 | }
27 | compileOptions {
28 | sourceCompatibility JavaVersion.VERSION_1_8
29 | targetCompatibility JavaVersion.VERSION_1_8
30 | }
31 | kotlinOptions {
32 | jvmTarget = '1.8'
33 | }
34 | dataBinding{
35 | enabled true
36 | }
37 | }
38 |
39 | dependencies {
40 |
41 | implementation "androidx.core:core-ktx:$kotlin_version"
42 | implementation 'androidx.appcompat:appcompat:1.5.0'
43 | implementation 'com.google.android.material:material:1.6.1'
44 | implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
45 | testImplementation 'junit:junit:4.+'
46 | androidTestImplementation 'androidx.test.ext:junit:1.1.3'
47 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
48 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
49 | debugImplementation project(path: ':debug')
50 | }
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/peakmain/debugtools/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.peakmain.debugtools
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22 | assertEquals("com.peakmain.debugtools", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
18 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/app/src/main/java/com/peakmain/debugtools/App.kt:
--------------------------------------------------------------------------------
1 | package com.peakmain.debugtools
2 |
3 | import android.app.Application
4 | import com.peakmain.debug.utils.CrashUtils
5 |
6 | /**
7 | * author :Peakmain
8 | * createTime:2022/3/13
9 | * mail:2726449200@qq.com
10 | * describe:
11 | */
12 | class App : Application() {
13 | companion object {
14 | @JvmStatic
15 | lateinit var mApplication: Application
16 | }
17 |
18 | override fun onCreate() {
19 | super.onCreate()
20 | CrashUtils.init(this)
21 | mApplication = this
22 | }
23 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/peakmain/debugtools/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.peakmain.debugtools
2 |
3 | import android.os.Bundle
4 | import android.util.Log
5 | import android.widget.TextView
6 | import androidx.appcompat.app.AppCompatActivity
7 | import com.peakmain.basiclibrary.network.RetrofitManager
8 | import com.peakmain.basiclibrary.network.status.ApiStatus
9 | import com.peakmain.debug.base.EnvironmentExchangeBean
10 | import com.peakmain.debug.manager.DebugToolsManager
11 | import com.peakmain.ui.utils.LogUtils
12 | import com.peakmain.ui.utils.ToastUtils
13 |
14 | class MainActivity : AppCompatActivity() {
15 | private var api = RetrofitManager.createService(WanAndroidService::class.java) {
16 | RetrofitUtils.create(WanAndroidService::class.java)
17 | }
18 | var mEnvironmentExchangeBeans: MutableList = ArrayList()
19 | var mH5EnvironmentExchangeBeans: MutableList = ArrayList()
20 |
21 | override fun onCreate(savedInstanceState: Bundle?) {
22 | super.onCreate(savedInstanceState)
23 | setContentView(R.layout.activity_main)
24 | initData()
25 | findViewById(R.id.tv_name).setOnClickListener {
26 | DebugToolsManager.instance
27 | .initEnvironmentExchangeBeanList(mEnvironmentExchangeBeans) {
28 | ToastUtils.showLong("当前选中的环境是:${it.title},url是:${it.url}")
29 | }.initH5EnvironmentExchangeBeanList(mH5EnvironmentExchangeBeans){
30 | LogUtils.e("当前选中的H5环境是:${it.title},url是:${it.url}")
31 | //ToastUtils.showLong("当前选中的H5环境是:${it.title},url是:${it.url}")
32 | }
33 | .show(this)
34 | }
35 | RetrofitManager.createData(api.getBannerJson(), object : ApiStatus() {
36 | override fun error(exception: Exception) {
37 | }
38 |
39 | override fun success(t: Any) {
40 | // Log.e("TAG",t.toString())
41 |
42 | }
43 |
44 | })
45 | RetrofitManager.createData(api.getListJson(1, 294), object : ApiStatus() {
46 | override fun error(exception: Exception) {
47 | }
48 |
49 | override fun success(t: Any) {
50 | // Log.e("TAG",t.toString())
51 |
52 | }
53 |
54 | })
55 | RetrofitManager.createData(api.loginUser("test", "123456"), object : ApiStatus() {
56 | override fun error(exception: Exception) {
57 | }
58 |
59 | override fun success(t: Any) {
60 | Log.e("TAG", t.toString())
61 | }
62 |
63 | })
64 | }
65 |
66 | private fun initData() {
67 | mEnvironmentExchangeBeans.add(
68 | EnvironmentExchangeBean(
69 | "qa0",
70 | "https://www.baidu.com",
71 | false
72 | )
73 | )
74 | mEnvironmentExchangeBeans.add(
75 | EnvironmentExchangeBean(
76 | "qa1",
77 | "https://www.baidu1.com",
78 | true
79 | )
80 | )
81 | mEnvironmentExchangeBeans.add(
82 | EnvironmentExchangeBean(
83 | "qa2",
84 | "https://www.baidu2.com",
85 | true
86 | )
87 | )
88 | mEnvironmentExchangeBeans.add(
89 | EnvironmentExchangeBean(
90 | "qa3",
91 | "https://www.baidu3.com",
92 | false
93 | )
94 | )
95 | mH5EnvironmentExchangeBeans.add(
96 | EnvironmentExchangeBean(
97 | "qa0",
98 | "https://www.baidu.com",
99 | false
100 | )
101 | )
102 | mH5EnvironmentExchangeBeans.add(
103 | EnvironmentExchangeBean(
104 | "qa1",
105 | "https://www.baidu1.com",
106 | true
107 | )
108 | )
109 |
110 | mH5EnvironmentExchangeBeans.add(
111 | EnvironmentExchangeBean(
112 | "qa2",
113 | "https://www.baidu2.com",
114 | false
115 | )
116 | )
117 | mH5EnvironmentExchangeBeans.add(
118 | EnvironmentExchangeBean(
119 | "qa3",
120 | "https://www.baidu3.com",
121 | false
122 | )
123 | )
124 | }
125 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/peakmain/debugtools/RetrofitUtils.java:
--------------------------------------------------------------------------------
1 | package com.peakmain.debugtools;
2 |
3 |
4 | import android.webkit.WebSettings;
5 |
6 | import com.google.gson.GsonBuilder;
7 | import com.google.gson.TypeAdapter;
8 | import com.google.gson.stream.JsonReader;
9 | import com.google.gson.stream.JsonToken;
10 | import com.google.gson.stream.JsonWriter;
11 |
12 | import java.io.IOException;
13 | import java.util.ArrayList;
14 | import java.util.Map;
15 | import java.util.concurrent.ConcurrentHashMap;
16 | import java.util.concurrent.TimeUnit;
17 |
18 | import okhttp3.HttpUrl;
19 | import okhttp3.Interceptor;
20 | import okhttp3.OkHttpClient;
21 | import okhttp3.Request;
22 | import retrofit2.Retrofit;
23 | import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
24 | import retrofit2.converter.gson.GsonConverterFactory;
25 |
26 | /**
27 | * author :Peakmain
28 | * createTime:2022/5/16
29 | * mail:2726449200@qq.com
30 | * describe:
31 | */
32 | public class RetrofitUtils {
33 | private static Retrofit retrofit = null;
34 | private static Map services = new ConcurrentHashMap<>();
35 |
36 | public static Retrofit getInstance() {
37 |
38 | if (retrofit == null) {
39 | OkHttpClient.Builder builder = new OkHttpClient.Builder();
40 | builder.addInterceptor(addQueryParameterInterceptor());
41 | builder.addInterceptor(new com.peakmain.debug.log.HttpLoggingInterceptor());
42 | //设置超时
43 | builder.connectTimeout(20, TimeUnit.SECONDS)
44 | .readTimeout(20, TimeUnit.SECONDS)
45 | .writeTimeout(20, TimeUnit.SECONDS);
46 |
47 | OkHttpClient client = builder.build();
48 | retrofit = new Retrofit.Builder()
49 | .baseUrl("https://www.wanandroid.com/")
50 | //增加返回值为Gson的支持(以实体类返回)
51 |
52 | .addConverterFactory(GsonConverterFactory.create(new GsonBuilder().
53 | registerTypeAdapter(String.class, new TypeAdapter() {
54 | @Override
55 | public void write(JsonWriter writer, String value) throws IOException {
56 | if (value == null) {
57 | // 在这里处理null改为空字符串
58 | writer.value("");
59 | return;
60 | }
61 | writer.value(value);
62 |
63 | }
64 |
65 | @Override
66 | public String read(JsonReader reader) throws IOException {
67 | if (reader.peek() == JsonToken.NULL) {
68 | reader.nextNull();
69 | return "";
70 | }
71 | return reader.nextString();
72 |
73 | }
74 | }).create()))
75 | //增加返回值为Oservable的支持
76 | .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
77 | .client(client)
78 | .build();
79 | }
80 | return retrofit;
81 | }
82 |
83 |
84 | @SuppressWarnings("unchecked")
85 | public static T create(final Class service) {
86 | if (services.get(service) == null) {
87 | services.put(service, getInstance().create(service));
88 | }
89 | return (T) services.get(service);
90 | }
91 |
92 | public static void changeApiBaseUrl() {
93 | retrofit = null;
94 | services.clear();
95 | }
96 |
97 | /**
98 | * 设置公共参数
99 | */
100 | private static Interceptor addQueryParameterInterceptor() {
101 | return chain -> {
102 | Request originalRequest = chain.request();
103 | HttpUrl.Builder modifiedUrl = originalRequest.url()
104 | .newBuilder();
105 |
106 |
107 | Request newRequest = originalRequest.newBuilder()
108 | .url(modifiedUrl.build()).removeHeader("User-Agent").addHeader("User-Agent",
109 | getUserAgent()).build();
110 | return chain.proceed(newRequest);
111 | };
112 | }
113 |
114 | private static String getUserAgent() {
115 | String userAgent;
116 | try {
117 | userAgent = WebSettings.getDefaultUserAgent(App.mApplication);
118 | } catch (Exception e) {
119 | userAgent = System.getProperty("http.agent");
120 | }
121 | StringBuilder sb = new StringBuilder();
122 | for (int i = 0, length = userAgent.length(); i < length; i++) {
123 | char c = userAgent.charAt(i);
124 | if (c <= '\u001f' || c >= '\u007f') {
125 | sb.append(String.format("\\u%04x", (int) c));
126 | } else {
127 | sb.append(c);
128 | }
129 | }
130 | return sb.toString();
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/app/src/main/java/com/peakmain/debugtools/WanAndroidService.kt:
--------------------------------------------------------------------------------
1 | package com.peakmain.debugtools
2 |
3 | import io.reactivex.Observable
4 | import retrofit2.http.*
5 |
6 |
7 | /**
8 | * author :Peakmain
9 | * createTime:2022/5/16
10 | * mail:2726449200@qq.com
11 | * describe:
12 | */
13 | interface WanAndroidService {
14 | @GET("banner/json")
15 | fun getBannerJson(): Observable
16 |
17 | @GET("project/list/{id}/json/")
18 | fun getListJson(@Path("id") id: Int, @Query("cid") cid: Int): Observable
19 |
20 | @FormUrlEncoded
21 | @POST("user/login")
22 | fun loginUser(
23 | @Field("username") username: String,
24 | @Field("password") password: String
25 | ): Observable
26 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
18 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Peakmain/DebugTools/fb99d544cde1fc7904f9e1483e01d9d0284f7ffc/app/src/main/res/mipmap-hdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Peakmain/DebugTools/fb99d544cde1fc7904f9e1483e01d9d0284f7ffc/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Peakmain/DebugTools/fb99d544cde1fc7904f9e1483e01d9d0284f7ffc/app/src/main/res/mipmap-mdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Peakmain/DebugTools/fb99d544cde1fc7904f9e1483e01d9d0284f7ffc/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Peakmain/DebugTools/fb99d544cde1fc7904f9e1483e01d9d0284f7ffc/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Peakmain/DebugTools/fb99d544cde1fc7904f9e1483e01d9d0284f7ffc/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Peakmain/DebugTools/fb99d544cde1fc7904f9e1483e01d9d0284f7ffc/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Peakmain/DebugTools/fb99d544cde1fc7904f9e1483e01d9d0284f7ffc/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Peakmain/DebugTools/fb99d544cde1fc7904f9e1483e01d9d0284f7ffc/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Peakmain/DebugTools/fb99d544cde1fc7904f9e1483e01d9d0284f7ffc/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/values-night/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFBB86FC
4 | #FF6200EE
5 | #FF3700B3
6 | #FF03DAC5
7 | #FF018786
8 | #FF000000
9 | #FFFFFFFF
10 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | DebugTools
3 |
--------------------------------------------------------------------------------
/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/network_security_config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/test/java/com/peakmain/debugtools/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.peakmain.debugtools
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | buildscript {
3 | ext.kotlin_version = '1.6.10'
4 | apply from :"config.gradle"
5 | repositories {
6 | google()
7 | mavenCentral()
8 | maven { url 'https://jitpack.io' }
9 | }
10 | dependencies {
11 | classpath 'com.android.tools.build:gradle:4.0.2'
12 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
13 |
14 | // NOTE: Do not place your application dependencies here; they belong
15 | // in the individual module build.gradle files
16 | }
17 | }
18 | allprojects {
19 | repositories {
20 | google()
21 | jcenter()
22 | mavenCentral()
23 | maven { url 'https://jitpack.io' }
24 | }
25 | }
26 |
27 | task clean(type: Delete) {
28 | delete rootProject.buildDir
29 | }
--------------------------------------------------------------------------------
/config.gradle:
--------------------------------------------------------------------------------
1 | ext {
2 | android = [
3 | androidCompileSdkVersion: 31,
4 | androidMinSdkVersion : 21,
5 | androidTargetSdkVersion : 31,
6 | ]
7 | }
--------------------------------------------------------------------------------
/debug/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/debug/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.library'
3 | id 'kotlin-android'
4 | }
5 | def androidId = rootProject.ext.android
6 | android {
7 | compileSdkVersion androidId.androidCompileSdkVersion
8 |
9 | defaultConfig {
10 | minSdkVersion androidId.androidMinSdkVersion
11 | targetSdkVersion androidId.androidTargetSdkVersion
12 | versionCode 1
13 | versionName "1.0"
14 |
15 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
16 | }
17 |
18 | buildTypes {
19 | release {
20 | minifyEnabled false
21 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
22 | buildConfigField("String", "BUILD_TIME", "\"" + this.buildTime() + "\"")
23 | }
24 | debug {
25 | buildConfigField("String", "BUILD_TIME", "\"" + this.buildTime() + "\"")
26 | }
27 | }
28 | compileOptions {
29 | sourceCompatibility JavaVersion.VERSION_1_8
30 | targetCompatibility JavaVersion.VERSION_1_8
31 | }
32 | kotlinOptions {
33 | jvmTarget = '1.8'
34 | }
35 | buildFeatures {
36 | dataBinding true
37 | }
38 | }
39 |
40 | static def buildTime() {
41 | return new Date().format("yyyy:MM:dd HH:mm:ss", TimeZone.getTimeZone("GMT+08:00"))
42 | }
43 |
44 | dependencies {
45 | compileOnly files('libs/libbreakpad.aar')
46 | implementation "androidx.core:core-ktx:$kotlin_version"
47 | implementation 'com.google.android.material:material:1.6.1'
48 | api 'com.github.Peakmain:BasicLibrary:1.1.7'
49 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
50 | api 'com.github.Peakmain:BasicUI:1.2.8'
51 | }
52 | repositories {
53 | maven { url 'https://dl.bintray.com/kotlin/kotlin-eap' }
54 | mavenCentral()
55 | }
56 |
--------------------------------------------------------------------------------
/debug/libs/libbreakpad.aar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Peakmain/DebugTools/fb99d544cde1fc7904f9e1483e01d9d0284f7ffc/debug/libs/libbreakpad.aar
--------------------------------------------------------------------------------
/debug/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/debug/src/androidTest/java/com/peakmain/debug/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.peakmain.debug
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22 | assertEquals("com.peakmain.debug", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/debug/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
12 |
13 |
16 |
17 |
18 |
19 |
21 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/debug/src/main/java/com/peakmain/debug/DebugFileProvider.java:
--------------------------------------------------------------------------------
1 | package com.peakmain.debug;
2 |
3 | import androidx.core.content.FileProvider;
4 |
5 | /**
6 | * author :Peakmain
7 | * createTime:2022/4/27
8 | * mail:2726449200@qq.com
9 | * describe:
10 | */
11 | public class DebugFileProvider extends FileProvider {
12 | }
13 |
--------------------------------------------------------------------------------
/debug/src/main/java/com/peakmain/debug/DebugToolDialogFragment.kt:
--------------------------------------------------------------------------------
1 | package com.peakmain.debug
2 |
3 | import android.content.Context
4 | import android.os.Bundle
5 | import android.text.TextUtils
6 | import android.view.LayoutInflater
7 | import android.view.View
8 | import android.view.ViewGroup
9 | import android.view.WindowManager
10 | import android.widget.TextView
11 | import androidx.appcompat.app.AppCompatDialogFragment
12 | import androidx.core.content.ContextCompat
13 | import androidx.recyclerview.widget.DividerItemDecoration
14 | import androidx.recyclerview.widget.LinearLayoutManager
15 | import androidx.recyclerview.widget.RecyclerView
16 | import com.peakmain.debug.annotation.PDebug
17 | import java.lang.reflect.Method
18 |
19 | /**
20 | * author :Peakmain
21 | * createTime:2022/3/13
22 | * mail:2726449200@qq.com
23 | * describe:
24 | */
25 | class DebugToolDialogFragment : AppCompatDialogFragment() {
26 | private val debugTools = arrayOf(DebugTools::class.java)
27 | private lateinit var mRecyclerView: RecyclerView
28 | override fun onCreateView(
29 | inflater: LayoutInflater,
30 | container: ViewGroup?,
31 | savedInstanceState: Bundle?
32 | ): View? {
33 | val parent = dialog?.window?.findViewById(android.R.id.content) ?: container
34 | val view = inflater.inflate(R.layout.debug_dialog_tool, parent, false)
35 |
36 | dialog?.window?.setLayout(
37 | (getScreenWidth() * 0.7f).toInt(),
38 | WindowManager.LayoutParams.WRAP_CONTENT
39 | )
40 | dialog?.window?.setBackgroundDrawableResource(R.drawable.shape_background_debug_tool)
41 | mRecyclerView=view.findViewById(R.id.debug_recycler_view)
42 | return view
43 | }
44 |
45 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
46 | super.onViewCreated(view, savedInstanceState)
47 |
48 | val itemDecoration = DividerItemDecoration(view.context, DividerItemDecoration.VERTICAL)
49 | itemDecoration.setDrawable(
50 | ContextCompat.getDrawable(
51 | view.context,
52 | R.drawable.shape_divider_debug_tool
53 | )!!
54 | )
55 |
56 | val list = mutableListOf()
57 | val size = debugTools.size
58 | for (index in 0 until size) {
59 | val claz = debugTools[index]
60 | val target = claz.getConstructor().newInstance()
61 | val declaredMethods = target.javaClass.declaredMethods
62 | for (method in declaredMethods) {
63 | var title = ""
64 | var desc = ""
65 | var enable = false
66 | val annotation = method.getAnnotation(PDebug::class.java)
67 |
68 | if (annotation != null) {
69 | title = annotation.name
70 | desc = annotation.desc
71 | enable = true
72 | } else {
73 | method.isAccessible = true
74 | title = method.invoke(target) as String
75 | }
76 |
77 | val func = DebugToolBean(title, desc, method, enable, target)
78 | list.add(func)
79 | }
80 | }
81 |
82 | mRecyclerView.addItemDecoration(itemDecoration)
83 | mRecyclerView.layoutManager =
84 | LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
85 | mRecyclerView.adapter = DebugToolAdapter(list)
86 | }
87 |
88 |
89 | inner class DebugToolAdapter(private val list: List) :
90 | RecyclerView.Adapter() {
91 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
92 | val itemView = layoutInflater.inflate(R.layout.debug_recycler_tool_item, parent, false)
93 | return object : RecyclerView.ViewHolder(itemView) {}
94 | }
95 |
96 | override fun getItemCount(): Int {
97 | return list.size
98 | }
99 |
100 | override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
101 | val debugFunction = list[position]
102 |
103 | val itemTitle = holder.itemView.findViewById(R.id.tv_debug_title)
104 | val itemDesc = holder.itemView.findViewById(R.id.tv_debug_desc)
105 |
106 | itemTitle.text = debugFunction.name
107 | if (TextUtils.isEmpty(debugFunction.desc)) {
108 | itemDesc.visibility = View.GONE
109 | } else {
110 | itemDesc.visibility = View.VISIBLE
111 | itemDesc.text = debugFunction.desc
112 | }
113 |
114 | if (debugFunction.enable) {
115 | holder.itemView.setOnClickListener {
116 | debugFunction.invoke(activity!!)
117 | dismiss()
118 | }
119 | }
120 | }
121 |
122 | }
123 |
124 |
125 | private fun getScreenWidth(): Int = resources.displayMetrics.widthPixels
126 | }
127 |
128 | data class DebugToolBean(
129 | val name: String,
130 | val desc: String,
131 | val method: Method,
132 | val enable: Boolean,
133 | val target: Any
134 | ) {
135 | fun invoke(context: Context) {
136 | method.invoke(target, context)
137 | }
138 | }
--------------------------------------------------------------------------------
/debug/src/main/java/com/peakmain/debug/DebugTools.kt:
--------------------------------------------------------------------------------
1 | package com.peakmain.debug
2 |
3 | import android.content.Context
4 | import android.content.Intent
5 | import android.os.Build
6 | import com.peakmain.debug.activity.CrashLogActivity
7 | import com.peakmain.debug.activity.EnvironmentExchangeActivity
8 | import com.peakmain.debug.annotation.PDebug
9 | import com.peakmain.debug.log.HttpLoggingActivity
10 | import com.peakmain.ui.utils.fps.FpsMonitorUtils
11 |
12 | /**
13 | * author :Peakmain
14 | * createTime:2022/3/13
15 | * mail:2726449200@qq.com
16 | * describe:
17 | */
18 | class DebugTools {
19 | fun buildVersion(): String =
20 | "DebugTools构建版本:${BuildConfig.VERSION_NAME}"
21 |
22 |
23 | fun buildTime(): String =
24 | "构建时间:${BuildConfig.BUILD_TIME}"
25 |
26 |
27 | fun buildEnvironment(): String =
28 | "构建环境: " + if (BuildConfig.DEBUG) "测试环境" else "正式环境"
29 |
30 | fun buildDevice(): String =
31 | "设备信息:" + Build.BRAND + "-" + Build.VERSION.SDK_INT + "-" + Build.CPU_ABI
32 |
33 |
34 | @PDebug(name = "查看Crash日志", desc = "可以一键分享给Android开发,迅速定位偶现问题")
35 | fun crashLog(context: Context) {
36 | val intent = Intent(context, CrashLogActivity::class.java)
37 | context.startActivity(intent)
38 | }
39 |
40 | @PDebug(name = "打开/关闭Fps", desc = "打开后可以查看页面实时的FPS")
41 | fun toggleFps(context: Context) {
42 | FpsMonitorUtils.toggle()
43 | }
44 |
45 | @PDebug(name = "接口抓包工具", desc = "可以查看100条最新的接口数据")
46 | fun httpLogging(context: Context) {
47 | context.startActivity(Intent(context, HttpLoggingActivity::class.java))
48 | }
49 |
50 | @PDebug(name = "环境切换", desc = "一键切换Http和H5环境")
51 | fun exchangeEnvironment(context: Context){
52 | context.startActivity(Intent(context,EnvironmentExchangeActivity::class.java))
53 | }
54 | @PDebug(name = "生成bug", desc = "生成bug,方便测试查看crash日志")
55 | fun createCrash(context: Context) {
56 | 5 / 0
57 | }
58 | }
--------------------------------------------------------------------------------
/debug/src/main/java/com/peakmain/debug/activity/CrashLogActivity.kt:
--------------------------------------------------------------------------------
1 | package com.peakmain.debug.activity
2 |
3 | import android.view.View
4 | import androidx.appcompat.app.AlertDialog
5 | import androidx.core.content.ContextCompat
6 | import androidx.recyclerview.widget.DividerItemDecoration
7 | import androidx.recyclerview.widget.LinearLayoutManager
8 | import com.peakmain.debug.R
9 | import com.peakmain.debug.adapter.CrashLogAdapter
10 | import com.peakmain.debug.base.BaseDebugActivity
11 | import com.peakmain.debug.databinding.DebugActivityCrashLogBinding
12 | import com.peakmain.debug.viewmodel.CrashLogViewModel
13 | import com.peakmain.ui.utils.FileUtils
14 | import com.peakmain.ui.utils.LogUtils
15 | import java.io.File
16 |
17 | /**
18 | * author :Peakmain
19 | * createTime:2022/3/13
20 | * mail:2726449200@qq.com
21 | * describe:
22 | */
23 | class CrashLogActivity(override val layoutId: Int = R.layout.debug_activity_crash_log) :
24 | BaseDebugActivity() {
25 | lateinit var crashFiles: Array
26 | var mAdapter: CrashLogAdapter? = null
27 | override fun initView() {
28 | initToolbar()
29 | val decoration = DividerItemDecoration(this, DividerItemDecoration.VERTICAL)
30 | decoration.setDrawable(
31 | ContextCompat.getDrawable(
32 | this,
33 | R.drawable.shape_divider_debug_tool
34 | )!!)
35 | crashFiles = mViewModel.crashFiles
36 | mBinding.recyclerView.apply {
37 | layoutManager = LinearLayoutManager(this@CrashLogActivity)
38 | mAdapter = CrashLogAdapter(this@CrashLogActivity, crashFiles)
39 | adapter = mAdapter
40 | if (crashFiles.isEmpty()) {
41 | showEmptyView()
42 | } else {
43 | showContentView()
44 | }
45 | addItemDecoration(decoration)
46 | }
47 |
48 |
49 | }
50 |
51 | private fun initToolbar() {
52 | mDefaultNavigationBar
53 | .setTitleText("crash日志")
54 | .setDisplayHomeAsUpEnabled(true)
55 | .setRightResId(R.drawable.ic_debug_clear_all_24)
56 | .showRightView()
57 | .setRightViewClickListener(View.OnClickListener { v ->
58 | AlertDialog.Builder(v.context)
59 | .setTitle("清空日志")
60 | .setMessage("是否确定清空所有日志")
61 | .setNegativeButton("取消"
62 | ) { dialog, _ -> dialog?.dismiss() }
63 | .setPositiveButton("确定"
64 | ) { dialog, _ ->
65 | crashFiles.filter {
66 | crashFiles.isNotEmpty()
67 | }.forEach {
68 | val isSuccess = FileUtils.deleteFile(it)
69 | LogUtils.e("是否成功:$isSuccess")
70 | }
71 | mAdapter?.clearData()
72 | mBinding.recyclerView.showEmptyView()
73 | dialog.dismiss()
74 | }.show()
75 | }).create()
76 |
77 | }
78 |
79 |
80 | }
--------------------------------------------------------------------------------
/debug/src/main/java/com/peakmain/debug/activity/EnvironmentExchangeActivity.kt:
--------------------------------------------------------------------------------
1 | package com.peakmain.debug.activity
2 |
3 | import android.view.View
4 | import androidx.recyclerview.widget.DividerItemDecoration
5 | import com.peakmain.debug.R
6 | import com.peakmain.debug.adapter.EnvironmentExchangeAdapter
7 | import com.peakmain.debug.adapter.H5EnvironmentExchangeAdapter
8 | import com.peakmain.debug.base.BaseDebugActivity
9 | import com.peakmain.debug.databinding.DebugActivityEnvironmentExchangeBinding
10 | import com.peakmain.debug.manager.DebugToolsManager
11 | import com.peakmain.debug.viewmodel.EnvironmentExchangeViewModel
12 |
13 | /**
14 | * author :Peakmain
15 | * createTime:2023/9/27
16 | * mail:2726449200@qq.com
17 | * describe:
18 | */
19 | class EnvironmentExchangeActivity(override val layoutId: Int = R.layout.debug_activity_environment_exchange) :
20 | BaseDebugActivity() {
21 | override fun initView() {
22 | mDefaultNavigationBar.setTitleText("环境切换").setDisplayHomeAsUpEnabled(true).create()
23 | initData()
24 | }
25 |
26 | private fun initData() {
27 | val environmentExchangeList = DebugToolsManager.instance.getEnvironmentExchangeList()
28 | val h5EnvironmentExchangeList = DebugToolsManager.instance.getH5EnvironmentExchangeList()
29 | if (h5EnvironmentExchangeList.isEmpty()) {
30 | mBinding.tvH5Title.visibility = View.GONE
31 | }else{
32 | mBinding.tvH5Title.visibility = View.VISIBLE
33 | H5EnvironmentExchangeAdapter(h5EnvironmentExchangeList).also {
34 | mBinding.rvH5EnvironmentExchange.adapter=it
35 | }
36 | mBinding.rvEnvironmentExchange.addItemDecoration(
37 | DividerItemDecoration(
38 | this,
39 | DividerItemDecoration.VERTICAL
40 | )
41 | )
42 | }
43 | if (environmentExchangeList.isEmpty()&&h5EnvironmentExchangeList.isEmpty()) {
44 | mBinding.rvEnvironmentExchange.showEmptyView()
45 | } else {
46 | EnvironmentExchangeAdapter(environmentExchangeList).also {
47 | mBinding.rvEnvironmentExchange.adapter = it
48 | }
49 |
50 | mBinding.rvEnvironmentExchange.addItemDecoration(
51 | DividerItemDecoration(
52 | this,
53 | DividerItemDecoration.VERTICAL
54 | )
55 | )
56 | }
57 | }
58 | }
--------------------------------------------------------------------------------
/debug/src/main/java/com/peakmain/debug/adapter/CrashLogAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.peakmain.debug.adapter
2 |
3 | import android.content.Context
4 | import android.content.Intent
5 | import android.net.Uri
6 | import android.os.Build
7 | import android.view.LayoutInflater
8 | import android.view.ViewGroup
9 | import android.widget.TextView
10 | import androidx.core.content.FileProvider
11 | import androidx.recyclerview.widget.RecyclerView
12 | import com.peakmain.debug.DebugFileProvider
13 | import com.peakmain.debug.R
14 | import java.io.File
15 |
16 | /**
17 | * author :Peakmain
18 | * createTime:2022/3/14
19 | * mail:2726449200@qq.com
20 | * describe:
21 | */
22 | class CrashLogAdapter(val context: Context, private var crashFiles: Array) :
23 | RecyclerView.Adapter() {
24 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
25 | return object : RecyclerView.ViewHolder(
26 | LayoutInflater.from(context).inflate(
27 | R.layout.debug_recycler_crash_log_item,
28 | parent,
29 | false
30 | )
31 | ) {}
32 | }
33 |
34 | override fun getItemCount(): Int {
35 | return crashFiles.size
36 | }
37 |
38 | override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
39 | val file = crashFiles[position]
40 | holder.itemView.findViewById(R.id.tv_debug_title).text = file.name
41 | holder.itemView.findViewById(R.id.tv_debug_share).setOnClickListener {
42 | val intent = Intent(Intent.ACTION_SEND)
43 | intent.putExtra("subject", "")
44 | intent.putExtra("body", "")
45 |
46 | val uri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
47 | DebugFileProvider.getUriForFile(
48 | context,
49 | "${context.packageName}.peakmain.debug.fileProvider",
50 | file
51 | )
52 | } else {
53 | Uri.fromFile(file)
54 | }
55 | intent.putExtra(Intent.EXTRA_STREAM, uri)//添加文件
56 | if (file.name.endsWith(".txt")) {
57 | intent.type = "text/plain"//纯文本
58 | } else {
59 | intent.type = "application/octet-stream" //二进制文件流
60 | }
61 | context.startActivity(Intent.createChooser(intent, "分享Crash 日志文件"))
62 | }
63 | }
64 | fun clearData() {
65 | crashFiles = arrayOf()
66 | notifyDataSetChanged()
67 | }
68 |
69 |
70 | }
--------------------------------------------------------------------------------
/debug/src/main/java/com/peakmain/debug/adapter/EnvironmentExchangeAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.peakmain.debug.adapter
2 |
3 | import com.peakmain.basiclibrary.adapter.CommonRecyclerDataBindingAdapter
4 | import com.peakmain.basiclibrary.adapter.holder.BaseLibraryViewHolder
5 | import com.peakmain.debug.R
6 | import com.peakmain.debug.base.EnvironmentExchangeBean
7 | import com.peakmain.debug.databinding.DebugRecyclerEnvironmentItemBinding
8 | import com.peakmain.debug.manager.DebugToolsManager
9 | import com.peakmain.debug.manager.H5PreferenceManager
10 |
11 | /**
12 | * author :Peakmain
13 | * createTime:2023/9/27
14 | * mail:2726449200@qq.com
15 | * describe:
16 | */
17 | internal class EnvironmentExchangeAdapter(
18 | data: MutableList,
19 | ) :
20 | CommonRecyclerDataBindingAdapter(
21 | data, R.layout.debug_recycler_environment_item, null
22 | ) {
23 | private var mOldSelectedPosition: Int = -1
24 | private var isClick = false
25 | private var mSaveUrl: String=H5PreferenceManager.instance.getNativeEnvironmentUrl()
26 |
27 | override fun convert(
28 | holder: BaseLibraryViewHolder,
29 | itemData: EnvironmentExchangeBean,
30 | position: Int,
31 | ) {
32 | val itemDataBinding = holder.itemDataBinding
33 | if (!isClick) {
34 | if (!android.text.TextUtils.isEmpty(mSaveUrl)) {
35 | if (mSaveUrl == itemData.url) {
36 | itemData.isSelected=true
37 | defaultSelect(position, itemData)
38 | } else {
39 | itemData.isSelected = false
40 | }
41 | } else if (mOldSelectedPosition == -1 && itemData.isSelected) {
42 | defaultSelect(position, itemData)
43 | H5PreferenceManager.instance.saveNativeEnvironmentUrl(itemData.url)
44 | } else {
45 | itemData.isSelected = false
46 | }
47 | }
48 | itemDataBinding.vm = itemData
49 |
50 | holder.itemView.setOnClickListener {
51 | isClick = true
52 | if (mOldSelectedPosition == position) return@setOnClickListener
53 | updateEnvironmentExchange(itemData, position)
54 | }
55 | }
56 |
57 | private fun defaultSelect(
58 | position: Int,
59 | itemData: EnvironmentExchangeBean,
60 | ) {
61 | mOldSelectedPosition = position
62 | DebugToolsManager.instance.mSelectEnvironmentCallback?.invoke(itemData)
63 | }
64 |
65 | private fun updateEnvironmentExchange(
66 | itemData: EnvironmentExchangeBean,
67 | position: Int,
68 | ) {
69 | itemData.isSelected = true
70 | if (mOldSelectedPosition != -1) {
71 | mData[mOldSelectedPosition].isSelected = false
72 | notifyItemChanged(mOldSelectedPosition)
73 | }
74 | notifyItemChanged(position)
75 | H5PreferenceManager.instance.saveNativeEnvironmentUrl(itemData.url)
76 | DebugToolsManager.instance.mSelectEnvironmentCallback?.invoke(itemData)
77 | mOldSelectedPosition = position
78 | }
79 | }
--------------------------------------------------------------------------------
/debug/src/main/java/com/peakmain/debug/adapter/H5EnvironmentExchangeAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.peakmain.debug.adapter
2 |
3 | import com.peakmain.basiclibrary.adapter.CommonRecyclerDataBindingAdapter
4 | import com.peakmain.basiclibrary.adapter.holder.BaseLibraryViewHolder
5 | import com.peakmain.debug.R
6 | import com.peakmain.debug.base.EnvironmentExchangeBean
7 | import com.peakmain.debug.databinding.DebugRecyclerEnvironmentItemBinding
8 | import com.peakmain.debug.manager.DebugToolsManager
9 | import com.peakmain.debug.manager.H5PreferenceManager
10 |
11 | /**
12 | * author :Peakmain
13 | * createTime:2023/9/27
14 | * mail:2726449200@qq.com
15 | * describe:
16 | */
17 | internal class H5EnvironmentExchangeAdapter(data: MutableList) :
18 | CommonRecyclerDataBindingAdapter(
19 | data, R.layout.debug_recycler_environment_item, null
20 | ) {
21 | private var mOldSelectedPosition: Int = -1
22 | private var isClick = false
23 |
24 |
25 | private var mSaveUrl: String = H5PreferenceManager.instance.getH5EnvironmentUrl()
26 | override fun convert(
27 | holder: BaseLibraryViewHolder,
28 | itemData: EnvironmentExchangeBean,
29 | position: Int,
30 | ) {
31 | val itemDataBinding = holder.itemDataBinding
32 | if (!isClick) {
33 | if (!android.text.TextUtils.isEmpty(mSaveUrl)) {
34 | if (mSaveUrl == itemData.url) {
35 | itemData.isSelected=true
36 | defaultSelect(position, itemData)
37 | } else {
38 | itemData.isSelected = false
39 | }
40 | } else if (mOldSelectedPosition == -1 && itemData.isSelected) {
41 | defaultSelect(position, itemData)
42 | H5PreferenceManager.instance.saveH5EnvironmentUrl(itemData.url)
43 | } else {
44 | itemData.isSelected = false
45 | }
46 | }
47 | itemDataBinding.vm = itemData
48 |
49 | holder.itemView.setOnClickListener {
50 | isClick = true
51 | if (mOldSelectedPosition == position) return@setOnClickListener
52 | updateEnvironmentExchange(itemData, position)
53 | }
54 | }
55 |
56 | private fun defaultSelect(
57 | position: Int,
58 | itemData: EnvironmentExchangeBean,
59 | ) {
60 | mOldSelectedPosition = position
61 | DebugToolsManager.instance.mSelectH5EnvironmentCallback?.invoke(itemData)
62 | }
63 |
64 | private fun updateEnvironmentExchange(
65 | itemData: EnvironmentExchangeBean,
66 | position: Int,
67 | ) {
68 | itemData.isSelected = true
69 | if (mOldSelectedPosition != -1) {
70 | mData[mOldSelectedPosition].isSelected = false
71 | notifyItemChanged(mOldSelectedPosition)
72 | }
73 | notifyItemChanged(position)
74 | H5PreferenceManager.instance.saveH5EnvironmentUrl(itemData.url)
75 | DebugToolsManager.instance.mSelectH5EnvironmentCallback?.invoke(itemData)
76 | mOldSelectedPosition = position
77 | }
78 | }
--------------------------------------------------------------------------------
/debug/src/main/java/com/peakmain/debug/adapter/HttpLoggingAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.peakmain.debug.adapter
2 |
3 | import android.content.Intent
4 | import com.peakmain.basiclibrary.adapter.CommonRecyclerDataBindingAdapter
5 | import com.peakmain.basiclibrary.adapter.holder.BaseLibraryViewHolder
6 | import com.peakmain.debug.R
7 | import com.peakmain.debug.bean.HttpLoggingBean
8 | import com.peakmain.debug.databinding.DebugRecyclerHttpLoggingBinding
9 | import com.peakmain.debug.log.HttpLoggingDetailActivity
10 | import com.peakmain.ui.utils.ActivityUtils
11 |
12 | /**
13 | * author :Peakmain
14 | * createTime:2022/5/16
15 | * mail:2726449200@qq.com
16 | * describe:
17 | */
18 | internal class HttpLoggingAdapter(data: MutableList) :
19 | CommonRecyclerDataBindingAdapter(
20 | data,
21 | R.layout.debug_recycler_http_logging
22 | ) {
23 | override fun convert(
24 | holder: BaseLibraryViewHolder,
25 | itemData: HttpLoggingBean,
26 | position: Int
27 | ) {
28 | val binding = holder.itemDataBinding
29 | binding.vm = itemData
30 | binding.position = position
31 | holder.itemView.setOnClickListener {
32 | it.context.startActivity(
33 | Intent(
34 | it.context,
35 | HttpLoggingDetailActivity::class.java
36 | ).apply {
37 | putExtra("HttpLoggingBean", itemData)
38 | })
39 | }
40 | }
41 |
42 | }
--------------------------------------------------------------------------------
/debug/src/main/java/com/peakmain/debug/annotation/PDebug.kt:
--------------------------------------------------------------------------------
1 | package com.peakmain.debug.annotation
2 |
3 | /**
4 | * author :Peakmain
5 | * createTime:2022/3/13
6 | * mail:2726449200@qq.com
7 | * describe:
8 | */
9 | @Retention(AnnotationRetention.RUNTIME)
10 | @Target(AnnotationTarget.FUNCTION )
11 | annotation class PDebug(val name:String,val desc:String)
--------------------------------------------------------------------------------
/debug/src/main/java/com/peakmain/debug/base/BaseDebugActivity.kt:
--------------------------------------------------------------------------------
1 | package com.peakmain.debug.base
2 |
3 | import android.view.View
4 | import androidx.core.content.ContextCompat
5 | import androidx.databinding.ViewDataBinding
6 | import com.peakmain.basiclibrary.base.activity.BaseActivity
7 | import com.peakmain.basiclibrary.base.viewmodel.BaseViewModel
8 | import com.peakmain.basiclibrary.utils.StatusBarUtils
9 | import com.peakmain.debug.R
10 | import com.peakmain.ui.navigationbar.DefaultNavigationBar
11 |
12 | /**
13 | * author :Peakmain
14 | * createTime:2022/3/14
15 | * mail:2726449200@qq.com
16 | * describe:
17 | */
18 | abstract class BaseDebugActivity : BaseActivity() {
19 |
20 | lateinit var mDefaultNavigationBar: DefaultNavigationBar.Builder
21 | override fun initBefore() {
22 | mDefaultNavigationBar= DefaultNavigationBar.Builder(this, findViewById(android.R.id.content))
23 | .setTitleTextColor(android.R.color.white)
24 | .setToolbarBackgroundColor(R.color.ui_color_2F73F6)
25 | .setNavigationOnClickListener(View.OnClickListener {
26 | finish()
27 | })
28 | .setDisplayHomeAsUpEnabled(true)
29 | .setRightResId(R.drawable.ic_debug_clear_all_24)
30 | .hideRightView()
31 | .hideLeftText()
32 | StatusBarUtils.setColor(this, ContextCompat.getColor(this,R.color.ui_color_2F73F6),255)
33 | }
34 | }
--------------------------------------------------------------------------------
/debug/src/main/java/com/peakmain/debug/base/EnvironmentExchangeBean.kt:
--------------------------------------------------------------------------------
1 | package com.peakmain.debug.base
2 |
3 | /**
4 | * author :Peakmain
5 | * createTime:2023/9/27
6 | * mail:2726449200@qq.com
7 | * describe:
8 | */
9 | data class EnvironmentExchangeBean(
10 | val title: String,
11 | val url: String,
12 | var isSelected: Boolean = false
13 |
14 | )
--------------------------------------------------------------------------------
/debug/src/main/java/com/peakmain/debug/bean/HttpLoggingBean.kt:
--------------------------------------------------------------------------------
1 | package com.peakmain.debug.bean
2 |
3 | /**
4 | * author :Peakmain
5 | * createTime:2022/5/16
6 | * mail:2726449200@qq.com
7 | * describe:
8 | */
9 | data class HttpLoggingBean(
10 | var requestStartMessage: String? = null,
11 | var requestUrl: String? = null,
12 | var requestBody: String? = null,
13 | var responseContent: String? = null,
14 | var responseBody: String? = null,
15 | var contentType: String? = null,
16 | var contentLength: String? = null,
17 | var headInfo: String? = null,
18 | var result: String? = null
19 | ):java.io.Serializable
--------------------------------------------------------------------------------
/debug/src/main/java/com/peakmain/debug/ext/DebugViewExt.kt:
--------------------------------------------------------------------------------
1 | package com.peakmain.debug.ext
2 |
3 | import android.content.Context
4 | import androidx.core.content.ContextCompat
5 | import com.peakmain.debug.R
6 | import com.peakmain.ui.widget.ShapeTextView
7 |
8 | /**
9 | * author :Peakmain
10 | * createTime:2023/07/31
11 | * mail:2726449200@qq.com
12 | * describe:
13 | */
14 | fun ShapeTextView.updateTextStyle(isSelect: Boolean, context: Context) {
15 | if (isSelect) {
16 | setTextColor(
17 | ContextCompat.getColor(
18 | context,
19 | R.color.white
20 | )
21 | )
22 | setNormalBackgroundColor(
23 | ContextCompat.getColor(
24 | context,
25 | R.color.amber_900
26 | )
27 | )
28 | } else {
29 | setTextColor(
30 | ContextCompat.getColor(
31 | context,
32 | R.color.ui_color_272A2B
33 | )
34 | )
35 | setNormalBackgroundColor(
36 | ContextCompat.getColor(
37 | context,
38 | R.color.white
39 | )
40 | )
41 | }
42 | }
--------------------------------------------------------------------------------
/debug/src/main/java/com/peakmain/debug/log/HttpLoggingActivity.kt:
--------------------------------------------------------------------------------
1 | package com.peakmain.debug.log
2 |
3 | import android.util.Log
4 | import android.view.View
5 | import android.widget.ImageView
6 | import androidx.appcompat.app.AlertDialog
7 | import androidx.lifecycle.Observer
8 | import androidx.recyclerview.widget.LinearLayoutManager
9 | import androidx.recyclerview.widget.RecyclerView
10 | import com.peakmain.debug.R
11 | import com.peakmain.debug.adapter.HttpLoggingAdapter
12 | import com.peakmain.debug.base.BaseDebugActivity
13 | import com.peakmain.debug.bean.HttpLoggingBean
14 | import com.peakmain.debug.databinding.DebugHttpLoggingBinding
15 | import com.peakmain.debug.viewmodel.HttpLoggingViewModel
16 |
17 | /**
18 | * author :Peakmain
19 | * createTime:2022/5/16
20 | * mail:2726449200@qq.com
21 | * describe:
22 | */
23 | class HttpLoggingActivity(override val layoutId: Int = R.layout.debug_http_logging) :
24 | BaseDebugActivity() {
25 | private var mAdapter: HttpLoggingAdapter? = null
26 |
27 | override fun initView() {
28 | initToolbar()
29 | if (mViewModel.mLoggingMutableList.value != null) {
30 | mAdapter = HttpLoggingAdapter(mViewModel.mLoggingMutableList.value!!)
31 | }
32 | mBinding.debugRecyclerView.apply {
33 | adapter = mAdapter
34 | layoutManager = LinearLayoutManager(this@HttpLoggingActivity)
35 | }
36 | val mutableList = mViewModel.mLoggingMutableList.value
37 | if (mutableList != null && mutableList.size > 0) {
38 | mBinding.tvHeader.text = mViewModel.mLoggingMutableList.value!![0].requestUrl
39 | }
40 | mViewModel.mLoggingMutableList.observe(
41 | this
42 | ) {
43 | if (mutableList != null && mutableList.size > 0) {
44 | mBinding.tvHeader.text = mViewModel.mLoggingMutableList.value!![0].requestUrl
45 | }
46 | notifyDataChange(it)
47 | }
48 | }
49 |
50 | private fun notifyDataChange(data: MutableList) {
51 | mAdapter?.setData(data)
52 | }
53 |
54 |
55 | private fun initToolbar() {
56 | val create = mDefaultNavigationBar
57 | .setTitleText("接口抓包工具")
58 | .setDisplayHomeAsUpEnabled(true)
59 | .setRightResId(R.drawable.ic_descending_order)
60 | .showRightView()
61 | .setRightViewClickListener { v ->
62 | val value = mViewModel.isPositiveSequence.value
63 | mViewModel.isPositiveSequence.value = if (value == null) true else !value
64 | mViewModel.mLoggingMutableList.value =
65 | mViewModel.mLoggingMutableList.value?.asReversed()
66 | }.create()
67 |
68 | mViewModel.isPositiveSequence.observe(this
69 | ) {
70 | create?.setRightResId(if (it) R.drawable.ic_positive_sequence else R.drawable.ic_descending_order)
71 | }
72 |
73 | }
74 |
75 | }
--------------------------------------------------------------------------------
/debug/src/main/java/com/peakmain/debug/log/HttpLoggingDetailActivity.kt:
--------------------------------------------------------------------------------
1 | package com.peakmain.debug.log
2 |
3 | import android.content.Intent
4 | import android.text.TextUtils
5 | import android.view.View
6 | import com.peakmain.basiclibrary.viewmodel.EmptyViewModel
7 | import com.peakmain.debug.R
8 | import com.peakmain.debug.base.BaseDebugActivity
9 | import com.peakmain.debug.bean.HttpLoggingBean
10 | import com.peakmain.debug.databinding.ActivityDebugHttpLoggingDetailBinding
11 | import com.peakmain.debug.ext.updateTextStyle
12 |
13 | /**
14 | * author :Peakmain
15 | * createTime:2023/07/26
16 | * mail:2726449200@qq.com
17 | * describe:
18 | */
19 | class HttpLoggingDetailActivity(override val layoutId: Int = R.layout.activity_debug_http_logging_detail) :
20 | BaseDebugActivity() {
21 | private val mHttpLoggingBean by lazy {
22 | intent.getSerializableExtra("HttpLoggingBean") as HttpLoggingBean
23 | }
24 |
25 | override fun initView() {
26 | mDefaultNavigationBar
27 | .setTitleText(mHttpLoggingBean.requestUrl ?: "接口详情")
28 | .setDisplayHomeAsUpEnabled(true)
29 | .create()
30 |
31 | mBinding.tvResult.text = mHttpLoggingBean.headInfo
32 | mBinding.stvHeader.setOnClickListener {
33 | updateSelectTvColor(0)
34 | mBinding.tvResult.text = mHttpLoggingBean.headInfo
35 | }
36 | mBinding.stvRequestBody.setOnClickListener {
37 | updateSelectTvColor(1)
38 | mBinding.tvResult.text = mHttpLoggingBean.requestBody ?: "无数据"
39 | }
40 | mBinding.stvBackResult.setOnClickListener {
41 | updateSelectTvColor(2)
42 | mBinding.tvResult.text = mHttpLoggingBean.result
43 | }
44 | mBinding.ivShare.setOnClickListener {
45 | val intent = Intent(Intent.ACTION_SEND)
46 | intent.type = "text/plain"
47 | val title = mHttpLoggingBean.requestUrl
48 | val shareResult = StringBuffer()
49 | if (!TextUtils.isEmpty(title)) {
50 | intent.putExtra(Intent.EXTRA_SUBJECT, title)
51 | shareResult.append("请求的url:${title}\n\n")
52 | }
53 | val requestBody = mHttpLoggingBean.requestBody
54 | if (requestBody != null)
55 | shareResult.append("请求参数body:${requestBody}\n\n")
56 | else
57 | shareResult.append("请求参数body:无数据\n\n")
58 | val result = mHttpLoggingBean.result
59 | if (!TextUtils.isEmpty(result)) {
60 | shareResult.append("返回的结果是:${result}\n\n")
61 | }
62 | intent.putExtra(Intent.EXTRA_TEXT, shareResult.toString())
63 | startActivity(Intent.createChooser(intent, "接口分享:$title"))
64 | }
65 | mBinding.ivShare.visibility = View.INVISIBLE
66 | }
67 |
68 | private fun updateSelectTvColor(position: Int) {
69 |
70 | mBinding.stvHeader.apply {
71 | updateTextStyle(position == 0, this@HttpLoggingDetailActivity)
72 | }
73 | mBinding.stvRequestBody.apply {
74 | updateTextStyle(position == 1, this@HttpLoggingDetailActivity)
75 | }
76 | mBinding.stvBackResult.apply {
77 | updateTextStyle(position == 2, this@HttpLoggingDetailActivity)
78 | }
79 | mBinding.ivShare.visibility = if (position == 2) View.VISIBLE else View.INVISIBLE
80 | }
81 |
82 | }
--------------------------------------------------------------------------------
/debug/src/main/java/com/peakmain/debug/log/HttpLoggingInterceptor.kt:
--------------------------------------------------------------------------------
1 | package com.peakmain.debug.log
2 |
3 | import com.peakmain.basiclibrary.config.BasicLibraryConfig
4 | import com.peakmain.debug.bean.HttpLoggingBean
5 | import com.peakmain.debug.manager.HttpLoggingManager
6 | import com.peakmain.debug.utils.FormatJson
7 | import com.peakmain.debug.viewmodel.HttpLoggingViewModel
8 | import com.peakmain.ui.utils.HandlerUtils
9 | import okhttp3.*
10 | import okhttp3.internal.http.*
11 | import okio.Buffer
12 | import okio.GzipSource
13 | import java.io.EOFException
14 | import java.io.IOException
15 | import java.nio.charset.Charset
16 | import java.util.*
17 | import java.util.concurrent.TimeUnit
18 |
19 | /**
20 | * author :Peakmain
21 | * createTime:2022/5/16
22 | * mail:2726449200@qq.com
23 | * describe:
24 | */
25 | class HttpLoggingInterceptor : Interceptor {
26 |
27 | @Volatile
28 | private var headersToRedact = emptySet()
29 | private var mViewModel: HttpLoggingViewModel? = null
30 |
31 | init {
32 | mViewModel = BasicLibraryConfig.getInstance()?.getApp()?.getViewModelProvider()
33 | ?.get(HttpLoggingViewModel::class.java)
34 |
35 | }
36 |
37 | fun redactHeader(name: String) {
38 | val newHeadersToRedact: MutableSet =
39 | TreeSet(java.lang.String.CASE_INSENSITIVE_ORDER)
40 | newHeadersToRedact.addAll(headersToRedact)
41 | newHeadersToRedact.add(name)
42 | headersToRedact = newHeadersToRedact
43 | }
44 |
45 | @Throws(IOException::class)
46 | override fun intercept(chain: Interceptor.Chain): Response {
47 | val request = chain.request()
48 | val requestBody = request.body
49 | val hasRequestBody = requestBody != null
50 | val connection = chain.connection()
51 | var result = "\n————————请求Start————————\n"
52 | val mHttpLoggingBean = HttpLoggingBean()
53 | mHttpLoggingBean.requestUrl = request.url.toString()
54 | mHttpLoggingBean.requestStartMessage = "<-- Start " +
55 | request.method + ' ' + request.url + if (connection != null) " " + connection.protocol() else ""
56 |
57 | if (hasRequestBody) {
58 | mHttpLoggingBean.requestStartMessage += "(${requestBody!!.contentLength()} + \"-byte body)"
59 | }
60 | mHttpLoggingBean.headInfo = ""
61 | if (hasRequestBody) {
62 | mHttpLoggingBean.headInfo = "Header首部:\n"
63 | if (requestBody!!.contentType() != null) {
64 | mHttpLoggingBean.headInfo += "Content-Type: ${requestBody.contentType()};"
65 | mHttpLoggingBean.contentType = "Content-Type: ${requestBody.contentType()};"
66 | }
67 | if (requestBody.contentLength() != -1L) {
68 | mHttpLoggingBean.headInfo += "Content-Length: ${requestBody.contentLength()};"
69 | mHttpLoggingBean.contentLength = "Content-Length: ${requestBody.contentLength()};"
70 | }
71 | val headers = request.headers
72 | var i = 0
73 | val count = headers.size
74 | while (i < count) {
75 | val name = headers.name(i)
76 | // Skip headers from the request body as they are explicitly logged above.
77 | if (!"Content-Type".equals(
78 | name,
79 | ignoreCase = true
80 | ) && !"Content-Length".equals(name, ignoreCase = true)
81 | ) {
82 | logHeader(headers, i, mHttpLoggingBean)
83 | }
84 | i++
85 | }
86 | if (bodyHasUnknownEncoding(request.headers)) {
87 | mHttpLoggingBean.headInfo += "--> END ${request.method} (encoded body omitted)\n"
88 | } else {
89 | val buffer = Buffer()
90 | requestBody.writeTo(buffer)
91 | var charset = UTF8
92 | val contentType = requestBody.contentType()
93 | if (contentType != null) {
94 | charset = contentType.charset(UTF8)
95 | }
96 |
97 | // logger.log("");
98 | if (isPlaintext(buffer)) {
99 | val body = "请求参数body:" + buffer.readString(charset) + "\n"
100 | mHttpLoggingBean.requestBody = body
101 | mHttpLoggingBean.requestBody += "--> END ${request.method} (${requestBody.contentLength()} -byte body)\n\n"
102 | mHttpLoggingBean.headInfo += "\n\n" + body
103 | mHttpLoggingBean.headInfo += "--> END ${request.method} (${requestBody.contentLength()} -byte body)\n\n"
104 | } else {
105 | mHttpLoggingBean.headInfo += "--> END ${request.method} (binary ${requestBody.contentLength()} -byte body omitted)"
106 | }
107 | }
108 | }
109 | mHttpLoggingBean.responseContent = ""
110 | val startNs = System.nanoTime()
111 | val response: Response = try {
112 | chain.proceed(request)
113 | } catch (e: Exception) {
114 | mHttpLoggingBean.responseContent += "<-- HTTP FAILED: $e"
115 | throw e
116 | }
117 | val tookMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs)
118 | val responseBody = response.body
119 | val contentLength = responseBody!!.contentLength()
120 | val bodySize = if (contentLength != -1L) "$contentLength-byte" else "unknown-length"
121 | mHttpLoggingBean.responseContent = "<--\n响应的结果:${response.code}(" + if (response.message
122 | .isEmpty()
123 | ) "" else ' ' + response.message + ")\n响应的url链接: ${
124 | response.request.url
125 | }" + " (" + tookMs + "ms," + ("$bodySize body") + ')'
126 | val headers = response.headers
127 | var i = 0
128 | val count = headers.size
129 | while (i < count) {
130 | logHeader(headers, i, mHttpLoggingBean)
131 | i++
132 | }
133 | if (!hasBody(response)) {
134 | result += "<-- END HTTP\n"
135 | } else if (bodyHasUnknownEncoding(response.headers)) {
136 | result += "<-- END HTTP (encoded body omitted)\n"
137 | } else {
138 | val source = responseBody.source()
139 | source.request(Long.MAX_VALUE) // Buffer the entire body.
140 | var buffer = source.buffer()
141 | var gzippedLength: Long? = null
142 | mHttpLoggingBean.responseBody = "返回结果:\n"
143 | if ("gzip".equals(headers["Content-Encoding"], ignoreCase = true)) {
144 | gzippedLength = buffer.size
145 | var gzippedResponseBody: GzipSource? = null
146 | try {
147 | gzippedResponseBody = GzipSource(buffer.clone())
148 | buffer = Buffer()
149 | buffer.writeAll(gzippedResponseBody)
150 | } finally {
151 | gzippedResponseBody?.close()
152 | }
153 | }
154 | var charset = UTF8
155 | val contentType = responseBody.contentType()
156 | if (contentType != null) {
157 | charset = contentType.charset(UTF8)
158 | }
159 | if (!isPlaintext(buffer)) {
160 | mHttpLoggingBean.responseBody += "<-- END HTTP (binary \" + buffer.size() + \"-byte body omitted)\n"
161 | return response
162 | }
163 | if (contentLength != 0L) {
164 | val resultBuffer = FormatJson.format(buffer.clone().readString(charset))
165 | mHttpLoggingBean.result = resultBuffer
166 | mHttpLoggingBean.responseBody += resultBuffer + "\n"
167 | }
168 | mHttpLoggingBean.responseBody += if (gzippedLength != null) {
169 | "<-- END HTTP (" + buffer.size + "-byte, " + gzippedLength + "-gzipped-byte body)\n"
170 |
171 | } else {
172 | "<-- END HTTP (" + buffer.size + "-byte body)\n"
173 | }
174 | }
175 | result += "————————请求End————————\n"
176 | HttpLoggingManager.instance.addFirst(mHttpLoggingBean)
177 | HandlerUtils.runOnUiThread {
178 | mViewModel?.mLoggingMutableList?.value = HttpLoggingManager.instance.getHttpLoggingList()
179 | }
180 | return response
181 | }
182 |
183 | fun hasBody(response: Response): Boolean {
184 | return response.body != null && response.body?.contentLength() != 0L
185 |
186 | }
187 |
188 | private fun logHeader(headers: Headers, i: Int, mHttpLoggingBean: HttpLoggingBean) {
189 | val value = if (headersToRedact.contains(headers.name(i))) "██" else headers.value(i)
190 | mHttpLoggingBean.headInfo += headers.name(i) + ": " + value + ";"
191 |
192 | }
193 |
194 | companion object {
195 | private val UTF8 = Charset.forName("UTF-8")
196 |
197 | /**
198 | * Returns true if the body in question probably contains human readable text. Uses a small sample
199 | * of code points to detect unicode control characters commonly used in binary file signatures.
200 | */
201 | fun isPlaintext(buffer: Buffer): Boolean {
202 | return try {
203 | val prefix = Buffer()
204 | val byteCount = if (buffer.size < 64) buffer.size else 64
205 | buffer.copyTo(prefix, 0, byteCount)
206 | for (i in 0..15) {
207 | if (prefix.exhausted()) {
208 | break
209 | }
210 | val codePoint = prefix.readUtf8CodePoint()
211 | if (Character.isISOControl(codePoint) && !Character.isWhitespace(codePoint)) {
212 | return false
213 | }
214 | }
215 | true
216 | } catch (e: EOFException) {
217 | false // Truncated UTF-8 sequence.
218 | }
219 | }
220 |
221 | private fun bodyHasUnknownEncoding(headers: Headers): Boolean {
222 | val contentEncoding = headers["Content-Encoding"]
223 | return (contentEncoding != null && !contentEncoding.equals(
224 | "identity",
225 | ignoreCase = true
226 | )
227 | && !contentEncoding.equals("gzip", ignoreCase = true))
228 | }
229 | }
230 | }
--------------------------------------------------------------------------------
/debug/src/main/java/com/peakmain/debug/manager/DebugToolsManager.kt:
--------------------------------------------------------------------------------
1 | package com.peakmain.debug.manager
2 |
3 | import androidx.fragment.app.FragmentActivity
4 | import com.peakmain.debug.DebugToolDialogFragment
5 | import com.peakmain.debug.base.EnvironmentExchangeBean
6 |
7 | /**
8 | * author :Peakmain
9 | * createTime:2023/9/27
10 | * mail:2726449200@qq.com
11 | * describe:
12 | */
13 | class DebugToolsManager private constructor() {
14 | private var mEnvironmentExchangeBeans: MutableList = ArrayList()
15 | private var mH5EnvironmentExchangeBeans: MutableList = ArrayList()
16 | var mSelectEnvironmentCallback: ((EnvironmentExchangeBean) -> Unit)? = null
17 | var mSelectH5EnvironmentCallback: ((EnvironmentExchangeBean) -> Unit)? = null
18 | var nativeEnvironmentUrl: String=H5PreferenceManager.instance.getNativeEnvironmentUrl()
19 | var h5EnvironmentUrl: String=H5PreferenceManager.instance.getH5EnvironmentUrl()
20 |
21 | companion object {
22 | @JvmStatic
23 | val instance by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
24 | DebugToolsManager()
25 | }
26 | }
27 |
28 | fun initEnvironmentExchangeBeanList(
29 | environmentExchangeBeans: MutableList,
30 | selectEnvironmentCallback: ((EnvironmentExchangeBean) -> Unit)? = null,
31 | ): DebugToolsManager {
32 | this.mEnvironmentExchangeBeans = environmentExchangeBeans
33 | this.mSelectEnvironmentCallback = selectEnvironmentCallback
34 | return this
35 | }
36 |
37 | fun initH5EnvironmentExchangeBeanList(
38 | environmentExchangeBeans: MutableList,
39 | selectH5EnvironmentCallback: ((EnvironmentExchangeBean) -> Unit)? = null,
40 | ): DebugToolsManager {
41 | this.mH5EnvironmentExchangeBeans = environmentExchangeBeans
42 | this.mSelectH5EnvironmentCallback = selectH5EnvironmentCallback
43 | return this
44 | }
45 |
46 | fun addEnvironmentExchangeBean(environmentExchangeBean: EnvironmentExchangeBean): DebugToolsManager {
47 | mEnvironmentExchangeBeans.add(environmentExchangeBean)
48 | return this
49 | }
50 |
51 | fun addH5EnvironmentExchangeBean(environmentExchangeBean: EnvironmentExchangeBean): DebugToolsManager {
52 | mH5EnvironmentExchangeBeans.add(environmentExchangeBean)
53 | return this
54 | }
55 |
56 | fun getEnvironmentExchangeList(): MutableList {
57 | return mEnvironmentExchangeBeans
58 | }
59 |
60 | fun getH5EnvironmentExchangeList(): MutableList {
61 | return mH5EnvironmentExchangeBeans
62 | }
63 |
64 | fun show(context: FragmentActivity) {
65 | val clazz = Class.forName("com.peakmain.debug.DebugToolDialogFragment")
66 | val target = clazz.getConstructor().newInstance() as DebugToolDialogFragment
67 | target.show(context.supportFragmentManager, "debug_tool")
68 | }
69 | }
--------------------------------------------------------------------------------
/debug/src/main/java/com/peakmain/debug/manager/H5PreferenceManager.kt:
--------------------------------------------------------------------------------
1 | package com.peakmain.debug.manager
2 |
3 | import com.peakmain.ui.utils.PreferencesUtil
4 |
5 | /**
6 | * author :Peakmain
7 | * createTime:2023/12/21
8 | * mail:2726449200@qq.com
9 | * describe:
10 | */
11 | internal class H5PreferenceManager private constructor() {
12 | private var mPreferencesUtils: PreferencesUtil? = null
13 |
14 | init {
15 | mPreferencesUtils = PreferencesUtil.instance
16 | }
17 |
18 | companion object {
19 | private const val environmentExchangeKey = "environmentExchangeKey"
20 | private const val h5EnvironmentExchangeKey = "h5EnvironmentExchangeKey"
21 |
22 | @JvmStatic
23 | val instance by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
24 | H5PreferenceManager()
25 | }
26 | }
27 |
28 | fun getH5EnvironmentUrl(): String {
29 | return mPreferencesUtils?.getParam(h5EnvironmentExchangeKey, "") as String? ?: ""
30 | }
31 |
32 | fun saveH5EnvironmentUrl(url: String) {
33 | mPreferencesUtils?.saveParam(h5EnvironmentExchangeKey, url)
34 | }
35 |
36 | fun getNativeEnvironmentUrl(): String {
37 | return mPreferencesUtils?.getParam(environmentExchangeKey, "") as String? ?: ""
38 | }
39 |
40 | fun saveNativeEnvironmentUrl(url: String) {
41 | mPreferencesUtils?.saveParam(environmentExchangeKey, url)
42 | }
43 | }
--------------------------------------------------------------------------------
/debug/src/main/java/com/peakmain/debug/manager/HttpLoggingManager.kt:
--------------------------------------------------------------------------------
1 | package com.peakmain.debug.manager
2 |
3 | import com.peakmain.debug.bean.HttpLoggingBean
4 |
5 | /**
6 | * author :Peakmain
7 | * createTime:2023/12/22
8 | * mail:2726449200@qq.com
9 | * describe:
10 | */
11 | class HttpLoggingManager private constructor(){
12 | private var mHttpLoggingList: MutableList = ArrayList()
13 | companion object{
14 | @JvmStatic
15 | val instance by lazy(LazyThreadSafetyMode.SYNCHRONIZED){
16 | HttpLoggingManager()
17 | }
18 | }
19 |
20 | fun addFirst(httpLoggingBean: HttpLoggingBean){
21 | mHttpLoggingList.apply {
22 | if (size > 100) {
23 | removeAt(size - 1)
24 | }
25 | }
26 | mHttpLoggingList.add(0, httpLoggingBean)
27 | }
28 |
29 | fun getHttpLoggingList():MutableList{
30 | return mHttpLoggingList
31 | }
32 |
33 |
34 | }
--------------------------------------------------------------------------------
/debug/src/main/java/com/peakmain/debug/utils/CrashHelper.kt:
--------------------------------------------------------------------------------
1 | package com.peakmain.debug.utils
2 |
3 | /**
4 | * author :Peakmain
5 | * createTime:2022/3/13
6 | * mail:2726449200@qq.com
7 | * describe:
8 | */
9 | import android.app.ActivityManager
10 | import android.app.Application
11 | import android.content.Context
12 | import android.content.Intent
13 | import android.os.Build
14 | import android.os.Environment
15 | import android.os.Process
16 | import android.os.StatFs
17 | import android.text.format.Formatter
18 | import android.util.Log
19 | import com.peakmain.debug.BuildConfig
20 | import java.io.*
21 | import java.lang.StringBuilder
22 | import java.text.SimpleDateFormat
23 | import java.util.*
24 | import kotlin.system.exitProcess
25 |
26 | /**
27 | * author :Peakmain
28 | * createTime:2021/5/7
29 | * mail:2726449200@qq.com
30 | * describe:
31 | */
32 | internal object CrashHelper {
33 | private var CRASH_DIR = "crash_dir"
34 | private const val TAG = "CrashHelper"
35 | private lateinit var context: Application
36 | fun init(application: Application, crashDir: String) {
37 | context = application
38 | Thread.setDefaultUncaughtExceptionHandler(CaughtExceptionHandler())
39 | CRASH_DIR = crashDir
40 | }
41 |
42 | class CaughtExceptionHandler : Thread.UncaughtExceptionHandler {
43 | private val formatter = SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.CHINA)
44 | private val LAUNCH_TIME = formatter.format(Date())
45 | private val mDefaultExceptionHandler = Thread.getDefaultUncaughtExceptionHandler()
46 | override fun uncaughtException(thread: Thread?, e: Throwable?) {
47 | if (!handleException(e) && mDefaultExceptionHandler != null) {
48 | //默认系统处理
49 | mDefaultExceptionHandler.uncaughtException(thread, e)
50 | }
51 | //重启app
52 | restartApp()
53 | }
54 |
55 | private fun restartApp() {
56 | val intent: Intent? =
57 | context.packageManager?.getLaunchIntentForPackage(context.packageName)
58 | intent?.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
59 | context.startActivity(intent)
60 |
61 | Process.killProcess(Process.myPid())
62 | exitProcess(10)
63 | }
64 |
65 | //是否有异常
66 | private fun handleException(e: Throwable?): Boolean {
67 | if (e == null) return false
68 | val logInfo = createLogInfo(e)
69 | if (BuildConfig.DEBUG) {
70 | Log.e(TAG, logInfo)
71 | }
72 | val file = saveCrashInfoToFile(logInfo)
73 | if (CrashUtils.mListener != null) {
74 | CrashUtils.mListener!!.onFileUploadListener(file)
75 | }
76 | return true
77 | }
78 |
79 | private fun saveCrashInfoToFile(logInfo: String): File {
80 | val crashDir = File(CRASH_DIR)
81 | if (!crashDir.exists()) {
82 | crashDir.mkdirs()
83 | }
84 | val file = File(crashDir, formatter.format(Date()) + "-crash.txt")
85 | file.createNewFile()
86 | Log.e(TAG,file.absolutePath)
87 | val fos = FileOutputStream(file)
88 | try {
89 | fos.write(logInfo.toByteArray())
90 | fos.flush()
91 | } catch (e: Exception) {
92 | e.printStackTrace()
93 | } finally {
94 | fos.close()
95 | }
96 | return file
97 | }
98 |
99 | private fun createLogInfo(e: Throwable): String {
100 | val sb = StringBuilder()
101 | sb.append("brand=${Build.BRAND}\n")
102 | sb.append("rom=${Build.MODEL}\n")
103 | sb.append("os=${Build.VERSION.RELEASE}\n")
104 | sb.append("sdk=${Build.VERSION.SDK_INT}\n")
105 | sb.append("launch_time=${LAUNCH_TIME}\n")//启动app的时间
106 | sb.append("crash_time=${formatter.format(Date())}\n")//crash发生时间
107 | //sb.append("forground=${ActivityUtils.mInstance.isFront}\n")//应用处于前后台
108 | sb.append("thread=${Thread.currentThread().name}\n")//异常线程名
109 | sb.append("cpu_arch=${Build.CPU_ABI}\n")
110 |
111 | //app 信息
112 | val packageInfo = context.packageManager.getPackageInfo(context!!.packageName, 0)
113 | sb.append("versionCode=${packageInfo.versionCode}\n")//版本号
114 | sb.append("versionName=${packageInfo.versionName}\n")
115 | sb.append("packageName=${packageInfo.packageName}\n")
116 | sb.append("requestedPermission=${Arrays.toString(packageInfo.requestedPermissions)}\n")
117 |
118 | val memoryInfo = ActivityManager.MemoryInfo()
119 | val activityManager =
120 | context?.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
121 | activityManager.getMemoryInfo(memoryInfo)
122 | sb.append("availMemory=${
123 | Formatter.formatFileSize(context,
124 | memoryInfo.availMem)
125 | }\n")//可用内存
126 | sb.append("totalMemory=${
127 | Formatter.formatFileSize(context,
128 | memoryInfo.totalMem)
129 | }\n")//设备总内存
130 |
131 | val file = Environment.getExternalStorageDirectory()
132 | //手机内部可用空间
133 | val statFs = StatFs(file.path)
134 | val availableSize = statFs.availableBlocks * statFs.blockSize
135 | sb.append(
136 | "availStorage=${
137 | Formatter.formatFileSize(
138 | context,
139 | availableSize.toLong()
140 | )
141 | }\n"
142 | )
143 | val write: Writer = StringWriter()
144 | val printWriter = PrintWriter(write)
145 | e.printStackTrace(printWriter)
146 | var cause = e.cause
147 | while (cause != null) {
148 | cause.printStackTrace(printWriter)
149 | cause = cause.cause
150 | }
151 | printWriter.close()
152 | sb.append(write.toString())
153 | return sb.toString()
154 | }
155 |
156 | }
157 | }
158 |
159 |
160 |
--------------------------------------------------------------------------------
/debug/src/main/java/com/peakmain/debug/utils/CrashUtils.kt:
--------------------------------------------------------------------------------
1 | package com.peakmain.debug.utils
2 |
3 | import android.app.Application
4 | import android.content.Context
5 | import android.os.Looper
6 | import android.os.MessageQueue
7 | import com.peakmain.breakpad.NativeCrashHandler
8 | import java.io.File
9 |
10 | /**
11 | * author :Peakmain
12 | * createTime:2021/5/7
13 | * mail:2726449200@qq.com
14 | * describe:异常处理工具类
15 | */
16 | object CrashUtils : MessageQueue.IdleHandler {
17 | private const val CRASH_DIR_JAVA = "javaCrash"
18 | private const val CRASH_DIR_NATIVE = "nativeCrash"
19 | private lateinit var application: Application
20 | fun init(context: Application) {
21 | application = context
22 | Looper.myQueue().addIdleHandler(this)
23 | }
24 |
25 | private fun getNativeCrashDir(): File {
26 | val file = File(application.cacheDir, CRASH_DIR_NATIVE)
27 | if (!file.exists()) {
28 | file.mkdirs()
29 | }
30 | return file
31 | }
32 |
33 | private fun getJavaCrashDir(): File {
34 | val file = File(application.cacheDir, CRASH_DIR_JAVA)
35 | if (!file.exists()) {
36 | file.mkdirs()
37 | }
38 | return file
39 | }
40 |
41 | var mListener: OnFileUploadListener? = null
42 |
43 | interface OnFileUploadListener {
44 | fun onFileUploadListener(file: File)
45 | }
46 |
47 | //文件上传
48 | fun setOnFileUploadListener(listener: OnFileUploadListener?) {
49 | this.mListener = listener
50 | }
51 |
52 | override fun queueIdle(): Boolean {
53 | val javaCrashDir = getJavaCrashDir()
54 | val nativeCrashDir = getNativeCrashDir()
55 | CrashHelper.init(application, javaCrashDir.absolutePath)
56 | NativeCrashHandler.init(nativeCrashDir.absolutePath)
57 | return false
58 | }
59 |
60 | fun getCrashFiles(): Array {
61 | val javaListFiles = getJavaCrashDir().listFiles()
62 | val nativeListFile = getNativeCrashDir().listFiles()
63 | return if (javaListFiles != null && nativeListFile != null)
64 | javaListFiles + nativeListFile
65 | else
66 | emptyArray()
67 | }
68 | }
--------------------------------------------------------------------------------
/debug/src/main/java/com/peakmain/debug/utils/FormatJson.java:
--------------------------------------------------------------------------------
1 | package com.peakmain.debug.utils;
2 |
3 | /**
4 | * author :Peakmain
5 | * createTime:2022/5/16
6 | * mail:2726449200@qq.com
7 | * describe:
8 | */
9 | public class FormatJson {
10 | public static String format(String strJson) {
11 | // 计数tab的个数
12 | int tabNum = 0;
13 | StringBuilder jsonFormat = new StringBuilder();
14 | int length = strJson.length();
15 |
16 | char last = 0;
17 | for (int i = 0; i < length; i++) {
18 | char c = strJson.charAt(i);
19 | if (c == '{') {
20 | tabNum++;
21 | jsonFormat.append(c).append("\n");
22 | jsonFormat.append(getSpaceOrTab(tabNum));
23 | } else if (c == '}') {
24 | tabNum--;
25 | jsonFormat.append("\n");
26 | jsonFormat.append(getSpaceOrTab(tabNum));
27 | jsonFormat.append(c);
28 | } else if (c == ',') {
29 | jsonFormat.append(c).append("\n");
30 | jsonFormat.append(getSpaceOrTab(tabNum));
31 | } else if (c == ':') {
32 | jsonFormat.append(c).append(" ");
33 | } else if (c == '[') {
34 | tabNum++;
35 | char next = strJson.charAt(i + 1);
36 | if (next == ']') {
37 | jsonFormat.append(c);
38 | } else {
39 | jsonFormat.append(c).append("\n");
40 | jsonFormat.append(getSpaceOrTab(tabNum));
41 | }
42 | } else if (c == ']') {
43 | tabNum--;
44 | if (last == '[') {
45 | jsonFormat.append(c);
46 | } else {
47 | jsonFormat.append("\n").append(getSpaceOrTab(tabNum)).append(c);
48 | }
49 | } else {
50 | jsonFormat.append(c);
51 | }
52 | last = c;
53 | }
54 | return jsonFormat.toString();
55 | }
56 |
57 | private static String getSpaceOrTab(int tabNum) {
58 | StringBuilder sbTab = new StringBuilder();
59 | for (int i = 0; i < tabNum; i++) {
60 | sbTab.append('\t');
61 | }
62 | return sbTab.toString();
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/debug/src/main/java/com/peakmain/debug/viewmodel/CrashLogViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.peakmain.debug.viewmodel
2 |
3 | import com.peakmain.basiclibrary.base.viewmodel.BaseViewModel
4 | import com.peakmain.debug.utils.CrashUtils
5 | import java.io.File
6 |
7 | /**
8 | * author :Peakmain
9 | * createTime:2022/3/14
10 | * mail:2726449200@qq.com
11 | * describe:
12 | */
13 | class CrashLogViewModel : BaseViewModel() {
14 |
15 | lateinit var crashFiles: Array
16 | override fun initModel() {
17 | crashFiles = CrashUtils.getCrashFiles()
18 | }
19 | }
--------------------------------------------------------------------------------
/debug/src/main/java/com/peakmain/debug/viewmodel/EnvironmentExchangeViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.peakmain.debug.viewmodel
2 |
3 | import com.peakmain.basiclibrary.base.viewmodel.BaseViewModel
4 |
5 | /**
6 | * author :Peakmain
7 | * createTime:2023/9/27
8 | * mail:2726449200@qq.com
9 | * describe:
10 | */
11 | class EnvironmentExchangeViewModel:BaseViewModel() {
12 | override fun initModel() {
13 |
14 | }
15 | }
--------------------------------------------------------------------------------
/debug/src/main/java/com/peakmain/debug/viewmodel/HttpLoggingViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.peakmain.debug.viewmodel
2 |
3 | import androidx.lifecycle.MutableLiveData
4 | import com.peakmain.basiclibrary.base.viewmodel.BaseViewModel
5 | import com.peakmain.debug.bean.HttpLoggingBean
6 |
7 | /**
8 | * author :Peakmain
9 | * createTime:2022/5/16
10 | * mail:2726449200@qq.com
11 | * describe:
12 | */
13 | class HttpLoggingViewModel : BaseViewModel() {
14 | var mLoggingMutableList: MutableLiveData> = MutableLiveData()
15 | var isPositiveSequence = MutableLiveData()
16 | override fun initModel() {
17 |
18 | }
19 | }
--------------------------------------------------------------------------------
/debug/src/main/res/drawable/ic_debug_clear_all_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/debug/src/main/res/drawable/ic_descending_order.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
12 |
13 |
--------------------------------------------------------------------------------
/debug/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/debug/src/main/res/drawable/ic_positive_sequence.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/debug/src/main/res/drawable/ic_share_24.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/debug/src/main/res/drawable/shape_background_debug_tool.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/debug/src/main/res/drawable/shape_divider_debug_tool.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/debug/src/main/res/layout/activity_debug_http_logging_detail.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
15 |
16 |
21 |
22 |
38 |
39 |
55 |
56 |
72 |
73 |
82 |
83 |
86 |
87 |
94 |
95 |
96 |
--------------------------------------------------------------------------------
/debug/src/main/res/layout/debug_activity_crash_log.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
13 |
14 |
18 |
19 |
--------------------------------------------------------------------------------
/debug/src/main/res/layout/debug_activity_environment_exchange.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
12 |
16 |
17 |
26 |
27 |
34 |
44 |
45 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/debug/src/main/res/layout/debug_dialog_tool.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
--------------------------------------------------------------------------------
/debug/src/main/res/layout/debug_http_logging.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
23 |
24 |
28 |
29 |
--------------------------------------------------------------------------------
/debug/src/main/res/layout/debug_recycler_crash_log_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
21 |
22 |
23 |
32 |
--------------------------------------------------------------------------------
/debug/src/main/res/layout/debug_recycler_environment_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
9 |
10 |
11 |
12 |
17 |
18 |
28 |
29 |
39 |
40 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/debug/src/main/res/layout/debug_recycler_http_logging.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
8 |
11 |
12 |
15 |
16 |
17 |
18 |
19 |
25 |
26 |
35 |
36 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/debug/src/main/res/layout/debug_recycler_tool_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
19 |
20 |
21 |
31 |
--------------------------------------------------------------------------------
/debug/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #999999
4 | #f1f1f1
5 |
6 |
--------------------------------------------------------------------------------
/debug/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | debug
3 | 环境切换
4 |
5 |
--------------------------------------------------------------------------------
/debug/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/debug/src/main/res/xml/debug_provider_paths.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
10 |
11 |
14 |
15 |
18 |
19 |
22 |
23 |
26 |
27 |
30 |
--------------------------------------------------------------------------------
/debug/src/test/java/com/peakmain/debug/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.peakmain.debug
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app"s APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
20 | # Kotlin code style for this project: "official" or "obsolete":
21 | kotlin.code.style=official
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Peakmain/DebugTools/fb99d544cde1fc7904f9e1483e01d9d0284f7ffc/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sun Mar 13 09:47:07 CST 2022
2 | distributionBase=GRADLE_USER_HOME
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip
4 | distributionPath=wrapper/dists
5 | zipStorePath=wrapper/dists
6 | zipStoreBase=GRADLE_USER_HOME
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 |
86 | # Determine the Java command to use to start the JVM.
87 | if [ -n "$JAVA_HOME" ] ; then
88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
89 | # IBM's JDK on AIX uses strange locations for the executables
90 | JAVACMD="$JAVA_HOME/jre/sh/java"
91 | else
92 | JAVACMD="$JAVA_HOME/bin/java"
93 | fi
94 | if [ ! -x "$JAVACMD" ] ; then
95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
96 |
97 | Please set the JAVA_HOME variable in your environment to match the
98 | location of your Java installation."
99 | fi
100 | else
101 | JAVACMD="java"
102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
103 |
104 | Please set the JAVA_HOME variable in your environment to match the
105 | location of your Java installation."
106 | fi
107 |
108 | # Increase the maximum file descriptors if we can.
109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
110 | MAX_FD_LIMIT=`ulimit -H -n`
111 | if [ $? -eq 0 ] ; then
112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
113 | MAX_FD="$MAX_FD_LIMIT"
114 | fi
115 | ulimit -n $MAX_FD
116 | if [ $? -ne 0 ] ; then
117 | warn "Could not set maximum file descriptor limit: $MAX_FD"
118 | fi
119 | else
120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
121 | fi
122 | fi
123 |
124 | # For Darwin, add options to specify how the application appears in the dock
125 | if $darwin; then
126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
127 | fi
128 |
129 | # For Cygwin or MSYS, switch paths to Windows format before running java
130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
133 |
134 | JAVACMD=`cygpath --unix "$JAVACMD"`
135 |
136 | # We build the pattern for arguments to be converted via cygpath
137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
138 | SEP=""
139 | for dir in $ROOTDIRSRAW ; do
140 | ROOTDIRS="$ROOTDIRS$SEP$dir"
141 | SEP="|"
142 | done
143 | OURCYGPATTERN="(^($ROOTDIRS))"
144 | # Add a user-defined pattern to the cygpath arguments
145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
147 | fi
148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
149 | i=0
150 | for arg in "$@" ; do
151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
153 |
154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
156 | else
157 | eval `echo args$i`="\"$arg\""
158 | fi
159 | i=`expr $i + 1`
160 | done
161 | case $i in
162 | 0) set -- ;;
163 | 1) set -- "$args0" ;;
164 | 2) set -- "$args0" "$args1" ;;
165 | 3) set -- "$args0" "$args1" "$args2" ;;
166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
172 | esac
173 | fi
174 |
175 | # Escape application args
176 | save () {
177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
178 | echo " "
179 | }
180 | APP_ARGS=`save "$@"`
181 |
182 | # Collect all arguments for the java command, following the shell quoting and substitution rules
183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
184 |
185 | exec "$JAVACMD" "$@"
186 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/jitpack.yml:
--------------------------------------------------------------------------------
1 | before_install:
2 | - sdk install java 11.0.10-open
3 | - sdk use java 11.0.10-open
4 |
5 |
6 | jdk:
7 | - openjdk11
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 |
2 | rootProject.name = "DebugTools"
3 | include ':app'
4 | include ':debug'
5 |
--------------------------------------------------------------------------------