├── .github ├── dependabot.yml └── workflows │ └── android.yml ├── .gitignore ├── .idea └── .gitignore ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ ├── AndroidManifest.xml │ └── java │ │ └── com │ │ └── nagopy │ │ └── android │ │ └── debugassistant │ │ └── ui │ │ └── main │ │ └── MainActivityKtTest.kt │ └── main │ ├── AndroidManifest.xml │ ├── ic_launcher-playstore.png │ ├── java │ └── com │ │ └── nagopy │ │ └── android │ │ └── debugassistant │ │ ├── AppModule.kt │ │ ├── DebugAssistantApplication.kt │ │ └── ui │ │ ├── UiModule.kt │ │ ├── main │ │ ├── MainActivity.kt │ │ ├── MainUiState.kt │ │ └── MainViewModel.kt │ │ ├── theme │ │ ├── Color.kt │ │ ├── Shape.kt │ │ ├── Theme.kt │ │ └── Type.kt │ │ └── tile │ │ ├── AdbTileService.kt │ │ └── ProxyTileService.kt │ └── res │ ├── drawable │ ├── ic_adb_tile.xml │ ├── ic_launcher_background.xml │ ├── ic_launcher_foreground.xml │ └── ic_proxy_tile.xml │ ├── mipmap-anydpi-v26 │ ├── ic_launcher.xml │ └── ic_launcher_round.xml │ ├── mipmap-hdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-mdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── values-ja │ └── strings.xml │ └── values │ ├── colors.xml │ ├── strings.xml │ └── themes.xml ├── build.gradle ├── data └── repository │ ├── .gitignore │ ├── build.gradle │ ├── consumer-rules.pro │ ├── proguard-rules.pro │ └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── nagopy │ └── android │ └── debugassistant │ └── data │ └── repository │ ├── GlobalSettingsRepository.kt │ ├── RepositoryModule.kt │ ├── UserPreferencesRepository.kt │ └── impl │ ├── GlobalSettingRepositoryImpl.kt │ └── UserPreferencesRepositoryImpl.kt ├── detekt.yml ├── domain ├── model │ ├── .gitignore │ ├── build.gradle │ └── src │ │ ├── main │ │ └── java │ │ │ └── com │ │ │ └── nagopy │ │ │ └── android │ │ │ └── debugassistant │ │ │ └── domain │ │ │ └── model │ │ │ └── ProxyInfo.kt │ │ └── test │ │ └── java │ │ └── com │ │ └── nagopy │ │ └── android │ │ └── debugassistant │ │ └── domain │ │ └── model │ │ └── ProxyInfoTest.kt └── usecase │ ├── .gitignore │ ├── build.gradle │ ├── consumer-rules.pro │ ├── proguard-rules.pro │ └── src │ ├── main │ ├── AndroidManifest.xml │ └── java │ │ └── com │ │ └── nagopy │ │ └── android │ │ └── debugassistant │ │ └── domain │ │ └── usecase │ │ ├── DisableAdbUseCase.kt │ │ ├── DisableProxyUseCase.kt │ │ ├── EnableAdbUseCase.kt │ │ ├── EnableProxyUseCase.kt │ │ ├── GetAdbStatusUseCase.kt │ │ ├── GetPermissionStatusUseCase.kt │ │ ├── GetProxyStatusUseCase.kt │ │ ├── GetUserProxyInfoUseCase.kt │ │ ├── PutUserProxyInfoUseCase.kt │ │ ├── UseCaseModule.kt │ │ └── interactor │ │ ├── DisableAdbInteractor.kt │ │ ├── DisableProxyInteractor.kt │ │ ├── EnableAdbInteractor.kt │ │ ├── EnableProxyInteractor.kt │ │ ├── GetAdbStatusInteractor.kt │ │ ├── GetPermissionStatusInteractor.kt │ │ ├── GetProxyStatusInteractor.kt │ │ ├── GetUserProxyInfoInteractor.kt │ │ └── PutUserProxyInfoInteractor.kt │ └── test │ └── java │ └── com │ └── nagopy │ └── android │ └── debugassistant │ └── domain │ └── usecase │ └── interactor │ ├── DisableAdbInteractorTest.kt │ ├── DisableProxyInteractorTest.kt │ ├── EnableAdbInteractorTest.kt │ ├── EnableProxyInteractorTest.kt │ ├── GetAdbStatusInteractorTest.kt │ ├── GetProxyStatusInteractorTest.kt │ ├── GetUserProxyInfoInteractorTest.kt │ └── PutUserProxyInfoInteractorTest.kt ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── images ├── feature_graphic.png ├── features_1.png ├── features_2.png ├── install_1.png └── install_2.png └── settings.gradle /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "gradle" # See documentation for possible values 4 | directory: "/" # Location of package manifests 5 | target-branch: "update-dependencies" 6 | schedule: 7 | interval: "weekly" -------------------------------------------------------------------------------- /.github/workflows/android.yml: -------------------------------------------------------------------------------- 1 | name: Android CI 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | paths-ignore: 7 | - images/** 8 | - README.md 9 | pull_request: 10 | branches: [ main ] 11 | paths-ignore: 12 | - images/** 13 | - README.md 14 | 15 | jobs: 16 | test: 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: actions/checkout@v2 20 | - name: set up JDK 17 21 | uses: actions/setup-java@v2 22 | with: 23 | java-version: '17' 24 | distribution: 'temurin' 25 | cache: gradle 26 | - name: Cache 27 | uses: actions/cache@v2.1.7 28 | with: 29 | path: ~/.gradle/caches 30 | key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }} 31 | restore-keys: | 32 | ${{ runner.os }}-gradle- 33 | - name: Grant execute permission for gradlew 34 | run: chmod +x gradlew 35 | - name: Test with Gradle 36 | run: ./gradlew testDebugUnitTest --stacktrace 37 | 38 | detekt: 39 | runs-on: ubuntu-latest 40 | steps: 41 | - uses: actions/checkout@v2 42 | - name: set up JDK 17 43 | uses: actions/setup-java@v2 44 | with: 45 | java-version: '17' 46 | distribution: 'temurin' 47 | cache: gradle 48 | - name: Cache 49 | uses: actions/cache@v2.1.7 50 | with: 51 | path: ~/.gradle/caches 52 | key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }} 53 | restore-keys: | 54 | ${{ runner.os }}-gradle- 55 | - name: Grant execute permission for gradlew 56 | run: chmod +x gradlew 57 | - name: detekt with Gradle 58 | run: ./gradlew detekt 59 | -------------------------------------------------------------------------------- /.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/.name 17 | .idea/compiler.xml 18 | .idea/gradle.xml 19 | .idea/misc.xml 20 | .idea/vcs.xml 21 | .idea/codeStyles/codeStyleConfig.xml 22 | .idea/codeStyles/Project.xml 23 | .idea/inspectionProfiles/ktlint.xml 24 | .idea/inspectionProfiles/profiles_settings.xml 25 | .idea/deploymentTargetDropDown.xml 26 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Debug Assistant 2 | 3 | Assists Android Developers! 4 | 5 | ## Features 6 | 7 | * Proxy toggle in quick settings 8 | * Adb toggle in quick settings 9 | 10 | 11 | 12 | ## Install 13 | 14 | 1. Download and install from [Google Play](https://play.google.com/store/apps/details?id=com.nagopy.android.debugassistant) 15 | 2. Grant WRITE_SECURE_SETTINGS permission by following command 16 | `adb shell pm grant com.nagopy.android.debugassistant android.permission.WRITE_SECURE_SETTINGS` 17 | 3. Input your proxy settings 18 | 4. Add quick setting icons 19 | 20 | 21 | 22 | ## Architecture 23 | 24 | ``` 25 | debugassistant 26 | ├─data 27 | │ └─repository 28 | │ │ GlobalSettingsRepository.kt 29 | │ │ UserPreferencesRepository.kt 30 | │ │ ... 31 | │ └─impl 32 | │ GlobalSettingRepositoryImpl.kt 33 | │ UserPreferencesRepositoryImpl.kt 34 | │ ... 35 | ├─domain 36 | │ │ ProxyInfo.kt 37 | │ │ ... 38 | │ └─usecase 39 | │ │ EnableProxyUseCase.kt 40 | │ │ DisableProxyUseCase.kt 41 | │ │ ... 42 | │ └─interactor 43 | │ EnableProxyInteractor.kt 44 | │ DisableProxyInteractor.kt 45 | │ ... 46 | └─ui 47 | ├─main 48 | │ MainActivity.kt 49 | │ MainViewModel.kt 50 | │ MainUiState.kt 51 | │ ... 52 | └─tile 53 | ProxyTileService.kt 54 | ... 55 | ``` 56 | 57 | ```mermaid 58 | classDiagram 59 | 60 | MainActivity --> MainViewModel : events 61 | MainViewModel --> MainActivity : MainUiState 62 | MainViewModel ..> EnableProxyUseCase 63 | MainViewModel ..> DisableProxyUseCase 64 | EnableProxyUseCase <|.. EnableProxyInteractor : implements 65 | DisableProxyUseCase <|.. DisableProxyInteractor : implements 66 | 67 | EnableProxyInteractor ..> GlobalSettingsRepository 68 | DisableProxyInteractor ..> GlobalSettingsRepository 69 | 70 | GlobalSettingsRepository <|.. GlobalSettingsRepositoryImpl : implements 71 | ``` 72 | 73 | ## License 74 | 75 | ``` 76 | Copyright 2022 75py 77 | 78 | Licensed under the Apache License, Version 2.0 (the "License"); 79 | you may not use this file except in compliance with the License. 80 | You may obtain a copy of the License at 81 | 82 | http://www.apache.org/licenses/LICENSE-2.0 83 | 84 | Unless required by applicable law or agreed to in writing, software 85 | distributed under the License is distributed on an "AS IS" BASIS, 86 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 87 | See the License for the specific language governing permissions and 88 | limitations under the License. 89 | ``` 90 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | id 'org.jetbrains.kotlin.android' 4 | id 'org.jlleitschuh.gradle.ktlint' 5 | id 'com.google.android.gms.oss-licenses-plugin' 6 | id 'io.gitlab.arturbosch.detekt' 7 | } 8 | 9 | detekt { 10 | buildUponDefaultConfig = true 11 | config = files("$projectDir/../detekt.yml") 12 | } 13 | 14 | 15 | android { 16 | compileSdk 34 17 | 18 | defaultConfig { 19 | applicationId "com.nagopy.android.debugassistant" 20 | minSdk 28 21 | targetSdk 34 22 | versionCode 9 23 | versionName "1.1.3" 24 | 25 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 26 | vectorDrawables { 27 | useSupportLibrary true 28 | } 29 | } 30 | 31 | buildTypes { 32 | release { 33 | minifyEnabled true 34 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 35 | } 36 | } 37 | compileOptions { 38 | sourceCompatibility JavaVersion.VERSION_17 39 | targetCompatibility JavaVersion.VERSION_17 40 | } 41 | kotlinOptions { 42 | jvmTarget = '17' 43 | } 44 | buildFeatures { 45 | compose true 46 | buildConfig true 47 | } 48 | composeOptions { 49 | kotlinCompilerExtensionVersion compose_version 50 | } 51 | packagingOptions { 52 | resources { 53 | excludes += '/META-INF/{AL2.0,LGPL2.1}' 54 | } 55 | } 56 | namespace 'com.nagopy.android.debugassistant' 57 | testNamespace 'com.nagopy.android.debugassistant.androidtest' 58 | } 59 | 60 | dependencies { 61 | implementation project(":domain:model") 62 | implementation project(":domain:usecase") 63 | implementation project(":data:repository") 64 | 65 | implementation 'androidx.core:core-ktx:1.10.1' 66 | implementation "androidx.compose.ui:ui:$compose_version" 67 | implementation "androidx.compose.material:material:$compose_version" 68 | implementation "androidx.compose.ui:ui-tooling-preview:$compose_version" 69 | implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.1' 70 | implementation 'androidx.activity:activity-compose:1.7.2' 71 | 72 | testImplementation 'junit:junit:4.13.2' 73 | androidTestImplementation 'androidx.test.ext:junit:1.1.5' 74 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' 75 | androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version" 76 | debugImplementation "androidx.compose.ui:ui-tooling:$compose_version" 77 | 78 | testImplementation "io.mockk:mockk:1.12.3" 79 | testImplementation 'org.jetbrains.kotlin:kotlin-test' 80 | 81 | androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version" 82 | debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_version" 83 | androidTestImplementation 'org.jetbrains.kotlin:kotlin-test' 84 | 85 | androidTestImplementation "io.mockk:mockk-android:1.12.3" 86 | 87 | implementation "io.insert-koin:koin-android:3.4.3" 88 | 89 | implementation 'com.google.android.gms:play-services-oss-licenses:17.0.1' 90 | implementation "androidx.appcompat:appcompat:1.6.1" 91 | } 92 | -------------------------------------------------------------------------------- /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 22 | 23 | -dontwarn com.google.errorprone.annotations.Immutable 24 | -------------------------------------------------------------------------------- /app/src/androidTest/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/nagopy/android/debugassistant/ui/main/MainActivityKtTest.kt: -------------------------------------------------------------------------------- 1 | package com.nagopy.android.debugassistant.ui.main 2 | 3 | import androidx.compose.foundation.layout.Column 4 | import androidx.compose.foundation.layout.Row 5 | import androidx.compose.ui.Modifier 6 | import androidx.compose.ui.platform.testTag 7 | import androidx.compose.ui.test.assertIsDisplayed 8 | import androidx.compose.ui.test.assertIsEnabled 9 | import androidx.compose.ui.test.assertIsNotEnabled 10 | import androidx.compose.ui.test.assertIsOff 11 | import androidx.compose.ui.test.assertIsOn 12 | import androidx.compose.ui.test.assertTextEquals 13 | import androidx.compose.ui.test.filterToOne 14 | import androidx.compose.ui.test.hasClickAction 15 | import androidx.compose.ui.test.hasImeAction 16 | import androidx.compose.ui.test.junit4.createComposeRule 17 | import androidx.compose.ui.test.onChildren 18 | import androidx.compose.ui.test.onNodeWithTag 19 | import androidx.compose.ui.test.onNodeWithText 20 | import androidx.compose.ui.test.performClick 21 | import androidx.compose.ui.text.input.ImeAction 22 | import io.mockk.mockk 23 | import io.mockk.verify 24 | import org.junit.Rule 25 | import org.junit.Test 26 | import kotlin.test.assertEquals 27 | 28 | class MainActivityKtTest { 29 | 30 | @get:Rule 31 | val composeTestRule = createComposeRule() 32 | 33 | private interface OnAdbCommandClickedClass { 34 | fun onClick() 35 | } 36 | 37 | @Test 38 | fun permissionErrorMessage() { 39 | val onAdbCommandClicked = mockk(relaxed = true) 40 | composeTestRule.setContent { 41 | PermissionErrorMessage{ onAdbCommandClicked.onClick() } 42 | } 43 | composeTestRule.onNode(hasClickAction()) 44 | .performClick() 45 | verify { 46 | onAdbCommandClicked.onClick() 47 | } 48 | } 49 | 50 | @Test 51 | fun proxyHost() { 52 | composeTestRule.setContent { 53 | Column { 54 | Row(modifier = Modifier.testTag("enabled")) { 55 | ProxyHost(enabled = true, value = "value1", onValueChanged = {}) 56 | } 57 | Row(modifier = Modifier.testTag("disabled")) { 58 | ProxyHost(enabled = false, value = "value2", onValueChanged = {}) 59 | } 60 | } 61 | } 62 | composeTestRule.onNodeWithTag("enabled") 63 | .onChildren() 64 | .filterToOne(hasImeAction(ImeAction.Default)) 65 | .assertIsEnabled() 66 | .assertTextEquals("Proxy Host", includeEditableText = false) 67 | composeTestRule.onNodeWithTag("disabled") 68 | .onChildren() 69 | .filterToOne(hasImeAction(ImeAction.Default)) 70 | .assertIsNotEnabled() 71 | .assertTextEquals("Proxy Host", includeEditableText = false) 72 | } 73 | 74 | @Test 75 | fun proxyPort() { 76 | composeTestRule.setContent { 77 | Column { 78 | Row(modifier = Modifier.testTag("enabled")) { 79 | ProxyPort(enabled = true, value = "value1", onValueChanged = {}) 80 | } 81 | Row(modifier = Modifier.testTag("disabled")) { 82 | ProxyPort(enabled = false, value = "value2", onValueChanged = {}) 83 | } 84 | } 85 | } 86 | composeTestRule.onNodeWithTag("enabled") 87 | .onChildren() 88 | .filterToOne(hasImeAction(ImeAction.Default)) 89 | .assertIsEnabled() 90 | .assertTextEquals("Proxy Port", includeEditableText = false) 91 | composeTestRule.onNodeWithTag("disabled") 92 | .onChildren() 93 | .filterToOne(hasImeAction(ImeAction.Default)) 94 | .assertIsNotEnabled() 95 | .assertTextEquals("Proxy Port", includeEditableText = false) 96 | } 97 | 98 | @Test 99 | fun proxyToggleSwitch() { 100 | var i = 0 101 | composeTestRule.setContent { 102 | ProxyToggleSwitch(enabled = true, checked = true, onCheckedChange = { i++ }) 103 | } 104 | 105 | val clickable = composeTestRule.onNode(hasClickAction()) 106 | clickable 107 | .assertIsEnabled() 108 | .assertIsOn() 109 | .assertIsDisplayed() 110 | 111 | clickable.performClick() 112 | assertEquals(1, i) 113 | } 114 | 115 | @Test 116 | fun adbSwitch() { 117 | var i = 0 118 | composeTestRule.setContent { 119 | AdbSwitch(enabled = true, checked = true, onCheckedChange = { i++ }) 120 | } 121 | 122 | val clickable = composeTestRule.onNode(hasClickAction()) 123 | clickable 124 | .assertIsEnabled() 125 | .assertIsOn() 126 | .assertIsDisplayed() 127 | 128 | clickable.performClick() 129 | assertEquals(1, i) 130 | } 131 | 132 | @Test 133 | fun adbSwitch_disabled() { 134 | composeTestRule.setContent { 135 | AdbSwitch(enabled = false, checked = false, onCheckedChange = { }) 136 | } 137 | composeTestRule.onNode(hasClickAction()) 138 | .assertIsNotEnabled() 139 | .assertIsOff() 140 | .assertIsDisplayed() 141 | composeTestRule.onNodeWithText("Adb") 142 | .assertIsDisplayed() 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 8 | 9 | 17 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 33 | 34 | 35 | 36 | 37 | 38 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /app/src/main/ic_launcher-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/75py/DebugAssistant/bff9000aae07541004d1df604dbf1037db3e7b01/app/src/main/ic_launcher-playstore.png -------------------------------------------------------------------------------- /app/src/main/java/com/nagopy/android/debugassistant/AppModule.kt: -------------------------------------------------------------------------------- 1 | package com.nagopy.android.debugassistant 2 | 3 | import android.app.KeyguardManager 4 | import android.content.ContentResolver 5 | import org.koin.android.ext.koin.androidApplication 6 | import org.koin.android.ext.koin.androidContext 7 | import org.koin.dsl.module 8 | 9 | val appModule = module { 10 | single { androidContext().contentResolver } 11 | 12 | single { androidApplication().getSystemService(KeyguardManager::class.java) } 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/java/com/nagopy/android/debugassistant/DebugAssistantApplication.kt: -------------------------------------------------------------------------------- 1 | package com.nagopy.android.debugassistant 2 | 3 | import android.app.Application 4 | import com.nagopy.android.debugassistant.data.repository.repositoryModule 5 | import com.nagopy.android.debugassistant.domain.usecase.domainModule 6 | import com.nagopy.android.debugassistant.ui.uiModule 7 | import org.koin.android.ext.koin.androidContext 8 | import org.koin.core.context.startKoin 9 | 10 | class DebugAssistantApplication : Application() { 11 | 12 | override fun onCreate() { 13 | super.onCreate() 14 | 15 | startKoin { 16 | // androidLogger() 17 | androidContext(this@DebugAssistantApplication) 18 | modules(appModule, uiModule, domainModule, repositoryModule) 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/nagopy/android/debugassistant/ui/UiModule.kt: -------------------------------------------------------------------------------- 1 | package com.nagopy.android.debugassistant.ui 2 | 3 | import com.nagopy.android.debugassistant.ui.main.MainViewModel 4 | import org.koin.android.ext.koin.androidApplication 5 | import org.koin.androidx.viewmodel.dsl.viewModel 6 | import org.koin.dsl.module 7 | 8 | val uiModule = module { 9 | viewModel { MainViewModel(androidApplication(), get(), get(), get(), get(), get(), get(), get(), get(), get()) } 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/java/com/nagopy/android/debugassistant/ui/main/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.nagopy.android.debugassistant.ui.main 2 | 3 | import android.os.Bundle 4 | import androidx.activity.ComponentActivity 5 | import androidx.activity.compose.setContent 6 | import androidx.compose.foundation.layout.Column 7 | import androidx.compose.foundation.layout.Row 8 | import androidx.compose.foundation.layout.Spacer 9 | import androidx.compose.foundation.layout.fillMaxWidth 10 | import androidx.compose.foundation.layout.height 11 | import androidx.compose.foundation.layout.padding 12 | import androidx.compose.foundation.layout.width 13 | import androidx.compose.foundation.rememberScrollState 14 | import androidx.compose.foundation.selection.toggleable 15 | import androidx.compose.foundation.text.KeyboardOptions 16 | import androidx.compose.foundation.verticalScroll 17 | import androidx.compose.material.Button 18 | import androidx.compose.material.ContentAlpha 19 | import androidx.compose.material.Divider 20 | import androidx.compose.material.LocalContentAlpha 21 | import androidx.compose.material.OutlinedTextField 22 | import androidx.compose.material.Scaffold 23 | import androidx.compose.material.Switch 24 | import androidx.compose.material.Text 25 | import androidx.compose.material.TopAppBar 26 | import androidx.compose.runtime.Composable 27 | import androidx.compose.runtime.collectAsState 28 | import androidx.compose.ui.Modifier 29 | import androidx.compose.ui.draw.alpha 30 | import androidx.compose.ui.graphics.Color 31 | import androidx.compose.ui.res.stringResource 32 | import androidx.compose.ui.semantics.Role 33 | import androidx.compose.ui.text.input.KeyboardType 34 | import androidx.compose.ui.tooling.preview.Preview 35 | import androidx.compose.ui.unit.dp 36 | import androidx.compose.ui.unit.sp 37 | import com.nagopy.android.debugassistant.BuildConfig 38 | import com.nagopy.android.debugassistant.R 39 | import com.nagopy.android.debugassistant.ui.theme.DebugAssistantTheme 40 | import org.koin.androidx.viewmodel.ext.android.viewModel 41 | 42 | class MainActivity : ComponentActivity() { 43 | 44 | private val mainViewModel: MainViewModel by viewModel() 45 | 46 | override fun onCreate(savedInstanceState: Bundle?) { 47 | super.onCreate(savedInstanceState) 48 | setContent { 49 | DebugAssistantTheme { 50 | Scaffold( 51 | topBar = { 52 | TopAppBar( 53 | title = { Text(stringResource(id = R.string.app_name)) }, 54 | ) 55 | }, 56 | content = { padding -> 57 | val state = mainViewModel.viewModelState.collectAsState().value 58 | MainScreen( 59 | modifier = Modifier.padding(padding), 60 | proxyHost = state.proxyHost, 61 | proxyPort = state.proxyPort, 62 | isPermissionGranted = state.isPermissionGranted, 63 | isProxyEnabled = state.isProxyEnabled, 64 | isAdbEnabled = state.isAdbEnabled, 65 | onAdbCommandClicked = { mainViewModel.onAdbCommandClicked() }, 66 | onProxyHostChanged = { mainViewModel.onProxyHostChanged(it) }, 67 | onProxyPortChanged = { mainViewModel.onProxyPortChanged(it) }, 68 | onProxySwitchClicked = { mainViewModel.onProxySwitchClicked(it) }, 69 | onAdbSwitchClicked = { mainViewModel.onAdbSwitchClicked(it) }, 70 | onHowToUseButtonClicked = { mainViewModel.onHowToUseButtonClicked() }, 71 | onLicensesButtonClicked = { mainViewModel.onLicensesButtonClicked() }, 72 | ) 73 | } 74 | ) 75 | } 76 | } 77 | } 78 | 79 | override fun onResume() { 80 | super.onResume() 81 | mainViewModel.updateStatus() 82 | } 83 | } 84 | 85 | @Preview(showBackground = true, showSystemUi = true) 86 | @Composable 87 | fun DefaultPreview() { 88 | DebugAssistantTheme { 89 | Scaffold( 90 | topBar = { 91 | TopAppBar( 92 | title = { Text(stringResource(id = R.string.app_name)) }, 93 | ) 94 | }, 95 | content = { padding -> 96 | MainScreen( 97 | modifier = Modifier.padding(padding), 98 | proxyHost = "host", 99 | proxyPort = "port", 100 | isPermissionGranted = false, 101 | isProxyEnabled = true, 102 | isAdbEnabled = true, 103 | onAdbCommandClicked = {}, 104 | onProxyHostChanged = {}, 105 | onProxyPortChanged = {}, 106 | onProxySwitchClicked = {}, 107 | onAdbSwitchClicked = {}, 108 | onHowToUseButtonClicked = {}, 109 | onLicensesButtonClicked = {}, 110 | ) 111 | } 112 | ) 113 | } 114 | } 115 | 116 | @Composable 117 | fun MainScreen( 118 | modifier: Modifier = Modifier, 119 | proxyHost: String, 120 | proxyPort: String, 121 | isPermissionGranted: Boolean, 122 | isProxyEnabled: Boolean, 123 | isAdbEnabled: Boolean, 124 | onAdbCommandClicked: () -> Unit, 125 | onProxyHostChanged: (String) -> Unit, 126 | onProxyPortChanged: (String) -> Unit, 127 | onProxySwitchClicked: (Boolean) -> Unit, 128 | onAdbSwitchClicked: (Boolean) -> Unit, 129 | onHowToUseButtonClicked: () -> Unit, 130 | onLicensesButtonClicked: () -> Unit, 131 | ) { 132 | Column( 133 | modifier 134 | .padding(16.dp) 135 | .verticalScroll(rememberScrollState()) 136 | ) { 137 | if (!isPermissionGranted) { 138 | PermissionErrorMessage(onAdbCommandClicked = onAdbCommandClicked) 139 | 140 | Spacer(modifier = Modifier.height(16.dp)) 141 | } 142 | 143 | ProxySection( 144 | isPermissionGranted = isPermissionGranted, 145 | proxyHost = proxyHost, 146 | onProxyHostChanged = onProxyHostChanged, 147 | proxyPort = proxyPort, 148 | onProxyPortChanged = onProxyPortChanged, 149 | isProxyEnabled = isProxyEnabled, 150 | onProxySwitchClicked = onProxySwitchClicked, 151 | ) 152 | 153 | Spacer(modifier = Modifier.height(16.dp)) 154 | 155 | AdbSection( 156 | isPermissionGranted = isPermissionGranted, 157 | isAdbEnabled = isAdbEnabled, 158 | onAdbSwitchClicked = onAdbSwitchClicked 159 | ) 160 | 161 | Spacer(modifier = Modifier.height(16.dp)) 162 | 163 | AboutSection( 164 | onHowToUseButtonClicked = onHowToUseButtonClicked, 165 | onLicensesButtonClicked = onLicensesButtonClicked, 166 | ) 167 | } 168 | } 169 | 170 | @Composable 171 | fun PermissionErrorMessage(onAdbCommandClicked: () -> Unit) { 172 | Column { 173 | Text( 174 | text = stringResource(id = R.string.permission_error), 175 | color = Color.Red 176 | ) 177 | Spacer(modifier = Modifier.height(8.dp)) 178 | Button(onClick = { onAdbCommandClicked() }) { 179 | Text( 180 | text = "adb shell pm grant ${BuildConfig.APPLICATION_ID} android.permission.WRITE_SECURE_SETTINGS", 181 | ) 182 | } 183 | } 184 | } 185 | 186 | @Composable 187 | fun ProxySection( 188 | isPermissionGranted: Boolean, 189 | proxyHost: String, 190 | onProxyHostChanged: (String) -> Unit, 191 | proxyPort: String, 192 | onProxyPortChanged: (String) -> Unit, 193 | isProxyEnabled: Boolean, 194 | onProxySwitchClicked: (Boolean) -> Unit, 195 | ) { 196 | Column { 197 | Text( 198 | "Settings.Global.HTTP_PROXY", fontSize = 18.sp 199 | ) 200 | Spacer(modifier = Modifier.height(3.dp)) 201 | Divider() 202 | 203 | Spacer(modifier = Modifier.height(8.dp)) 204 | 205 | Text( 206 | text = stringResource(id = R.string.caution_http_proxy), 207 | color = Color.Red, 208 | ) 209 | 210 | Spacer(modifier = Modifier.height(8.dp)) 211 | 212 | ProxyHost( 213 | enabled = isPermissionGranted, 214 | value = proxyHost, 215 | onValueChanged = { 216 | onProxyHostChanged(it) 217 | } 218 | ) 219 | 220 | Spacer(modifier = Modifier.height(8.dp)) 221 | 222 | ProxyPort( 223 | enabled = isPermissionGranted, 224 | value = proxyPort, 225 | onValueChanged = { 226 | onProxyPortChanged(it) 227 | } 228 | ) 229 | 230 | Spacer(modifier = Modifier.height(4.dp)) 231 | 232 | ProxyToggleSwitch( 233 | enabled = isPermissionGranted && proxyHost.isNotEmpty() && proxyPort.isNotEmpty(), 234 | checked = isProxyEnabled, 235 | ) { 236 | onProxySwitchClicked(it) 237 | } 238 | } 239 | } 240 | 241 | @Composable 242 | fun ProxyHost(enabled: Boolean, value: String, onValueChanged: (String) -> Unit) { 243 | OutlinedTextField( 244 | enabled = enabled, 245 | value = value, 246 | onValueChange = { onValueChanged(it) }, 247 | label = { Text("Proxy Host") }, 248 | maxLines = 1, 249 | keyboardOptions = KeyboardOptions( 250 | keyboardType = KeyboardType.Uri 251 | ) 252 | ) 253 | } 254 | 255 | @Composable 256 | fun ProxyPort(enabled: Boolean, value: String, onValueChanged: (String) -> Unit) { 257 | OutlinedTextField( 258 | enabled = enabled, 259 | value = value, 260 | onValueChange = { onValueChanged(it) }, 261 | label = { Text("Proxy Port") }, 262 | maxLines = 1, 263 | keyboardOptions = KeyboardOptions( 264 | keyboardType = KeyboardType.Number 265 | ) 266 | ) 267 | } 268 | 269 | @Composable 270 | fun ProxyToggleSwitch(enabled: Boolean, checked: Boolean, onCheckedChange: (Boolean) -> Unit) { 271 | val alpha = if (enabled) LocalContentAlpha.current else ContentAlpha.disabled 272 | 273 | Row( 274 | modifier = Modifier 275 | .toggleable( 276 | enabled = enabled, 277 | value = checked, 278 | role = Role.Switch, 279 | onValueChange = { onCheckedChange(it) } 280 | ) 281 | .padding(16.dp) 282 | .fillMaxWidth() 283 | ) { 284 | Text(text = "Use Proxy", modifier = Modifier.alpha(alpha)) 285 | Spacer(modifier = Modifier.width(8.dp)) 286 | Switch(enabled = enabled, checked = checked, onCheckedChange = null) 287 | } 288 | } 289 | 290 | @Composable 291 | fun AdbSection( 292 | isPermissionGranted: Boolean, 293 | isAdbEnabled: Boolean, 294 | onAdbSwitchClicked: (Boolean) -> Unit, 295 | ) { 296 | Column { 297 | Text( 298 | "Settings.Global.ADB_ENABLED", fontSize = 18.sp 299 | ) 300 | Spacer(modifier = Modifier.height(3.dp)) 301 | Divider() 302 | 303 | Spacer(modifier = Modifier.height(8.dp)) 304 | 305 | AdbSwitch( 306 | enabled = isPermissionGranted, 307 | checked = isAdbEnabled, 308 | onCheckedChange = onAdbSwitchClicked 309 | ) 310 | } 311 | } 312 | 313 | @Composable 314 | fun AdbSwitch(enabled: Boolean, checked: Boolean, onCheckedChange: (Boolean) -> Unit) { 315 | val alpha = if (enabled) LocalContentAlpha.current else ContentAlpha.disabled 316 | 317 | Row( 318 | Modifier 319 | .toggleable( 320 | enabled = enabled, 321 | value = checked, 322 | role = Role.Switch, 323 | onValueChange = { onCheckedChange(it) } 324 | ) 325 | .padding(16.dp) 326 | .fillMaxWidth() 327 | ) { 328 | Text(text = "Adb", modifier = Modifier.alpha(alpha)) 329 | Spacer(modifier = Modifier.width(8.dp)) 330 | Switch(enabled = enabled, checked = checked, onCheckedChange = null) 331 | } 332 | } 333 | 334 | @Composable 335 | fun AboutSection( 336 | onHowToUseButtonClicked: () -> Unit, 337 | onLicensesButtonClicked: () -> Unit, 338 | ) { 339 | Column { 340 | Text( 341 | "About", fontSize = 18.sp 342 | ) 343 | Spacer(modifier = Modifier.height(3.dp)) 344 | Divider() 345 | 346 | Spacer(modifier = Modifier.height(8.dp)) 347 | 348 | Button(onClick = onHowToUseButtonClicked) { 349 | Text(stringResource(id = R.string.how_to_use)) 350 | } 351 | Spacer(modifier = Modifier.height(4.dp)) 352 | Button(onClick = onLicensesButtonClicked) { 353 | Text(stringResource(id = R.string.licenses)) 354 | } 355 | } 356 | } 357 | -------------------------------------------------------------------------------- /app/src/main/java/com/nagopy/android/debugassistant/ui/main/MainUiState.kt: -------------------------------------------------------------------------------- 1 | package com.nagopy.android.debugassistant.ui.main 2 | 3 | data class MainUiState( 4 | val isLoading: Boolean, 5 | val proxyHost: String = "", 6 | val proxyPort: String = "", 7 | val isPermissionGranted: Boolean = false, 8 | val isProxyEnabled: Boolean = false, 9 | val isAdbEnabled: Boolean = false, 10 | ) 11 | -------------------------------------------------------------------------------- /app/src/main/java/com/nagopy/android/debugassistant/ui/main/MainViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.nagopy.android.debugassistant.ui.main 2 | 3 | import android.Manifest 4 | import android.app.Application 5 | import android.content.Intent 6 | import android.net.Uri 7 | import androidx.core.app.ShareCompat 8 | import androidx.lifecycle.AndroidViewModel 9 | import androidx.lifecycle.viewModelScope 10 | import com.google.android.gms.oss.licenses.OssLicensesMenuActivity 11 | import com.nagopy.android.debugassistant.BuildConfig 12 | import com.nagopy.android.debugassistant.domain.model.ProxyInfo 13 | import com.nagopy.android.debugassistant.domain.usecase.DisableAdbUseCase 14 | import com.nagopy.android.debugassistant.domain.usecase.DisableProxyUseCase 15 | import com.nagopy.android.debugassistant.domain.usecase.EnableAdbUseCase 16 | import com.nagopy.android.debugassistant.domain.usecase.EnableProxyUseCase 17 | import com.nagopy.android.debugassistant.domain.usecase.GetAdbStatusUseCase 18 | import com.nagopy.android.debugassistant.domain.usecase.GetPermissionStatusUseCase 19 | import com.nagopy.android.debugassistant.domain.usecase.GetProxyStatusUseCase 20 | import com.nagopy.android.debugassistant.domain.usecase.GetUserProxyInfoUseCase 21 | import com.nagopy.android.debugassistant.domain.usecase.PutUserProxyInfoUseCase 22 | import kotlinx.coroutines.flow.MutableStateFlow 23 | import kotlinx.coroutines.flow.SharingStarted 24 | import kotlinx.coroutines.flow.stateIn 25 | import kotlinx.coroutines.flow.update 26 | 27 | class MainViewModel( 28 | application: Application, 29 | private val getProxyStatusUseCase: GetProxyStatusUseCase, 30 | private val enableProxyUseCase: EnableProxyUseCase, 31 | private val disableProxyUseCase: DisableProxyUseCase, 32 | private val getAdbStatusUseCase: GetAdbStatusUseCase, 33 | private val enableAdbUseCase: EnableAdbUseCase, 34 | private val disableAdbUseCase: DisableAdbUseCase, 35 | private val getPermissionStatusUseCase: GetPermissionStatusUseCase, 36 | getUserProxyInfoUseCase: GetUserProxyInfoUseCase, 37 | private val putUserProxyInfoUseCase: PutUserProxyInfoUseCase, 38 | ) : AndroidViewModel(application) { 39 | 40 | private val _viewModelState = MutableStateFlow(MainUiState(isLoading = true)) 41 | val viewModelState = _viewModelState.stateIn(viewModelScope, SharingStarted.Eagerly, _viewModelState.value) 42 | 43 | init { 44 | val proxyInfo = getUserProxyInfoUseCase.getUserProxyInfo() 45 | val isPermissionGranted = getPermissionStatusUseCase.isPermissionGranted(Manifest.permission.WRITE_SECURE_SETTINGS) 46 | val isProxyEnabled = getProxyStatusUseCase.isProxyEnabled() 47 | val isAdbEnabled = getAdbStatusUseCase.isAdbEnabled() 48 | _viewModelState.update { 49 | it.copy( 50 | isLoading = false, 51 | proxyHost = proxyInfo.host, 52 | proxyPort = proxyInfo.port, 53 | isPermissionGranted = isPermissionGranted, 54 | isProxyEnabled = isProxyEnabled, 55 | isAdbEnabled = isAdbEnabled, 56 | ) 57 | } 58 | } 59 | 60 | fun updateStatus() { 61 | updatePermissionStatus() 62 | updateProxyStatus() 63 | updateAdbStatus() 64 | } 65 | 66 | private fun updateProxyStatus() { 67 | _viewModelState.update { 68 | it.copy( 69 | isProxyEnabled = getProxyStatusUseCase.isProxyEnabled() 70 | ) 71 | } 72 | } 73 | 74 | private fun updateAdbStatus() { 75 | _viewModelState.update { 76 | it.copy( 77 | isAdbEnabled = getAdbStatusUseCase.isAdbEnabled() 78 | ) 79 | } 80 | } 81 | 82 | private fun updatePermissionStatus() { 83 | _viewModelState.update { 84 | it.copy( 85 | isPermissionGranted = getPermissionStatusUseCase.isPermissionGranted(Manifest.permission.WRITE_SECURE_SETTINGS) 86 | ) 87 | } 88 | } 89 | 90 | fun onAdbCommandClicked() { 91 | getApplication().let { application -> 92 | application.startActivity( 93 | ShareCompat.IntentBuilder(application) 94 | .setText("adb shell pm grant ${BuildConfig.APPLICATION_ID} android.permission.WRITE_SECURE_SETTINGS") 95 | .setType("text/plain") 96 | .intent 97 | .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP) 98 | ) 99 | } 100 | } 101 | 102 | fun onProxyHostChanged(newValue: String) { 103 | putUserProxyInfoUseCase.putUserProxyInfo( 104 | ProxyInfo( 105 | newValue, 106 | _viewModelState.value.proxyPort 107 | ) 108 | ) 109 | _viewModelState.update { 110 | it.copy( 111 | proxyHost = newValue 112 | ) 113 | } 114 | } 115 | 116 | fun onProxyPortChanged(newValue: String) { 117 | putUserProxyInfoUseCase.putUserProxyInfo( 118 | ProxyInfo( 119 | _viewModelState.value.proxyHost, 120 | newValue 121 | ) 122 | ) 123 | _viewModelState.update { 124 | it.copy( 125 | proxyPort = newValue 126 | ) 127 | } 128 | } 129 | 130 | fun onProxySwitchClicked(checked: Boolean) { 131 | if (checked) { 132 | enableProxyUseCase.enableProxy(viewModelState.value.proxyHost, viewModelState.value.proxyPort) 133 | } else { 134 | disableProxyUseCase.disableProxy() 135 | } 136 | 137 | updateProxyStatus() 138 | } 139 | 140 | fun onAdbSwitchClicked(checked: Boolean) { 141 | if (checked) { 142 | enableAdbUseCase.enableAdb() 143 | } else { 144 | disableAdbUseCase.disableAdb() 145 | } 146 | 147 | updateAdbStatus() 148 | } 149 | 150 | fun onHowToUseButtonClicked() { 151 | getApplication().let { application -> 152 | application.startActivity( 153 | Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/75py/DebugAssistant#install")) 154 | .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP) 155 | ) 156 | } 157 | } 158 | 159 | fun onLicensesButtonClicked() { 160 | getApplication().let { application -> 161 | application.startActivity( 162 | Intent(application, OssLicensesMenuActivity::class.java) 163 | .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP) 164 | ) 165 | } 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /app/src/main/java/com/nagopy/android/debugassistant/ui/theme/Color.kt: -------------------------------------------------------------------------------- 1 | package com.nagopy.android.debugassistant.ui.theme 2 | 3 | import androidx.compose.ui.graphics.Color 4 | 5 | val Purple200 = Color(0xFFBB86FC) 6 | val Purple500 = Color(0xFF6200EE) 7 | val Purple700 = Color(0xFF3700B3) 8 | val Teal200 = Color(0xFF03DAC5) 9 | -------------------------------------------------------------------------------- /app/src/main/java/com/nagopy/android/debugassistant/ui/theme/Shape.kt: -------------------------------------------------------------------------------- 1 | package com.nagopy.android.debugassistant.ui.theme 2 | 3 | import androidx.compose.foundation.shape.RoundedCornerShape 4 | import androidx.compose.material.Shapes 5 | import androidx.compose.ui.unit.dp 6 | 7 | val Shapes = Shapes( 8 | small = RoundedCornerShape(4.dp), 9 | medium = RoundedCornerShape(4.dp), 10 | large = RoundedCornerShape(0.dp) 11 | ) 12 | -------------------------------------------------------------------------------- /app/src/main/java/com/nagopy/android/debugassistant/ui/theme/Theme.kt: -------------------------------------------------------------------------------- 1 | package com.nagopy.android.debugassistant.ui.theme 2 | 3 | import androidx.compose.foundation.isSystemInDarkTheme 4 | import androidx.compose.material.MaterialTheme 5 | import androidx.compose.material.darkColors 6 | import androidx.compose.material.lightColors 7 | import androidx.compose.runtime.Composable 8 | 9 | private val DarkColorPalette = darkColors( 10 | primary = Purple200, 11 | primaryVariant = Purple700, 12 | secondary = Teal200 13 | ) 14 | 15 | private val LightColorPalette = lightColors( 16 | primary = Purple500, 17 | primaryVariant = Purple700, 18 | secondary = Teal200 19 | 20 | /* Other default colors to override 21 | background = Color.White, 22 | surface = Color.White, 23 | onPrimary = Color.White, 24 | onSecondary = Color.Black, 25 | onBackground = Color.Black, 26 | onSurface = Color.Black, 27 | */ 28 | ) 29 | 30 | @Composable 31 | fun DebugAssistantTheme( 32 | darkTheme: Boolean = isSystemInDarkTheme(), 33 | content: @Composable () -> Unit 34 | ) { 35 | val colors = if (darkTheme) { 36 | DarkColorPalette 37 | } else { 38 | LightColorPalette 39 | } 40 | 41 | MaterialTheme( 42 | colors = colors, 43 | typography = Typography, 44 | shapes = Shapes, 45 | content = content 46 | ) 47 | } 48 | -------------------------------------------------------------------------------- /app/src/main/java/com/nagopy/android/debugassistant/ui/theme/Type.kt: -------------------------------------------------------------------------------- 1 | package com.nagopy.android.debugassistant.ui.theme 2 | 3 | import androidx.compose.material.Typography 4 | import androidx.compose.ui.text.TextStyle 5 | import androidx.compose.ui.text.font.FontFamily 6 | import androidx.compose.ui.text.font.FontWeight 7 | import androidx.compose.ui.unit.sp 8 | 9 | // Set of Material typography styles to start with 10 | val Typography = Typography( 11 | body1 = TextStyle( 12 | fontFamily = FontFamily.Default, 13 | fontWeight = FontWeight.Normal, 14 | fontSize = 16.sp 15 | ) 16 | /* Other default text styles to override 17 | button = TextStyle( 18 | fontFamily = FontFamily.Default, 19 | fontWeight = FontWeight.W500, 20 | fontSize = 14.sp 21 | ), 22 | caption = TextStyle( 23 | fontFamily = FontFamily.Default, 24 | fontWeight = FontWeight.Normal, 25 | fontSize = 12.sp 26 | ) 27 | */ 28 | ) 29 | -------------------------------------------------------------------------------- /app/src/main/java/com/nagopy/android/debugassistant/ui/tile/AdbTileService.kt: -------------------------------------------------------------------------------- 1 | package com.nagopy.android.debugassistant.ui.tile 2 | 3 | import android.Manifest 4 | import android.app.KeyguardManager 5 | import android.database.ContentObserver 6 | import android.net.Uri 7 | import android.os.Handler 8 | import android.os.Looper 9 | import android.provider.Settings 10 | import android.service.quicksettings.Tile 11 | import android.service.quicksettings.TileService 12 | import com.nagopy.android.debugassistant.domain.usecase.DisableAdbUseCase 13 | import com.nagopy.android.debugassistant.domain.usecase.EnableAdbUseCase 14 | import com.nagopy.android.debugassistant.domain.usecase.GetAdbStatusUseCase 15 | import com.nagopy.android.debugassistant.domain.usecase.GetPermissionStatusUseCase 16 | import org.koin.android.ext.android.inject 17 | 18 | class AdbTileService : TileService() { 19 | 20 | private val getPermissionStatusUseCase: GetPermissionStatusUseCase by inject() 21 | private val getAdbStatusUseCase: GetAdbStatusUseCase by inject() 22 | private val enableAdbUseCase: EnableAdbUseCase by inject() 23 | private val disableAdbUseCase: DisableAdbUseCase by inject() 24 | private val keyguardManager: KeyguardManager by inject() 25 | 26 | private fun refresh() { 27 | qsTile.state = 28 | if (getPermissionStatusUseCase.isPermissionGranted(Manifest.permission.WRITE_SECURE_SETTINGS)) { 29 | if (getAdbStatusUseCase.isAdbEnabled()) { 30 | Tile.STATE_ACTIVE 31 | } else { 32 | Tile.STATE_INACTIVE 33 | } 34 | } else { 35 | Tile.STATE_UNAVAILABLE 36 | } 37 | qsTile.updateTile() 38 | } 39 | 40 | override fun onStartListening() { 41 | super.onStartListening() 42 | refresh() 43 | contentResolver.registerContentObserver( 44 | Settings.Global.getUriFor(Settings.Global.ADB_ENABLED), 45 | false, 46 | settingsObserver 47 | ) 48 | } 49 | 50 | override fun onStopListening() { 51 | super.onStopListening() 52 | contentResolver.unregisterContentObserver(settingsObserver) 53 | } 54 | 55 | override fun onClick() { 56 | super.onClick() 57 | when (qsTile.state) { 58 | Tile.STATE_ACTIVE -> { 59 | disableAdbUseCase.disableAdb() 60 | } 61 | Tile.STATE_INACTIVE -> { 62 | if (!keyguardManager.isKeyguardLocked) { 63 | enableAdbUseCase.enableAdb() 64 | } 65 | } 66 | } 67 | refresh() 68 | } 69 | 70 | private val handler: Handler = Handler(Looper.getMainLooper()) 71 | private val settingsObserver: ContentObserver = object : ContentObserver(handler) { 72 | override fun onChange(selfChange: Boolean, uri: Uri?) { 73 | refresh() 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /app/src/main/java/com/nagopy/android/debugassistant/ui/tile/ProxyTileService.kt: -------------------------------------------------------------------------------- 1 | package com.nagopy.android.debugassistant.ui.tile 2 | 3 | import android.Manifest 4 | import android.app.KeyguardManager 5 | import android.database.ContentObserver 6 | import android.net.Uri 7 | import android.os.Handler 8 | import android.os.Looper 9 | import android.provider.Settings 10 | import android.service.quicksettings.Tile 11 | import android.service.quicksettings.TileService 12 | import com.nagopy.android.debugassistant.domain.usecase.DisableProxyUseCase 13 | import com.nagopy.android.debugassistant.domain.usecase.EnableProxyUseCase 14 | import com.nagopy.android.debugassistant.domain.usecase.GetPermissionStatusUseCase 15 | import com.nagopy.android.debugassistant.domain.usecase.GetProxyStatusUseCase 16 | import com.nagopy.android.debugassistant.domain.usecase.GetUserProxyInfoUseCase 17 | import org.koin.android.ext.android.inject 18 | 19 | class ProxyTileService : TileService() { 20 | 21 | private val getPermissionStatusUseCase: GetPermissionStatusUseCase by inject() 22 | private val getProxyStatusUseCase: GetProxyStatusUseCase by inject() 23 | private val enableProUseCase: EnableProxyUseCase by inject() 24 | private val disableProxyUseCase: DisableProxyUseCase by inject() 25 | private val getUserProxyInfoUseCase: GetUserProxyInfoUseCase by inject() 26 | private val keyguardManager: KeyguardManager by inject() 27 | 28 | private fun refresh() { 29 | qsTile.state = 30 | if (getPermissionStatusUseCase.isPermissionGranted(Manifest.permission.WRITE_SECURE_SETTINGS) && 31 | getUserProxyInfoUseCase.getUserProxyInfo().isAvailable() 32 | ) { 33 | if (getProxyStatusUseCase.isProxyEnabled()) { 34 | Tile.STATE_ACTIVE 35 | } else { 36 | Tile.STATE_INACTIVE 37 | } 38 | } else { 39 | Tile.STATE_UNAVAILABLE 40 | } 41 | qsTile.updateTile() 42 | } 43 | 44 | override fun onStartListening() { 45 | super.onStartListening() 46 | refresh() 47 | contentResolver.registerContentObserver( 48 | Settings.Global.getUriFor(Settings.Global.HTTP_PROXY), 49 | false, 50 | settingsObserver 51 | ) 52 | } 53 | 54 | override fun onStopListening() { 55 | super.onStopListening() 56 | contentResolver.unregisterContentObserver(settingsObserver) 57 | } 58 | 59 | override fun onClick() { 60 | super.onClick() 61 | when (qsTile.state) { 62 | Tile.STATE_ACTIVE -> { 63 | disableProxyUseCase.disableProxy() 64 | } 65 | Tile.STATE_INACTIVE -> { 66 | if (!keyguardManager.isKeyguardLocked) { 67 | val proxyInfo = getUserProxyInfoUseCase.getUserProxyInfo() 68 | enableProUseCase.enableProxy( 69 | proxyInfo.host, 70 | proxyInfo.port 71 | ) 72 | } 73 | } 74 | } 75 | refresh() 76 | } 77 | 78 | private val handler: Handler = Handler(Looper.getMainLooper()) 79 | private val settingsObserver: ContentObserver = object : ContentObserver(handler) { 80 | override fun onChange(selfChange: Boolean, uri: Uri?) { 81 | refresh() 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_adb_tile.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 10 | 12 | 14 | 16 | 18 | 20 | 22 | 24 | 26 | 28 | 30 | 32 | 34 | 36 | 38 | 40 | 42 | 44 | 46 | 48 | 50 | 52 | 54 | 56 | 58 | 60 | 62 | 64 | 66 | 68 | 70 | 72 | 74 | 75 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 11 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_proxy_tile.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /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.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/75py/DebugAssistant/bff9000aae07541004d1df604dbf1037db3e7b01/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/75py/DebugAssistant/bff9000aae07541004d1df604dbf1037db3e7b01/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/75py/DebugAssistant/bff9000aae07541004d1df604dbf1037db3e7b01/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/75py/DebugAssistant/bff9000aae07541004d1df604dbf1037db3e7b01/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/75py/DebugAssistant/bff9000aae07541004d1df604dbf1037db3e7b01/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/75py/DebugAssistant/bff9000aae07541004d1df604dbf1037db3e7b01/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/75py/DebugAssistant/bff9000aae07541004d1df604dbf1037db3e7b01/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/75py/DebugAssistant/bff9000aae07541004d1df604dbf1037db3e7b01/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/75py/DebugAssistant/bff9000aae07541004d1df604dbf1037db3e7b01/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/75py/DebugAssistant/bff9000aae07541004d1df604dbf1037db3e7b01/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values-ja/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Debug Assistant 3 | WRITE_SECURE_SETTINGSのパーミッションが許可されていません。\n以下のコマンドを実行してください。 4 | 使い方 5 | オープンソースライセンス 6 | HTTP_PROXYは組織の管理者によって利用されている場合があります。この機能の利用は自己責任でお願いします。 7 | -------------------------------------------------------------------------------- /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 | Debug Assistant 3 | WRITE_SECURE_SETTINGS is not granted.\nRun the following command: 4 | How to use 5 | Open source licenses 6 | HTTP_PROXY may be managed by an administrator / organization. Please use this feature at your own risk. 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext { 3 | compose_version = '1.5.0' 4 | } 5 | repositories { 6 | google() 7 | } 8 | dependencies { 9 | classpath("com.google.android.gms:oss-licenses-plugin:0.10.6") 10 | } 11 | }// Top-level build file where you can add configuration options common to all sub-projects/modules. 12 | plugins { 13 | id 'com.android.application' version '8.1.2' apply false 14 | id 'com.android.library' version '8.1.2' apply false 15 | id 'org.jetbrains.kotlin.android' version '1.8.22' apply false 16 | id("org.jlleitschuh.gradle.ktlint") version "11.5.1" 17 | id("io.gitlab.arturbosch.detekt") version "1.23.1" 18 | id 'org.jetbrains.kotlin.jvm' version '1.9.0' apply false 19 | } 20 | 21 | task clean(type: Delete) { 22 | delete rootProject.buildDir 23 | } 24 | -------------------------------------------------------------------------------- /data/repository/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /data/repository/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | id 'org.jetbrains.kotlin.android' 4 | } 5 | 6 | android { 7 | compileSdk 34 8 | 9 | defaultConfig { 10 | minSdk 28 11 | targetSdk 34 12 | 13 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 14 | consumerProguardFiles "consumer-rules.pro" 15 | } 16 | 17 | buildTypes { 18 | release { 19 | minifyEnabled true 20 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 21 | } 22 | } 23 | compileOptions { 24 | sourceCompatibility JavaVersion.VERSION_17 25 | targetCompatibility JavaVersion.VERSION_17 26 | } 27 | kotlinOptions { 28 | jvmTarget = '17' 29 | } 30 | namespace 'com.nagopy.android.debugassistant.data.repository' 31 | } 32 | 33 | dependencies { 34 | implementation "io.insert-koin:koin-android:3.4.3" 35 | implementation "androidx.security:security-crypto:1.0.0" 36 | } -------------------------------------------------------------------------------- /data/repository/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/75py/DebugAssistant/bff9000aae07541004d1df604dbf1037db3e7b01/data/repository/consumer-rules.pro -------------------------------------------------------------------------------- /data/repository/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 -------------------------------------------------------------------------------- /data/repository/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /data/repository/src/main/java/com/nagopy/android/debugassistant/data/repository/GlobalSettingsRepository.kt: -------------------------------------------------------------------------------- 1 | package com.nagopy.android.debugassistant.data.repository 2 | 3 | interface GlobalSettingsRepository { 4 | 5 | fun putString(name: String, value: String?): Boolean 6 | 7 | fun getString(name: String): String? 8 | 9 | fun putInt(name: String, value: Int): Boolean 10 | 11 | fun getInt(name: String, def: Int): Int 12 | 13 | companion object { 14 | const val DISABLE_PROXY_VALUE = ":0" 15 | const val SETTING_ON = 1 16 | const val SETTING_OFF = 0 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /data/repository/src/main/java/com/nagopy/android/debugassistant/data/repository/RepositoryModule.kt: -------------------------------------------------------------------------------- 1 | package com.nagopy.android.debugassistant.data.repository 2 | 3 | import com.nagopy.android.debugassistant.data.repository.impl.GlobalSettingRepositoryImpl 4 | import com.nagopy.android.debugassistant.data.repository.impl.UserPreferencesRepositoryImpl 5 | import org.koin.android.ext.koin.androidApplication 6 | import org.koin.dsl.module 7 | 8 | val repositoryModule = module { 9 | single { GlobalSettingRepositoryImpl(get()) } 10 | 11 | single { UserPreferencesRepositoryImpl(androidApplication().applicationContext) } 12 | } 13 | -------------------------------------------------------------------------------- /data/repository/src/main/java/com/nagopy/android/debugassistant/data/repository/UserPreferencesRepository.kt: -------------------------------------------------------------------------------- 1 | package com.nagopy.android.debugassistant.data.repository 2 | 3 | interface UserPreferencesRepository { 4 | var proxyHost: String 5 | var proxyPort: String 6 | } 7 | -------------------------------------------------------------------------------- /data/repository/src/main/java/com/nagopy/android/debugassistant/data/repository/impl/GlobalSettingRepositoryImpl.kt: -------------------------------------------------------------------------------- 1 | package com.nagopy.android.debugassistant.data.repository.impl 2 | 3 | import android.content.ContentResolver 4 | import android.provider.Settings 5 | import com.nagopy.android.debugassistant.data.repository.GlobalSettingsRepository 6 | 7 | internal class GlobalSettingRepositoryImpl(private val resolver: ContentResolver) : 8 | GlobalSettingsRepository { 9 | 10 | override fun putString(name: String, value: String?): Boolean { 11 | return Settings.Global.putString(resolver, name, value) 12 | } 13 | 14 | override fun getString(name: String): String? { 15 | return Settings.Global.getString(resolver, name) 16 | } 17 | 18 | override fun putInt(name: String, value: Int): Boolean { 19 | return Settings.Global.putInt(resolver, name, value) 20 | } 21 | 22 | override fun getInt(name: String, def: Int): Int { 23 | return Settings.Global.getInt(resolver, name, def) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /data/repository/src/main/java/com/nagopy/android/debugassistant/data/repository/impl/UserPreferencesRepositoryImpl.kt: -------------------------------------------------------------------------------- 1 | package com.nagopy.android.debugassistant.data.repository.impl 2 | 3 | import android.content.Context 4 | import android.content.SharedPreferences 5 | import androidx.security.crypto.EncryptedSharedPreferences 6 | import androidx.security.crypto.MasterKeys 7 | import com.nagopy.android.debugassistant.data.repository.UserPreferencesRepository 8 | 9 | internal class UserPreferencesRepositoryImpl(context: Context) : UserPreferencesRepository { 10 | private val sharedPreferences: SharedPreferences 11 | 12 | init { 13 | val masterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC) 14 | sharedPreferences = EncryptedSharedPreferences.create( 15 | "secret_shared_prefs", 16 | masterKeyAlias, 17 | context, 18 | EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, 19 | EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM 20 | ) 21 | } 22 | 23 | override var proxyHost: String 24 | get() = sharedPreferences.getString("proxyHost", "") ?: "" 25 | set(value) { 26 | sharedPreferences.edit().putString("proxyHost", value).apply() 27 | } 28 | override var proxyPort: String 29 | get() = sharedPreferences.getString("proxyPort", "") ?: "" 30 | set(value) { 31 | sharedPreferences.edit().putString("proxyPort", value).apply() 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /detekt.yml: -------------------------------------------------------------------------------- 1 | complexity: 2 | LongParameterList: 3 | active: false 4 | TooManyFunctions: 5 | ignorePrivate: true 6 | 7 | naming: 8 | FunctionNaming: 9 | ignoreAnnotated: [ 'Composable' ] 10 | 11 | style: 12 | MagicNumber: 13 | ignorePropertyDeclaration: true 14 | MaxLineLength: 15 | maxLineLength: 140 16 | -------------------------------------------------------------------------------- /domain/model/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /domain/model/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java-library' 3 | id 'org.jetbrains.kotlin.jvm' 4 | } 5 | 6 | java { 7 | sourceCompatibility = JavaVersion.VERSION_17 8 | targetCompatibility = JavaVersion.VERSION_17 9 | } 10 | 11 | dependencies { 12 | testImplementation 'junit:junit:4.13.2' 13 | testImplementation 'org.jetbrains.kotlin:kotlin-test' 14 | } 15 | -------------------------------------------------------------------------------- /domain/model/src/main/java/com/nagopy/android/debugassistant/domain/model/ProxyInfo.kt: -------------------------------------------------------------------------------- 1 | package com.nagopy.android.debugassistant.domain.model 2 | 3 | data class ProxyInfo( 4 | val host: String, 5 | val port: String 6 | ) { 7 | fun isAvailable(): Boolean { 8 | return host.isNotEmpty() && port.isNotEmpty() 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /domain/model/src/test/java/com/nagopy/android/debugassistant/domain/model/ProxyInfoTest.kt: -------------------------------------------------------------------------------- 1 | package com.nagopy.android.debugassistant.domain.model 2 | 3 | import org.junit.Test 4 | import kotlin.test.assertFalse 5 | import kotlin.test.assertTrue 6 | 7 | class ProxyInfoTest { 8 | 9 | @Test 10 | fun isAvailable() { 11 | assertFalse(ProxyInfo("", "").isAvailable()) 12 | assertFalse(ProxyInfo("localhost", "").isAvailable()) 13 | assertFalse(ProxyInfo("", "8888").isAvailable()) 14 | assertTrue(ProxyInfo("localhost", "8888").isAvailable()) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /domain/usecase/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /domain/usecase/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | id 'org.jetbrains.kotlin.android' 4 | } 5 | 6 | android { 7 | compileSdk 34 8 | 9 | defaultConfig { 10 | minSdk 28 11 | targetSdk 34 12 | 13 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 14 | consumerProguardFiles "consumer-rules.pro" 15 | } 16 | 17 | buildTypes { 18 | release { 19 | minifyEnabled true 20 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 21 | } 22 | } 23 | compileOptions { 24 | sourceCompatibility JavaVersion.VERSION_17 25 | targetCompatibility JavaVersion.VERSION_17 26 | } 27 | kotlinOptions { 28 | jvmTarget = '17' 29 | } 30 | namespace 'com.nagopy.android.debugassistant.domain.usecase' 31 | } 32 | 33 | dependencies { 34 | implementation project(":domain:model") 35 | implementation project(":data:repository") 36 | implementation "io.insert-koin:koin-android:3.4.3" 37 | testImplementation 'junit:junit:4.13.2' 38 | testImplementation "io.mockk:mockk:1.13.7" 39 | testImplementation 'org.jetbrains.kotlin:kotlin-test' 40 | } -------------------------------------------------------------------------------- /domain/usecase/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/75py/DebugAssistant/bff9000aae07541004d1df604dbf1037db3e7b01/domain/usecase/consumer-rules.pro -------------------------------------------------------------------------------- /domain/usecase/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 -------------------------------------------------------------------------------- /domain/usecase/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /domain/usecase/src/main/java/com/nagopy/android/debugassistant/domain/usecase/DisableAdbUseCase.kt: -------------------------------------------------------------------------------- 1 | package com.nagopy.android.debugassistant.domain.usecase 2 | 3 | interface DisableAdbUseCase { 4 | fun disableAdb(): Boolean 5 | } 6 | -------------------------------------------------------------------------------- /domain/usecase/src/main/java/com/nagopy/android/debugassistant/domain/usecase/DisableProxyUseCase.kt: -------------------------------------------------------------------------------- 1 | package com.nagopy.android.debugassistant.domain.usecase 2 | 3 | interface DisableProxyUseCase { 4 | fun disableProxy(): Boolean 5 | } 6 | -------------------------------------------------------------------------------- /domain/usecase/src/main/java/com/nagopy/android/debugassistant/domain/usecase/EnableAdbUseCase.kt: -------------------------------------------------------------------------------- 1 | package com.nagopy.android.debugassistant.domain.usecase 2 | 3 | interface EnableAdbUseCase { 4 | 5 | fun enableAdb(): Boolean 6 | } 7 | -------------------------------------------------------------------------------- /domain/usecase/src/main/java/com/nagopy/android/debugassistant/domain/usecase/EnableProxyUseCase.kt: -------------------------------------------------------------------------------- 1 | package com.nagopy.android.debugassistant.domain.usecase 2 | 3 | interface EnableProxyUseCase { 4 | fun enableProxy(host: String, port: String): Boolean 5 | } 6 | -------------------------------------------------------------------------------- /domain/usecase/src/main/java/com/nagopy/android/debugassistant/domain/usecase/GetAdbStatusUseCase.kt: -------------------------------------------------------------------------------- 1 | package com.nagopy.android.debugassistant.domain.usecase 2 | 3 | interface GetAdbStatusUseCase { 4 | 5 | fun isAdbEnabled(): Boolean 6 | } 7 | -------------------------------------------------------------------------------- /domain/usecase/src/main/java/com/nagopy/android/debugassistant/domain/usecase/GetPermissionStatusUseCase.kt: -------------------------------------------------------------------------------- 1 | package com.nagopy.android.debugassistant.domain.usecase 2 | 3 | interface GetPermissionStatusUseCase { 4 | 5 | fun isPermissionGranted(permission: String): Boolean 6 | } 7 | -------------------------------------------------------------------------------- /domain/usecase/src/main/java/com/nagopy/android/debugassistant/domain/usecase/GetProxyStatusUseCase.kt: -------------------------------------------------------------------------------- 1 | package com.nagopy.android.debugassistant.domain.usecase 2 | 3 | interface GetProxyStatusUseCase { 4 | fun isProxyEnabled(): Boolean 5 | } 6 | -------------------------------------------------------------------------------- /domain/usecase/src/main/java/com/nagopy/android/debugassistant/domain/usecase/GetUserProxyInfoUseCase.kt: -------------------------------------------------------------------------------- 1 | package com.nagopy.android.debugassistant.domain.usecase 2 | 3 | import com.nagopy.android.debugassistant.domain.model.ProxyInfo 4 | 5 | interface GetUserProxyInfoUseCase { 6 | fun getUserProxyInfo(): ProxyInfo 7 | } 8 | -------------------------------------------------------------------------------- /domain/usecase/src/main/java/com/nagopy/android/debugassistant/domain/usecase/PutUserProxyInfoUseCase.kt: -------------------------------------------------------------------------------- 1 | package com.nagopy.android.debugassistant.domain.usecase 2 | 3 | import com.nagopy.android.debugassistant.domain.model.ProxyInfo 4 | 5 | interface PutUserProxyInfoUseCase { 6 | fun putUserProxyInfo(proxyInfo: ProxyInfo) 7 | } 8 | -------------------------------------------------------------------------------- /domain/usecase/src/main/java/com/nagopy/android/debugassistant/domain/usecase/UseCaseModule.kt: -------------------------------------------------------------------------------- 1 | package com.nagopy.android.debugassistant.domain.usecase 2 | 3 | import com.nagopy.android.debugassistant.domain.usecase.DisableAdbUseCase 4 | import com.nagopy.android.debugassistant.domain.usecase.DisableProxyUseCase 5 | import com.nagopy.android.debugassistant.domain.usecase.EnableAdbUseCase 6 | import com.nagopy.android.debugassistant.domain.usecase.EnableProxyUseCase 7 | import com.nagopy.android.debugassistant.domain.usecase.GetAdbStatusUseCase 8 | import com.nagopy.android.debugassistant.domain.usecase.GetPermissionStatusUseCase 9 | import com.nagopy.android.debugassistant.domain.usecase.GetProxyStatusUseCase 10 | import com.nagopy.android.debugassistant.domain.usecase.GetUserProxyInfoUseCase 11 | import com.nagopy.android.debugassistant.domain.usecase.PutUserProxyInfoUseCase 12 | import com.nagopy.android.debugassistant.domain.usecase.interactor.DisableAdbInteractor 13 | import com.nagopy.android.debugassistant.domain.usecase.interactor.DisableProxyInteractor 14 | import com.nagopy.android.debugassistant.domain.usecase.interactor.EnableAdbInteractor 15 | import com.nagopy.android.debugassistant.domain.usecase.interactor.EnableProxyInteractor 16 | import com.nagopy.android.debugassistant.domain.usecase.interactor.GetAdbStatusInteractor 17 | import com.nagopy.android.debugassistant.domain.usecase.interactor.GetPermissionStatusInteractor 18 | import com.nagopy.android.debugassistant.domain.usecase.interactor.GetProxyStatusInteractor 19 | import com.nagopy.android.debugassistant.domain.usecase.interactor.GetUserProxyInfoInteractor 20 | import com.nagopy.android.debugassistant.domain.usecase.interactor.PutUserProxyInfoInteractor 21 | import org.koin.dsl.module 22 | 23 | val domainModule = module { 24 | 25 | single { GetProxyStatusInteractor(get()) } 26 | 27 | single { EnableProxyInteractor(get()) } 28 | 29 | single { DisableProxyInteractor(get()) } 30 | 31 | single { GetAdbStatusInteractor(get()) } 32 | 33 | single { EnableAdbInteractor(get()) } 34 | 35 | single { DisableAdbInteractor(get()) } 36 | 37 | single { GetPermissionStatusInteractor(get()) } 38 | 39 | single { GetUserProxyInfoInteractor(get()) } 40 | 41 | single { PutUserProxyInfoInteractor(get()) } 42 | } 43 | -------------------------------------------------------------------------------- /domain/usecase/src/main/java/com/nagopy/android/debugassistant/domain/usecase/interactor/DisableAdbInteractor.kt: -------------------------------------------------------------------------------- 1 | package com.nagopy.android.debugassistant.domain.usecase.interactor 2 | 3 | import android.provider.Settings 4 | import com.nagopy.android.debugassistant.data.repository.GlobalSettingsRepository 5 | import com.nagopy.android.debugassistant.domain.usecase.DisableAdbUseCase 6 | 7 | class DisableAdbInteractor( 8 | private val globalSettingsRepository: GlobalSettingsRepository 9 | ) : DisableAdbUseCase { 10 | 11 | override fun disableAdb(): Boolean { 12 | return globalSettingsRepository.putInt( 13 | Settings.Global.ADB_ENABLED, 14 | GlobalSettingsRepository.SETTING_OFF 15 | ) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /domain/usecase/src/main/java/com/nagopy/android/debugassistant/domain/usecase/interactor/DisableProxyInteractor.kt: -------------------------------------------------------------------------------- 1 | package com.nagopy.android.debugassistant.domain.usecase.interactor 2 | 3 | import android.provider.Settings 4 | import com.nagopy.android.debugassistant.data.repository.GlobalSettingsRepository 5 | import com.nagopy.android.debugassistant.domain.usecase.DisableProxyUseCase 6 | 7 | class DisableProxyInteractor( 8 | private val globalSettingsRepository: GlobalSettingsRepository 9 | ) : DisableProxyUseCase { 10 | 11 | override fun disableProxy(): Boolean { 12 | return globalSettingsRepository.putString( 13 | Settings.Global.HTTP_PROXY, 14 | GlobalSettingsRepository.DISABLE_PROXY_VALUE 15 | ) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /domain/usecase/src/main/java/com/nagopy/android/debugassistant/domain/usecase/interactor/EnableAdbInteractor.kt: -------------------------------------------------------------------------------- 1 | package com.nagopy.android.debugassistant.domain.usecase.interactor 2 | 3 | import android.provider.Settings 4 | import com.nagopy.android.debugassistant.data.repository.GlobalSettingsRepository 5 | import com.nagopy.android.debugassistant.domain.usecase.EnableAdbUseCase 6 | 7 | class EnableAdbInteractor( 8 | private val globalSettingsRepository: GlobalSettingsRepository, 9 | ) : EnableAdbUseCase { 10 | 11 | override fun enableAdb(): Boolean { 12 | return globalSettingsRepository.putInt( 13 | Settings.Global.ADB_ENABLED, 14 | GlobalSettingsRepository.SETTING_ON 15 | ) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /domain/usecase/src/main/java/com/nagopy/android/debugassistant/domain/usecase/interactor/EnableProxyInteractor.kt: -------------------------------------------------------------------------------- 1 | package com.nagopy.android.debugassistant.domain.usecase.interactor 2 | 3 | import android.provider.Settings 4 | import com.nagopy.android.debugassistant.data.repository.GlobalSettingsRepository 5 | import com.nagopy.android.debugassistant.domain.usecase.EnableProxyUseCase 6 | 7 | class EnableProxyInteractor( 8 | private val globalSettingsRepository: GlobalSettingsRepository 9 | ) : EnableProxyUseCase { 10 | 11 | override fun enableProxy(host: String, port: String): Boolean { 12 | return globalSettingsRepository.putString(Settings.Global.HTTP_PROXY, "$host:$port") 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /domain/usecase/src/main/java/com/nagopy/android/debugassistant/domain/usecase/interactor/GetAdbStatusInteractor.kt: -------------------------------------------------------------------------------- 1 | package com.nagopy.android.debugassistant.domain.usecase.interactor 2 | 3 | import android.provider.Settings 4 | import com.nagopy.android.debugassistant.data.repository.GlobalSettingsRepository 5 | import com.nagopy.android.debugassistant.domain.usecase.GetAdbStatusUseCase 6 | 7 | class GetAdbStatusInteractor( 8 | private val globalSettingsRepository: GlobalSettingsRepository, 9 | ) : GetAdbStatusUseCase { 10 | 11 | override fun isAdbEnabled(): Boolean { 12 | return globalSettingsRepository.getInt( 13 | Settings.Global.ADB_ENABLED, 14 | GlobalSettingsRepository.SETTING_OFF 15 | ) != GlobalSettingsRepository.SETTING_OFF 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /domain/usecase/src/main/java/com/nagopy/android/debugassistant/domain/usecase/interactor/GetPermissionStatusInteractor.kt: -------------------------------------------------------------------------------- 1 | package com.nagopy.android.debugassistant.domain.usecase.interactor 2 | 3 | import android.content.Context 4 | import android.content.pm.PackageManager 5 | import androidx.core.content.ContextCompat 6 | import com.nagopy.android.debugassistant.domain.usecase.GetPermissionStatusUseCase 7 | 8 | class GetPermissionStatusInteractor( 9 | private val context: Context 10 | ) : GetPermissionStatusUseCase { 11 | override fun isPermissionGranted(permission: String): Boolean { 12 | return ContextCompat.checkSelfPermission( 13 | context, 14 | permission 15 | ) == PackageManager.PERMISSION_GRANTED 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /domain/usecase/src/main/java/com/nagopy/android/debugassistant/domain/usecase/interactor/GetProxyStatusInteractor.kt: -------------------------------------------------------------------------------- 1 | package com.nagopy.android.debugassistant.domain.usecase.interactor 2 | 3 | import android.provider.Settings 4 | import com.nagopy.android.debugassistant.data.repository.GlobalSettingsRepository 5 | import com.nagopy.android.debugassistant.domain.usecase.GetProxyStatusUseCase 6 | 7 | class GetProxyStatusInteractor( 8 | private val globalSettingsRepository: GlobalSettingsRepository, 9 | ) : GetProxyStatusUseCase { 10 | 11 | override fun isProxyEnabled(): Boolean { 12 | val currentValue = globalSettingsRepository.getString(Settings.Global.HTTP_PROXY) 13 | return !currentValue.isNullOrEmpty() && currentValue != GlobalSettingsRepository.DISABLE_PROXY_VALUE 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /domain/usecase/src/main/java/com/nagopy/android/debugassistant/domain/usecase/interactor/GetUserProxyInfoInteractor.kt: -------------------------------------------------------------------------------- 1 | package com.nagopy.android.debugassistant.domain.usecase.interactor 2 | 3 | import com.nagopy.android.debugassistant.data.repository.UserPreferencesRepository 4 | import com.nagopy.android.debugassistant.domain.model.ProxyInfo 5 | import com.nagopy.android.debugassistant.domain.usecase.GetUserProxyInfoUseCase 6 | 7 | class GetUserProxyInfoInteractor( 8 | private val userPreferencesRepository: UserPreferencesRepository 9 | ) : GetUserProxyInfoUseCase { 10 | 11 | override fun getUserProxyInfo(): ProxyInfo { 12 | return ProxyInfo(userPreferencesRepository.proxyHost, userPreferencesRepository.proxyPort) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /domain/usecase/src/main/java/com/nagopy/android/debugassistant/domain/usecase/interactor/PutUserProxyInfoInteractor.kt: -------------------------------------------------------------------------------- 1 | package com.nagopy.android.debugassistant.domain.usecase.interactor 2 | 3 | import com.nagopy.android.debugassistant.data.repository.UserPreferencesRepository 4 | import com.nagopy.android.debugassistant.domain.model.ProxyInfo 5 | import com.nagopy.android.debugassistant.domain.usecase.PutUserProxyInfoUseCase 6 | 7 | class PutUserProxyInfoInteractor( 8 | private val userPreferencesRepository: UserPreferencesRepository 9 | ) : PutUserProxyInfoUseCase { 10 | 11 | override fun putUserProxyInfo(proxyInfo: ProxyInfo) { 12 | userPreferencesRepository.proxyHost = proxyInfo.host 13 | userPreferencesRepository.proxyPort = proxyInfo.port 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /domain/usecase/src/test/java/com/nagopy/android/debugassistant/domain/usecase/interactor/DisableAdbInteractorTest.kt: -------------------------------------------------------------------------------- 1 | package com.nagopy.android.debugassistant.domain.usecase.interactor 2 | 3 | import android.provider.Settings 4 | import com.nagopy.android.debugassistant.data.repository.GlobalSettingsRepository 5 | import io.mockk.every 6 | import io.mockk.mockk 7 | import io.mockk.verify 8 | import org.junit.Before 9 | import org.junit.Test 10 | 11 | class DisableAdbInteractorTest { 12 | 13 | private lateinit var disableAdbInteractor: DisableAdbInteractor 14 | private lateinit var globalSettingsRepository: GlobalSettingsRepository 15 | 16 | @Before 17 | fun setUp() { 18 | globalSettingsRepository = mockk(relaxed = true) 19 | disableAdbInteractor = DisableAdbInteractor(globalSettingsRepository) 20 | } 21 | 22 | @Test 23 | fun disableAdb_putSuccess() { 24 | every { globalSettingsRepository.putInt(any(), any()) } returns true 25 | val ret = disableAdbInteractor.disableAdb() 26 | kotlin.test.assertTrue(ret) 27 | verify { 28 | globalSettingsRepository.putInt( 29 | Settings.Global.ADB_ENABLED, 30 | GlobalSettingsRepository.SETTING_OFF 31 | ) 32 | } 33 | } 34 | 35 | @Test 36 | fun disableAdb_putError() { 37 | every { globalSettingsRepository.putInt(any(), any()) } returns false 38 | val ret = disableAdbInteractor.disableAdb() 39 | kotlin.test.assertFalse(ret) 40 | verify { 41 | globalSettingsRepository.putInt( 42 | Settings.Global.ADB_ENABLED, 43 | GlobalSettingsRepository.SETTING_OFF 44 | ) 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /domain/usecase/src/test/java/com/nagopy/android/debugassistant/domain/usecase/interactor/DisableProxyInteractorTest.kt: -------------------------------------------------------------------------------- 1 | package com.nagopy.android.debugassistant.domain.usecase.interactor 2 | 3 | import android.provider.Settings 4 | import com.nagopy.android.debugassistant.data.repository.GlobalSettingsRepository 5 | import io.mockk.every 6 | import io.mockk.mockk 7 | import io.mockk.verify 8 | import org.junit.Before 9 | import org.junit.Test 10 | import kotlin.test.assertFalse 11 | import kotlin.test.assertTrue 12 | 13 | class DisableProxyInteractorTest { 14 | 15 | private lateinit var disableProxyInteractor: DisableProxyInteractor 16 | private lateinit var globalSettingsRepository: GlobalSettingsRepository 17 | 18 | @Before 19 | fun setUp() { 20 | globalSettingsRepository = mockk(relaxed = true) 21 | disableProxyInteractor = 22 | DisableProxyInteractor(globalSettingsRepository) 23 | } 24 | 25 | @Test 26 | fun disableProxy_putSuccess() { 27 | every { globalSettingsRepository.putString(any(), any()) } returns true 28 | val ret = disableProxyInteractor.disableProxy() 29 | assertTrue(ret) 30 | verify { 31 | globalSettingsRepository.putString( 32 | Settings.Global.HTTP_PROXY, 33 | GlobalSettingsRepository.DISABLE_PROXY_VALUE 34 | ) 35 | } 36 | } 37 | 38 | @Test 39 | fun disableProxy_putError() { 40 | every { globalSettingsRepository.putString(any(), any()) } returns false 41 | val ret2 = disableProxyInteractor.disableProxy() 42 | assertFalse(ret2) 43 | verify { 44 | globalSettingsRepository.putString( 45 | Settings.Global.HTTP_PROXY, 46 | GlobalSettingsRepository.DISABLE_PROXY_VALUE 47 | ) 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /domain/usecase/src/test/java/com/nagopy/android/debugassistant/domain/usecase/interactor/EnableAdbInteractorTest.kt: -------------------------------------------------------------------------------- 1 | package com.nagopy.android.debugassistant.domain.usecase.interactor 2 | 3 | import android.provider.Settings 4 | import com.nagopy.android.debugassistant.data.repository.GlobalSettingsRepository 5 | import io.mockk.every 6 | import io.mockk.mockk 7 | import io.mockk.verify 8 | import org.junit.Before 9 | import org.junit.Test 10 | 11 | class EnableAdbInteractorTest { 12 | 13 | private lateinit var enableAdbInteractor: EnableAdbInteractor 14 | private lateinit var globalSettingsRepository: GlobalSettingsRepository 15 | 16 | @Before 17 | fun setUp() { 18 | globalSettingsRepository = mockk(relaxed = true) 19 | enableAdbInteractor = EnableAdbInteractor(globalSettingsRepository) 20 | } 21 | 22 | @Test 23 | fun enableAdb_putSuccess() { 24 | every { globalSettingsRepository.putInt(any(), any()) } returns true 25 | val ret = enableAdbInteractor.enableAdb() 26 | kotlin.test.assertTrue(ret) 27 | verify { 28 | globalSettingsRepository.putInt( 29 | Settings.Global.ADB_ENABLED, 30 | GlobalSettingsRepository.SETTING_ON 31 | ) 32 | } 33 | } 34 | 35 | @Test 36 | fun enableAdb_putError() { 37 | every { globalSettingsRepository.putInt(any(), any()) } returns false 38 | val ret = enableAdbInteractor.enableAdb() 39 | kotlin.test.assertFalse(ret) 40 | verify { 41 | globalSettingsRepository.putInt( 42 | Settings.Global.ADB_ENABLED, 43 | GlobalSettingsRepository.SETTING_ON 44 | ) 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /domain/usecase/src/test/java/com/nagopy/android/debugassistant/domain/usecase/interactor/EnableProxyInteractorTest.kt: -------------------------------------------------------------------------------- 1 | package com.nagopy.android.debugassistant.domain.usecase.interactor 2 | 3 | import android.provider.Settings 4 | import com.nagopy.android.debugassistant.data.repository.GlobalSettingsRepository 5 | import io.mockk.every 6 | import io.mockk.mockk 7 | import io.mockk.verify 8 | import org.junit.Before 9 | import org.junit.Test 10 | 11 | class EnableProxyInteractorTest { 12 | 13 | private lateinit var enableProxyInteractor: EnableProxyInteractor 14 | private lateinit var globalSettingsRepository: GlobalSettingsRepository 15 | 16 | @Before 17 | fun setUp() { 18 | globalSettingsRepository = mockk(relaxed = true) 19 | enableProxyInteractor = 20 | EnableProxyInteractor(globalSettingsRepository) 21 | } 22 | 23 | @Test 24 | fun enableProxy_putSuccess() { 25 | val host = "localhost" 26 | val port = "8080" 27 | 28 | every { globalSettingsRepository.putString(any(), any()) } returns true 29 | val ret = enableProxyInteractor.enableProxy(host, port) 30 | kotlin.test.assertTrue(ret) 31 | verify { 32 | globalSettingsRepository.putString(Settings.Global.HTTP_PROXY, "$host:$port") 33 | } 34 | } 35 | 36 | @Test 37 | fun enableProxy_putError() { 38 | val host = "localhost" 39 | val port = "8080" 40 | 41 | every { globalSettingsRepository.putString(any(), any()) } returns false 42 | val ret2 = enableProxyInteractor.enableProxy(host, port) 43 | kotlin.test.assertFalse(ret2) 44 | verify { globalSettingsRepository.putString(Settings.Global.HTTP_PROXY, "$host:$port") } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /domain/usecase/src/test/java/com/nagopy/android/debugassistant/domain/usecase/interactor/GetAdbStatusInteractorTest.kt: -------------------------------------------------------------------------------- 1 | package com.nagopy.android.debugassistant.domain.usecase.interactor 2 | 3 | import android.provider.Settings 4 | import com.nagopy.android.debugassistant.data.repository.GlobalSettingsRepository 5 | import io.mockk.clearAllMocks 6 | import io.mockk.every 7 | import io.mockk.mockk 8 | import io.mockk.verify 9 | import org.junit.Before 10 | import org.junit.Test 11 | 12 | class GetAdbStatusInteractorTest { 13 | private lateinit var getAdbStatusInteractor: GetAdbStatusInteractor 14 | private lateinit var globalSettingsRepository: GlobalSettingsRepository 15 | 16 | @Before 17 | fun setUp() { 18 | globalSettingsRepository = mockk(relaxed = true) 19 | getAdbStatusInteractor = 20 | GetAdbStatusInteractor(globalSettingsRepository) 21 | } 22 | 23 | @Test 24 | fun isProxyEnabled() { 25 | every { 26 | globalSettingsRepository.getInt( 27 | any(), 28 | any() 29 | ) 30 | } returns GlobalSettingsRepository.SETTING_OFF 31 | kotlin.test.assertFalse(getAdbStatusInteractor.isAdbEnabled()) 32 | verify { 33 | globalSettingsRepository.getInt( 34 | Settings.Global.ADB_ENABLED, 35 | GlobalSettingsRepository.SETTING_OFF 36 | ) 37 | } 38 | 39 | clearAllMocks() 40 | 41 | every { 42 | globalSettingsRepository.getInt( 43 | any(), 44 | any() 45 | ) 46 | } returns GlobalSettingsRepository.SETTING_ON 47 | kotlin.test.assertTrue(getAdbStatusInteractor.isAdbEnabled()) 48 | verify { 49 | globalSettingsRepository.getInt( 50 | Settings.Global.ADB_ENABLED, 51 | GlobalSettingsRepository.SETTING_OFF 52 | ) 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /domain/usecase/src/test/java/com/nagopy/android/debugassistant/domain/usecase/interactor/GetProxyStatusInteractorTest.kt: -------------------------------------------------------------------------------- 1 | package com.nagopy.android.debugassistant.domain.usecase.interactor 2 | 3 | import android.provider.Settings 4 | import com.nagopy.android.debugassistant.data.repository.GlobalSettingsRepository 5 | import com.nagopy.android.debugassistant.data.repository.UserPreferencesRepository 6 | import io.mockk.every 7 | import io.mockk.mockk 8 | import io.mockk.verify 9 | import org.junit.Before 10 | import org.junit.Test 11 | 12 | class GetProxyStatusInteractorTest { 13 | private lateinit var getProxyStatusInteractor: GetProxyStatusInteractor 14 | private lateinit var globalSettingsRepository: GlobalSettingsRepository 15 | private lateinit var userPreferencesRepository: UserPreferencesRepository 16 | 17 | @Before 18 | fun setUp() { 19 | globalSettingsRepository = mockk(relaxed = true) 20 | userPreferencesRepository = mockk(relaxed = true) 21 | getProxyStatusInteractor = 22 | GetProxyStatusInteractor(globalSettingsRepository) 23 | } 24 | 25 | @Test 26 | fun isProxyEnabled() { 27 | every { globalSettingsRepository.getString(Settings.Global.HTTP_PROXY) } returns null 28 | kotlin.test.assertFalse(getProxyStatusInteractor.isProxyEnabled()) 29 | verify { globalSettingsRepository.getString(Settings.Global.HTTP_PROXY) } 30 | 31 | every { globalSettingsRepository.getString(Settings.Global.HTTP_PROXY) } returns "" 32 | kotlin.test.assertFalse(getProxyStatusInteractor.isProxyEnabled()) 33 | verify { globalSettingsRepository.getString(Settings.Global.HTTP_PROXY) } 34 | 35 | every { globalSettingsRepository.getString(Settings.Global.HTTP_PROXY) } returns GlobalSettingsRepository.DISABLE_PROXY_VALUE 36 | kotlin.test.assertFalse(getProxyStatusInteractor.isProxyEnabled()) 37 | verify { globalSettingsRepository.getString(Settings.Global.HTTP_PROXY) } 38 | 39 | every { globalSettingsRepository.getString(Settings.Global.HTTP_PROXY) } returns "localhost:8080" 40 | kotlin.test.assertTrue(getProxyStatusInteractor.isProxyEnabled()) 41 | verify { globalSettingsRepository.getString(Settings.Global.HTTP_PROXY) } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /domain/usecase/src/test/java/com/nagopy/android/debugassistant/domain/usecase/interactor/GetUserProxyInfoInteractorTest.kt: -------------------------------------------------------------------------------- 1 | package com.nagopy.android.debugassistant.domain.usecase.interactor 2 | 3 | import com.nagopy.android.debugassistant.data.repository.UserPreferencesRepository 4 | import io.mockk.every 5 | import io.mockk.mockk 6 | import org.junit.Before 7 | import org.junit.Test 8 | 9 | class GetUserProxyInfoInteractorTest { 10 | private lateinit var getUserProxyInfoInteractor: GetUserProxyInfoInteractor 11 | private lateinit var userPreferencesRepository: UserPreferencesRepository 12 | 13 | @Before 14 | fun setUp() { 15 | userPreferencesRepository = mockk(relaxed = true) 16 | getUserProxyInfoInteractor = GetUserProxyInfoInteractor(userPreferencesRepository) 17 | } 18 | 19 | @Test 20 | fun getUserProxyInfo() { 21 | 22 | every { userPreferencesRepository.proxyHost } returns "host" 23 | every { userPreferencesRepository.proxyPort } returns "port" 24 | val result = getUserProxyInfoInteractor.getUserProxyInfo() 25 | kotlin.test.assertEquals("host", result.host) 26 | kotlin.test.assertEquals("port", result.port) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /domain/usecase/src/test/java/com/nagopy/android/debugassistant/domain/usecase/interactor/PutUserProxyInfoInteractorTest.kt: -------------------------------------------------------------------------------- 1 | package com.nagopy.android.debugassistant.domain.usecase.interactor 2 | 3 | import com.nagopy.android.debugassistant.data.repository.UserPreferencesRepository 4 | import com.nagopy.android.debugassistant.domain.model.ProxyInfo 5 | import io.mockk.mockk 6 | import io.mockk.verify 7 | import org.junit.Before 8 | import org.junit.Test 9 | 10 | class PutUserProxyInfoInteractorTest { 11 | private lateinit var putUserProxyInfoInteractor: PutUserProxyInfoInteractor 12 | private lateinit var userPreferencesRepository: UserPreferencesRepository 13 | 14 | @Before 15 | fun setUp() { 16 | userPreferencesRepository = mockk(relaxed = true) 17 | putUserProxyInfoInteractor = PutUserProxyInfoInteractor(userPreferencesRepository) 18 | } 19 | 20 | @Test 21 | fun getUserProxyInfo() { 22 | val proxyInfo = ProxyInfo("host", "port") 23 | putUserProxyInfoInteractor.putUserProxyInfo(proxyInfo) 24 | verify { 25 | userPreferencesRepository.proxyHost = "host" 26 | userPreferencesRepository.proxyPort = "port" 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /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 | # Kotlin code style for this project: "official" or "obsolete": 19 | kotlin.code.style=official 20 | # Enables namespacing of each library's R class so that its R class includes only the 21 | # resources declared in the library itself and none from the library's dependencies, 22 | # thereby reducing the size of the R class for that library 23 | android.nonTransitiveRClass=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/75py/DebugAssistant/bff9000aae07541004d1df604dbf1037db3e7b01/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat Feb 12 22:29:37 JST 2022 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.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 | -------------------------------------------------------------------------------- /images/feature_graphic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/75py/DebugAssistant/bff9000aae07541004d1df604dbf1037db3e7b01/images/feature_graphic.png -------------------------------------------------------------------------------- /images/features_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/75py/DebugAssistant/bff9000aae07541004d1df604dbf1037db3e7b01/images/features_1.png -------------------------------------------------------------------------------- /images/features_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/75py/DebugAssistant/bff9000aae07541004d1df604dbf1037db3e7b01/images/features_2.png -------------------------------------------------------------------------------- /images/install_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/75py/DebugAssistant/bff9000aae07541004d1df604dbf1037db3e7b01/images/install_1.png -------------------------------------------------------------------------------- /images/install_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/75py/DebugAssistant/bff9000aae07541004d1df604dbf1037db3e7b01/images/install_2.png -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | gradlePluginPortal() 4 | google() 5 | mavenCentral() 6 | } 7 | } 8 | dependencyResolutionManagement { 9 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 10 | repositories { 11 | google() 12 | mavenCentral() 13 | } 14 | } 15 | rootProject.name = "Debug Assistant" 16 | include ':app' 17 | include ':data:repository' 18 | include ':domain:usecase' 19 | include ':domain:model' 20 | --------------------------------------------------------------------------------