├── .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 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 19 | 20 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 36 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | 29 | 30 | 34 | 35 | 39 | 40 | -------------------------------------------------------------------------------- /.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/migrations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 31 | 32 | 33 | 34 | 35 | 36 | 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 | ![日志捕获分享框架](https://user-images.githubusercontent.com/26482737/173175618-37bd970d-4c6d-42de-a304-eb69aee3719d.gif) 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 | ![网络抓包工具](https://github.com/Peakmain/DebugTools/assets/26482737/82bf1c6b-a3ce-47bd-b0be-69bee77f755c) 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 | image 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 | ![fps](https://user-images.githubusercontent.com/26482737/173175633-403a0f86-f914-40ac-a74d-359c0808361f.gif) 133 | - App的AndroidManifest.xml需要添加悬浮窗权限 134 | ```kotlin 135 | 136 | ``` 137 | - 开启悬浮窗权限即可 138 | 139 | #### 环境切换 140 | ![一键网络切换](https://github.com/Peakmain/DebugTools/assets/26482737/27b1d507-ca09-4550-8880-88d26e728841) 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 | --------------------------------------------------------------------------------