├── .codequality
└── dynamic.json
├── .github
└── workflows
│ └── android.yml
├── .gitignore
├── .idea
├── codeStyles
│ ├── Project.xml
│ └── codeStyleConfig.xml
├── jarRepositories.xml
└── misc.xml
├── LICENSE
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
├── src
│ ├── androidTest
│ │ └── java
│ │ │ └── com
│ │ │ └── hellobike
│ │ │ └── sample
│ │ │ └── ExampleInstrumentedTest.kt
│ ├── debug
│ │ └── assets
│ │ │ └── dynamic.json
│ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── kronos
│ │ │ │ └── sample
│ │ │ │ ├── A.java
│ │ │ │ ├── Adapter.kt
│ │ │ │ ├── App.kt
│ │ │ │ ├── CustomView.kt
│ │ │ │ ├── Event.kt
│ │ │ │ ├── FileHelper.kt
│ │ │ │ ├── FragmentTest.kt
│ │ │ │ ├── JavaFragment.java
│ │ │ │ ├── Logger.kt
│ │ │ │ ├── MainActivity.kt
│ │ │ │ └── uitls
│ │ │ │ ├── DynamicConfigEntity.kt
│ │ │ │ ├── EpicHook.kt
│ │ │ │ └── GsonUtils.kt
│ │ └── res
│ │ │ ├── drawable-v24
│ │ │ └── ic_launcher_foreground.xml
│ │ │ ├── drawable
│ │ │ └── ic_launcher_background.xml
│ │ │ ├── layout
│ │ │ └── activity_main.xml
│ │ │ ├── mipmap-anydpi-v26
│ │ │ ├── ic_launcher.xml
│ │ │ └── ic_launcher_round.xml
│ │ │ ├── mipmap-hdpi
│ │ │ ├── ic_launcher.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
│ │ │ ├── abc.gif
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ │ └── values
│ │ │ ├── colors.xml
│ │ │ ├── strings.xml
│ │ │ └── styles.xml
│ └── test
│ │ └── java
│ │ └── com
│ │ └── hellobike
│ │ └── sample
│ │ └── ExampleUnitTest.kt
└── test.gradle
├── build.gradle
├── demo1
├── .gitignore
├── build.gradle
├── consumer-rules.pro
├── dynamic.json
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── kronos
│ │ └── demo1
│ │ └── ExampleInstrumentedTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ └── java
│ │ └── com
│ │ └── kronos
│ │ └── demo1
│ │ ├── NewTestFragment.kt
│ │ └── Test.kt
│ └── test
│ └── java
│ └── com
│ └── kronos
│ └── demo1
│ └── ExampleUnitTest.kt
├── dynamic.yaml
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── lint_sample
├── .gitignore
├── build.gradle
└── src
│ └── main
│ └── java
│ └── com
│ └── kornos
│ └── lint
│ └── demo
│ ├── AlertDialogDetector.java
│ ├── ContextCastDetector.kt
│ ├── DynamicLint.kt
│ ├── EventSpaceDetector.java
│ ├── FragmentHiddenDetector.kt
│ ├── GlideUnusedDetector.java
│ ├── KAEDetector.kt
│ ├── LogDetector.java
│ ├── PngResourceDetector.java
│ ├── PreDrawDetector.kt
│ ├── PrivacyClassDetector.kt
│ ├── RouteDetector.java
│ ├── SafeFileDetector.kt
│ ├── TestGradleDetector.kt
│ ├── TestIssueRegistry.kt
│ ├── ThreadDetector.java
│ ├── entity
│ ├── DynamicConfigEntity.kt
│ ├── GsonUtils.kt
│ ├── LintEx.kt
│ └── LintMatcher.kt
│ └── privacy
│ ├── PrivacyAsmEntity.kt
│ └── PrivacyHelper.kt
├── settings.gradle
└── subSample
└── demo2
├── .gitignore
├── build.gradle
├── consumer-rules.pro
├── proguard-rules.pro
└── src
├── androidTest
└── java
│ └── com
│ └── kronos
│ └── demo2
│ └── ExampleInstrumentedTest.kt
├── main
└── AndroidManifest.xml
└── test
└── java
└── com
└── kronos
└── demo2
└── ExampleUnitTest.kt
/.codequality/dynamic.json:
--------------------------------------------------------------------------------
1 | {
2 | "methods": [
3 | {
4 | "name_regex": "java.io.File",
5 | "message": "gie gie 别删除了好吗 覆盖",
6 | "excludes": [
7 |
8 | ]
9 | }
10 | ],
11 | "constructions": [
12 | {
13 | "name_regex": "java.util.zip.ZipEntry",
14 | "message": "西内 2333333"
15 | }
16 | ]
17 | }
--------------------------------------------------------------------------------
/.github/workflows/android.yml:
--------------------------------------------------------------------------------
1 | name: Android CI
2 |
3 | on:
4 | push:
5 | branches: [ master ]
6 | pull_request:
7 | branches: [ master ]
8 |
9 | jobs:
10 | build:
11 |
12 | runs-on: ubuntu-latest
13 |
14 | steps:
15 | - uses: actions/checkout@v2
16 | - name: set up JDK 11
17 | uses: actions/setup-java@v2
18 | with:
19 | java-version: '11'
20 | distribution: 'adopt'
21 |
22 | - name: Grant execute permission for gradlew
23 | run: chmod +x gradlew
24 | - name: Build with Gradle
25 | run: ./gradlew lint
26 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | *.apk
3 | *.aar
4 | *.ap_
5 | *.aab
6 |
7 |
8 | # Files for the ART/Dalvik VM
9 | *.dex
10 |
11 | # Java class files
12 | *.class
13 |
14 | # Generated files
15 | bin/
16 | gen/
17 | out/
18 | # Uncomment the following line in case you need and you don't have the release build type files in your app
19 | # release/
20 |
21 | # Gradle files
22 | .gradle/
23 | build/
24 | .idea/
25 |
26 | # Local configuration file (sdk path, etc)
27 | local.properties
28 |
29 | # Proguard folder generated by Eclipse
30 | proguard/
31 |
32 | # Log Files
33 | *.log
34 |
35 | # Android Studio Navigation editor temp files
36 | .navigation/
37 |
38 | # Android Studio captures folder
39 | captures/
40 |
41 | # IntelliJ
42 | *.iml
43 | .idea/workspace.xml
44 | .idea/tasks.xml
45 | .idea/gradle.xml
46 | .idea/assetWizardSettings.xml
47 | .idea/dictionaries
48 | .idea/libraries
49 | # Android Studio 3 in .gitignore file.
50 | .idea/caches
51 | .idea/modules.xml
52 | # Comment next line if keeping position of elements in Navigation Editor is relevant for you
53 | .idea/navEditor.xml
54 |
55 | # Keystore files
56 | # Uncomment the following lines if you do not want to check your keystore files in.
57 | #*.jks
58 | #*.keystore
59 |
60 | # External native build folder generated in Android Studio 2.2 and later
61 | .externalNativeBuild
62 | .cxx/
63 |
64 | # Google Services (e.g. APIs or Firebase)
65 | # google-services.json
66 |
67 | # Freeline
68 | freeline.py
69 | freeline/
70 | freeline_project_description.json
71 |
72 | # fastlane
73 | fastlane/report.xml
74 | fastlane/Preview.html
75 | fastlane/screenshots
76 | fastlane/test_output
77 | fastlane/readme.md
78 |
79 | # Version control
80 | vcs.xml
81 |
82 | # lint
83 | lint/intermediates/
84 | lint/generated/
85 | lint/outputs/
86 | lint/tmp/
87 | # lint/reports/
88 |
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | xmlns:android
17 |
18 | ^$
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | xmlns:.*
28 |
29 | ^$
30 |
31 |
32 | BY_NAME
33 |
34 |
35 |
36 |
37 |
38 |
39 | .*:id
40 |
41 | http://schemas.android.com/apk/res/android
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | .*:name
51 |
52 | http://schemas.android.com/apk/res/android
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 | name
62 |
63 | ^$
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 | style
73 |
74 | ^$
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 | .*
84 |
85 | ^$
86 |
87 |
88 | BY_NAME
89 |
90 |
91 |
92 |
93 |
94 |
95 | .*
96 |
97 | http://schemas.android.com/apk/res/android
98 |
99 |
100 | ANDROID_ATTRIBUTE_ORDER
101 |
102 |
103 |
104 |
105 |
106 |
107 | .*
108 |
109 | .*
110 |
111 |
112 | BY_NAME
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/jarRepositories.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/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 | # AndroidLint
2 |
3 | Android 自定义lint教学
4 |
5 | 主要功能罗列下:
6 |
7 | 1. EventSpaceDetector Event 传入参数是否含有空格扫描
8 | 2. GlideDetector 不允许直接使用glide 以及直接用BitmapFactory
9 | 3. LogDetector 不允许直接使用Log
10 | 4. PngResourceDetector 扫描Png 大图检查 扫描的res文件
11 | 5. RouteDetector 不允许项目内直接使用路由
12 | 6. ThreadDetector 这个是别人写的 线程构造的检查
13 |
14 |
15 | ## 支持动态拔插的能力
16 |
17 | 通过SPI机制,可以允许自定义lint规则,然后动态化合并到Lintcheck中,增加lint的可拓展性。
18 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | apply plugin: 'kotlin-android'
3 | apply from: "./test.gradle"
4 |
5 | android {
6 | compileSdkVersion 30
7 |
8 | defaultConfig {
9 | applicationId "com.kronos.sample"
10 | minSdkVersion 21
11 | targetSdkVersion 30
12 | versionCode 1
13 | versionName "1.0"
14 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
15 | }
16 | buildTypes {
17 | release {
18 | minifyEnabled false
19 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
20 | }
21 | }
22 |
23 |
24 | }
25 |
26 | dependencies {
27 | implementation fileTree(dir: 'libs', include: ['*.jar'])
28 | implementation 'com.google.code.gson:gson:2.8.7'
29 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
30 | implementation 'androidx.appcompat:appcompat:1.3.0'
31 | implementation 'androidx.core:core-ktx:1.5.0'
32 | implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
33 | testImplementation 'junit:junit:4.13.2'
34 | androidTestImplementation 'androidx.test:runner:1.3.0'
35 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
36 | //implementation 'com.sankuai.waimai.router:router:1.2.0'
37 | implementation 'com.github.bumptech.glide:glide:4.12.0'
38 | implementation "io.reactivex.rxjava2:rxjava:2.2.18"
39 | implementation 'androidx.recyclerview:recyclerview:1.2.0'
40 | implementation project(":demo1")
41 | implementation project(":subSample:demo2")
42 | lintChecks project(":lint_sample")
43 | }
44 |
45 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/hellobike/sample/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.hellobike.sample
2 |
3 | import androidx.test.InstrumentationRegistry
4 | import androidx.test.runner.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getTargetContext()
22 | assertEquals("com.hellobike.sample", appContext.packageName)
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/src/debug/assets/dynamic.json:
--------------------------------------------------------------------------------
1 | {
2 | "methods": [
3 | {
4 | "name_regex": "java.io",
5 | "message": "gie gie 别删除了好吗 覆盖",
6 | "excludes": [
7 | "com.kronos.sample.MainActivity"
8 | ]
9 | },
10 | {
11 | "name_regex": "",
12 | "message": ""
13 | }
14 | ],
15 | "constructions": [
16 | {
17 | "name_regex": "",
18 | "message": ""
19 | }
20 | ]
21 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/java/com/kronos/sample/A.java:
--------------------------------------------------------------------------------
1 | package com.kronos.sample;
2 |
3 | public interface A {
4 | }
5 |
--------------------------------------------------------------------------------
/app/src/main/java/com/kronos/sample/Adapter.kt:
--------------------------------------------------------------------------------
1 | package com.kronos.sample
2 |
3 | import android.view.View
4 | import android.widget.ImageView
5 | import androidx.appcompat.view.menu.ActionMenuItemView
6 | import androidx.recyclerview.widget.RecyclerView
7 |
8 | /**
9 | * @Author LiABao
10 | * @Since 2020/10/15
11 | */
12 | class Adapter(itemView: View) : RecyclerView.ViewHolder(itemView) {
13 | init {
14 | itemView.findViewById(R.id.tv1)
15 | }
16 |
17 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/kronos/sample/App.kt:
--------------------------------------------------------------------------------
1 | package com.kronos.sample
2 |
3 | import android.app.Application
4 | import com.kronos.sample.uitls.DynamicEntity
5 | import com.kronos.sample.uitls.EpicHook
6 |
7 | /**
8 | *
9 | * @Author LiABao
10 | * @Since 2021/8/2
11 | *
12 | */
13 | class App : Application() {
14 | override fun onCreate() {
15 | super.onCreate()
16 | EpicHook.start(DynamicEntity("java.io.File.delete", "别删除啊", null))
17 | }
18 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/kronos/sample/CustomView.kt:
--------------------------------------------------------------------------------
1 | package com.kronos.sample
2 |
3 | import android.content.Context
4 | import android.view.LayoutInflater
5 | import android.widget.FrameLayout
6 | import android.widget.ImageView
7 |
8 | /**
9 | * @Author LiABao
10 | * @Since 2020/10/15
11 | */
12 | class CustomView(context: Context) : FrameLayout(context) {
13 | init {
14 | LayoutInflater.from(context).inflate(R.layout.activity_main, this)
15 | }
16 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/kronos/sample/Event.kt:
--------------------------------------------------------------------------------
1 | package com.kronos.sample
2 |
3 | import android.util.Log
4 |
5 | data class Event(val eventId: String) {
6 |
7 | fun unknownNameFun() {
8 | Log.i("", "")
9 | }
10 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/kronos/sample/FileHelper.kt:
--------------------------------------------------------------------------------
1 | package com.kronos.sample
2 |
3 | import android.annotation.SuppressLint
4 | import androidx.annotation.WorkerThread
5 | import java.io.File
6 | import java.io.FileInputStream
7 | import java.io.FileOutputStream
8 |
9 | /**
10 | *
11 | * @Author LiABao
12 | * @Since 2021/6/23
13 | *
14 | */
15 | class FileHelper {
16 | @SuppressLint("file_path_issue")
17 | @WorkerThread
18 | fun write(fileName: String, block: (FileInputStream) -> R) = apply {
19 | val file = File("Pictures", fileName)
20 | if (file.exists()) {
21 |
22 | }
23 | val fileInputStream = FileInputStream(file)
24 | fileInputStream.use { block.invoke(it) }
25 | }
26 |
27 | @SuppressLint("file_path_issue")
28 | @WorkerThread
29 | fun writeOverWrite(fileName: String, block: (FileInputStream) -> R) = apply {
30 | val file = File("Pictures", fileName)
31 | if (file.exists()) {
32 | // return
33 | }
34 | val fileInputStream = FileInputStream(file)
35 | fileInputStream.use { block.invoke(it) }
36 | }
37 |
38 | @SuppressLint("file_path_issue")
39 | @WorkerThread
40 | fun read(fileName: String, block: (FileOutputStream) -> R) = apply {
41 | val file = File("Pictures", fileName)
42 | val fileInputStream = FileOutputStream(file)
43 | fileInputStream.use { block.invoke(it) }
44 | }
45 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/kronos/sample/FragmentTest.kt:
--------------------------------------------------------------------------------
1 | package com.kronos.sample
2 |
3 | import android.os.Bundle
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import android.widget.ImageView
8 | import androidx.fragment.app.Fragment
9 | import com.kronos.demo1.NewTestFragment
10 |
11 | /**
12 | * @Author LiABao
13 | * @Since 2020/10/15
14 | */
15 | class FragmentTest : NewTestFragment() {
16 | override fun onCreateView(
17 | inflater: LayoutInflater,
18 | container: ViewGroup?,
19 | savedInstanceState: Bundle?
20 | ): View? {
21 | return inflater.inflate(R.layout.activity_main, container, false)
22 | }
23 |
24 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
25 | super.onViewCreated(view, savedInstanceState)
26 | val image = view.findViewById(R.id.tv1)
27 | }
28 |
29 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/kronos/sample/JavaFragment.java:
--------------------------------------------------------------------------------
1 | package com.kronos.sample;
2 |
3 | import androidx.fragment.app.Fragment;
4 |
5 | /**
6 | * @Author LiABao
7 | * @Since 2022/11/9
8 | */
9 | public class JavaFragment extends Fragment {
10 |
11 | @Override
12 | public void onHiddenChanged(boolean hidden) {
13 | super.onHiddenChanged(hidden);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/app/src/main/java/com/kronos/sample/Logger.kt:
--------------------------------------------------------------------------------
1 | package com.kronos.sample
2 |
3 | import android.annotation.SuppressLint
4 | import android.util.Log
5 |
6 | object Logger {
7 | fun i(logInfo: String) {
8 | Log.i("Logger", logInfo)
9 | }
10 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/kronos/sample/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.kronos.sample
2 |
3 | import android.annotation.SuppressLint
4 | import android.graphics.BitmapFactory
5 | import android.os.Build
6 | import android.os.Bundle
7 | import android.os.Environment
8 | import android.provider.Settings
9 | import android.telephony.TelephonyManager
10 | import android.util.Log
11 | import android.view.ViewTreeObserver
12 | import android.widget.ImageView
13 | import android.widget.TextView
14 | import androidx.annotation.NonNull
15 | import androidx.appcompat.app.AlertDialog
16 | import androidx.appcompat.app.AppCompatActivity
17 | import com.bumptech.glide.Glide
18 | import io.reactivex.Observable
19 | import java.io.File
20 | import java.io.InputStream
21 | import java.io.OutputStream
22 | import java.util.zip.ZipEntry
23 |
24 |
25 | class MainActivity : AppCompatActivity() {
26 | @NonNull
27 | override fun onCreate(savedInstanceState: Bundle?) {
28 | super.onCreate(savedInstanceState)
29 | setContentView(R.layout.activity_main)
30 | // Router.startPageUri(this, "")
31 | // Router.getService(A::class.java, "")
32 | Thread {
33 |
34 | }
35 | AlertDialog.Builder(this).show()
36 | // DefaultUriRequest(this, "").start()
37 | Log.i("", "")
38 | Glide.with(this)
39 | val image = BitmapFactory.decodeFile("")
40 | Observable.just("").subscribe()
41 | val text = "我想说"
42 | findViewById(R.id.tv1).text = String.format("%s", text)
43 | Event(" 123")
44 | Event("111")
45 | val path = Environment.getExternalStorageDirectory()
46 | val otherPath = Environment.getDownloadCacheDirectory()
47 | val test = Environment.DIRECTORY_PICTURES
48 | //File(Environment.getExternalStorageDirectory(), Environment.DIRECTORY_ALARMS)
49 | val value = "Pictures"
50 | File(Environment.DIRECTORY_PICTURES, "").delete()
51 | if (this is AppCompatActivity) {
52 |
53 | }
54 | findViewById(R.id.tv1).viewTreeObserver.addOnPreDrawListener(object : ViewTreeObserver.OnPreDrawListener {
55 | override fun onPreDraw(): Boolean {
56 | return true
57 | }
58 | })
59 | val entry = ZipEntry("zip")
60 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
61 | val manager = getSystemService(TelephonyManager::class.java)
62 | val did = manager.deviceId
63 | val androidId = Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID)
64 | // String newDid =PrivacyUtils.getString(getContentResolver(), Settings.Secure.ANDROID_ID);
65 | }
66 | }
67 |
68 | }
69 |
--------------------------------------------------------------------------------
/app/src/main/java/com/kronos/sample/uitls/DynamicConfigEntity.kt:
--------------------------------------------------------------------------------
1 | package com.kronos.sample.uitls
2 |
3 | /**
4 | * @Author LiABao
5 | * @Since 2021/7/20
6 | */
7 | class DynamicConfigEntity {
8 | val methods = mutableListOf()
9 |
10 | val constructions = mutableListOf()
11 | }
12 |
13 | data class DynamicEntity(
14 | val name_regex: String,
15 | val message: String,
16 | val excludes: MutableList?
17 | )
18 |
19 |
--------------------------------------------------------------------------------
/app/src/main/java/com/kronos/sample/uitls/EpicHook.kt:
--------------------------------------------------------------------------------
1 | package com.kronos.sample.uitls
2 |
3 | import android.content.Context
4 |
5 | /**
6 | *
7 | * @Author LiABao
8 | * @Since 2021/8/2
9 | *
10 | */
11 | object EpicHook {
12 |
13 | fun hookManager(context: Context) {
14 | val steam = context.resources.assets.open("dynamic.json")
15 | val configEntity = GsonUtils.inflate(steam)
16 | configEntity.methods.forEach {
17 | start(it)
18 | }
19 | }
20 |
21 |
22 | fun start(entity: DynamicEntity) {
23 | if (entity.name_regex.isNotEmpty()) {
24 | val methodName = entity.name_regex.substring(entity.name_regex.lastIndexOf(".") + 1)
25 | val className = entity.name_regex.substring(0, entity.name_regex.lastIndexOf("."))
26 | val lintClass = Class.forName(className)
27 | /*DexposedBridge.hookAllMethods(lintClass, methodName, object : XC_MethodHook() {
28 | override fun beforeHookedMethod(param: MethodHookParam?) {
29 | super.beforeHookedMethod(param)
30 |
31 | Log.i("EpicHook", "EpicHook")
32 | }
33 | })*/
34 | }
35 | }
36 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/kronos/sample/uitls/GsonUtils.kt:
--------------------------------------------------------------------------------
1 | package com.kronos.sample.uitls
2 |
3 | import com.google.gson.Gson
4 | import java.io.File
5 | import java.io.InputStream
6 |
7 | /**
8 | *
9 | * @Author LiABao
10 | * @Since 2021/4/20
11 | *
12 | */
13 | object GsonUtils {
14 |
15 | fun inflate(projectDir: InputStream?): DynamicConfigEntity {
16 | val gson = Gson()
17 | return projectDir?.let { file ->
18 | gson.fromJson(file.bufferedReader(), DynamicConfigEntity::class.java).apply {
19 | methods.forEach {
20 | it.excludes?.apply {
21 | println("excludes:$this \r\n")
22 | }
23 | }
24 | }
25 | } ?: DynamicConfigEntity()
26 | }
27 |
28 |
29 | private const val DYNAMIC_FILE_NAME = "dynamic.json"
30 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/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/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
18 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Leifzhang/AndroidLint/84a4e3cd98e0d3098a832030780d5c4576b8bf75/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Leifzhang/AndroidLint/84a4e3cd98e0d3098a832030780d5c4576b8bf75/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Leifzhang/AndroidLint/84a4e3cd98e0d3098a832030780d5c4576b8bf75/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Leifzhang/AndroidLint/84a4e3cd98e0d3098a832030780d5c4576b8bf75/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Leifzhang/AndroidLint/84a4e3cd98e0d3098a832030780d5c4576b8bf75/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Leifzhang/AndroidLint/84a4e3cd98e0d3098a832030780d5c4576b8bf75/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/abc.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Leifzhang/AndroidLint/84a4e3cd98e0d3098a832030780d5c4576b8bf75/app/src/main/res/mipmap-xxhdpi/abc.gif
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Leifzhang/AndroidLint/84a4e3cd98e0d3098a832030780d5c4576b8bf75/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Leifzhang/AndroidLint/84a4e3cd98e0d3098a832030780d5c4576b8bf75/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Leifzhang/AndroidLint/84a4e3cd98e0d3098a832030780d5c4576b8bf75/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Leifzhang/AndroidLint/84a4e3cd98e0d3098a832030780d5c4576b8bf75/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #008577
4 | #00574B
5 | #D81B60
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | sample
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/test/java/com/hellobike/sample/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.hellobike.sample
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/app/test.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: "kotlin-android-extensions"
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext.kotlin_version = '1.7.10'
3 | repositories {
4 | maven {
5 | url 'https://maven.aliyun.com/repository/central/'
6 | }
7 | google()
8 | }
9 |
10 | dependencies {
11 | classpath 'com.android.tools.build:gradle:7.2.2'
12 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
13 | // classpath("com.sankuai.waimai.router:plugin:1.2.0")
14 | }
15 | }
16 |
17 | allprojects {
18 | repositories {
19 | maven {
20 | url 'https://maven.aliyun.com/repository/central/'
21 | }
22 | google()
23 | }
24 |
25 | afterEvaluate {
26 | if (project.plugins.hasPlugin('com.android.application')
27 | || project.plugins.hasPlugin('com.android.library')) {
28 | def android = project.extensions.getByName('android')
29 | android.lintOptions {
30 | abortOnError false
31 | absolutePaths false
32 | checkReleaseBuilds false
33 | xmlReport true
34 | }
35 | }
36 | }
37 |
38 |
39 | }
40 |
41 | task clean(type: Delete) {
42 | delete rootProject.buildDir
43 | }
44 |
45 |
--------------------------------------------------------------------------------
/demo1/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/demo1/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.library'
3 | id 'kotlin-android'
4 | id "kotlin-android-extensions"
5 | }
6 |
7 | apply plugin :"kotlin-android-extensions"
8 |
9 | android {
10 | compileSdkVersion 30
11 | buildToolsVersion "30.0.3"
12 |
13 | defaultConfig {
14 | minSdkVersion 21
15 | targetSdkVersion 30
16 |
17 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
18 | consumerProguardFiles "consumer-rules.pro"
19 | }
20 |
21 | buildTypes {
22 | release {
23 | minifyEnabled false
24 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
25 | }
26 | }
27 | compileOptions {
28 | sourceCompatibility JavaVersion.VERSION_1_8
29 | targetCompatibility JavaVersion.VERSION_1_8
30 | }
31 | kotlinOptions {
32 | jvmTarget = '1.8'
33 | }
34 | }
35 |
36 | dependencies {
37 |
38 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
39 | implementation 'androidx.core:core-ktx:1.5.0'
40 | implementation 'androidx.appcompat:appcompat:1.3.0'
41 | implementation 'com.google.android.material:material:1.3.0'
42 | lintChecks project(":lint_sample")
43 | testImplementation 'junit:junit:4.+'
44 | androidTestImplementation 'androidx.test.ext:junit:1.1.2'
45 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
46 | }
--------------------------------------------------------------------------------
/demo1/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Leifzhang/AndroidLint/84a4e3cd98e0d3098a832030780d5c4576b8bf75/demo1/consumer-rules.pro
--------------------------------------------------------------------------------
/demo1/dynamic.json:
--------------------------------------------------------------------------------
1 | {
2 | "methods": [
3 | {
4 | "name_regex": "java.lang.System.currentTimeMillis",
5 | "message": "卧槽 66666"
6 | }
7 | ],
8 | "constructions": [
9 | {
10 | "name_regex": "",
11 | "message": ""
12 | }
13 | ]
14 | }
--------------------------------------------------------------------------------
/demo1/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
--------------------------------------------------------------------------------
/demo1/src/androidTest/java/com/kronos/demo1/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.kronos.demo1
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22 | assertEquals("com.kronos.demo1.test", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/demo1/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/demo1/src/main/java/com/kronos/demo1/NewTestFragment.kt:
--------------------------------------------------------------------------------
1 | package com.kronos.demo1
2 |
3 | import androidx.fragment.app.Fragment
4 |
5 | /**
6 | *
7 | * @Author LiABao
8 | * @Since 2022/11/9
9 | *
10 | */
11 | open class NewTestFragment : Fragment() {
12 |
13 | override fun onHiddenChanged(hidden: Boolean) {
14 | super.onHiddenChanged(hidden)
15 | }
16 | }
--------------------------------------------------------------------------------
/demo1/src/main/java/com/kronos/demo1/Test.kt:
--------------------------------------------------------------------------------
1 | package com.kronos.demo1
2 |
3 | /**
4 | * @Author LiABao
5 | * @Since 2021/7/26
6 | */
7 | class Test {
8 | fun log() {
9 | System.currentTimeMillis()
10 | }
11 | }
--------------------------------------------------------------------------------
/demo1/src/test/java/com/kronos/demo1/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.kronos.demo1
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
--------------------------------------------------------------------------------
/dynamic.yaml:
--------------------------------------------------------------------------------
1 | methods:
2 | - name_regex: master
3 | message: https://github.com/Leifzhang/QRScaner.git
4 | - name_regex: master
5 | message: https://github.com/Leifzhang/QRScaner.git
6 | constructions:
7 | - name_regex: 123
8 | message: https://github.com/Leifzhang/QRScaner.git
--------------------------------------------------------------------------------
/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=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
20 | # Kotlin code style for this project: "official" or "obsolete":
21 | kotlin.code.style=official
22 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Leifzhang/AndroidLint/84a4e3cd98e0d3098a832030780d5c4576b8bf75/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Tue Sep 13 11:29:46 CST 2022
2 | distributionBase=GRADLE_USER_HOME
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.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 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/lint_sample/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/lint_sample/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'java-library'
3 | id 'kotlin'
4 | id 'kotlin-kapt'
5 | }
6 |
7 |
8 | dependencies {
9 | implementation fileTree(dir: 'libs', include: ['*.jar'])
10 | compileOnly 'com.android.tools.lint:lint-api:30.3.0'
11 | compileOnly 'com.android.tools.lint:lint-checks:30.3.0'
12 | implementation 'com.google.code.gson:gson:2.10'
13 | }
14 |
15 | sourceCompatibility = "1.8"
16 | targetCompatibility = "1.8"
17 |
18 | jar {
19 | manifest {
20 | attributes("Lint-Registry-v2": "com.kornos.lint.demo.TestIssueRegistry")
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/lint_sample/src/main/java/com/kornos/lint/demo/AlertDialogDetector.java:
--------------------------------------------------------------------------------
1 | package com.kornos.lint.demo;
2 |
3 | import com.android.tools.lint.client.api.UElementHandler;
4 | import com.android.tools.lint.detector.api.Category;
5 | import com.android.tools.lint.detector.api.Detector;
6 | import com.android.tools.lint.detector.api.Implementation;
7 | import com.android.tools.lint.detector.api.Issue;
8 | import com.android.tools.lint.detector.api.JavaContext;
9 | import com.android.tools.lint.detector.api.Scope;
10 | import com.android.tools.lint.detector.api.Severity;
11 |
12 | import org.jetbrains.annotations.NotNull;
13 | import org.jetbrains.uast.UCallExpression;
14 | import org.jetbrains.uast.UElement;
15 | import org.jetbrains.uast.UReferenceExpression;
16 | import org.jetbrains.uast.UastUtils;
17 | import org.jetbrains.uast.util.UastExpressionUtils;
18 |
19 | import java.util.ArrayList;
20 | import java.util.List;
21 |
22 | /**
23 | * @Author LiABao
24 | * @Since 2020/10/12
25 | */
26 | public class AlertDialogDetector extends Detector implements Detector.UastScanner {
27 |
28 | private final String ANDROIDX_PACKAGE = "androidx.appcompat.app";
29 | private final String ALERT_DIALOG_BUILDER = ANDROIDX_PACKAGE + ".AlertDialog.Builder";
30 | private final String ALERT_DIALOG = ANDROIDX_PACKAGE + ".AlertDialog";
31 |
32 | public static final Issue ISSUE = Issue.create(
33 | "alert_dialog_issue", //唯一 ID
34 | "更换AlertDialog到BiliCommonDialog", //简单描述
35 | "全局项目不允许使用该类 请更换BiliCommonDialog", //详细描述
36 | Category.CORRECTNESS, //问题种类(正确性、安全性等)
37 | 6, //权重
38 | Severity.WARNING, //问题严重程度(忽略、警告、错误)
39 | new Implementation( //实现,包括处理实例和作用域
40 | AlertDialogDetector.class,
41 | Scope.JAVA_FILE_SCOPE));
42 |
43 | @Override
44 | public List> getApplicableUastTypes() {
45 | List> types = new ArrayList<>();
46 | types.add(UCallExpression.class);
47 | return types;
48 | }
49 |
50 |
51 | @Override
52 | public UElementHandler createUastHandler(@NotNull final JavaContext context) {
53 | return new UElementHandler() {
54 |
55 | @Override
56 | public void visitCallExpression(@NotNull UCallExpression node) {
57 | // checkIsMethod(node);
58 | checkIsConstructorCall(node);
59 | }
60 |
61 | private void checkIsConstructorCall(UCallExpression node) {
62 | try {
63 | if (!UastExpressionUtils.isConstructorCall(node)) {
64 | return;
65 | }
66 | UReferenceExpression classRef = node.getClassReference();
67 | if (classRef != null) {
68 | String className = UastUtils.getQualifiedName(classRef);
69 | System.out.println(className);
70 | if (className.equals(ALERT_DIALOG_BUILDER) || className.equals(ALERT_DIALOG)) {
71 | context.report(ISSUE, node, context.getLocation(node),
72 | "请使用项目提供的BiliCommonDialog");
73 | }
74 | }
75 | } catch (Exception e) {
76 | e.printStackTrace();
77 | }
78 | }
79 |
80 | /* private void checkIsMethod(UCallExpression node) {
81 | if (UastExpressionUtils.isMethodCall(node)) {
82 | if (node.getReceiver() != null && node.getMethodName() != null) {
83 | PsiMethod method = node.resolve();
84 | if (context.getEvaluator().isMemberInClass(method, WM_ROUTER_CALL)) {
85 | context.report(CALL_ISSUE, node, context.getLocation(node),
86 | "请使用项目提供的路由中间件");
87 | }
88 | }
89 | }
90 | }
91 | */
92 | };
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/lint_sample/src/main/java/com/kornos/lint/demo/ContextCastDetector.kt:
--------------------------------------------------------------------------------
1 | package com.kornos.lint.demo
2 |
3 | import com.android.tools.lint.client.api.UElementHandler
4 | import com.android.tools.lint.detector.api.Category
5 | import com.android.tools.lint.detector.api.Detector
6 | import com.android.tools.lint.detector.api.Implementation
7 | import com.android.tools.lint.detector.api.Issue
8 | import com.android.tools.lint.detector.api.JavaContext
9 | import com.android.tools.lint.detector.api.Scope
10 | import com.android.tools.lint.detector.api.Severity
11 | import com.android.tools.lint.detector.api.SourceCodeScanner
12 | import com.android.tools.lint.detector.api.TextFormat
13 | import org.jetbrains.uast.UBinaryExpressionWithType
14 | import org.jetbrains.uast.UCallExpression
15 | import org.jetbrains.uast.UElement
16 | import org.jetbrains.uast.UastBinaryExpressionWithTypeKind
17 |
18 | class ContextCastDetector : Detector(), SourceCodeScanner {
19 |
20 | companion object {
21 | val ISSUE = Issue.create(
22 | "ContextCast",
23 | "不要对context引用进行类型检查和类型转换。",
24 | "从View、Fragment等处获取的Context对象,有时候不等于其所处的Activity对象,而是一个包裹了Activity的ContextWrapper。" +
25 | "这时,直接对Context引用进行类型转换往往会产生不正确的结果。" +
26 | "使用Context.findActivityOrNull()或Context.requireActivity()来获取Context中对应的Activity对象。",
27 | Category.CORRECTNESS,
28 | 7,
29 | Severity.WARNING,
30 | Implementation(ContextCastDetector::class.java, Scope.JAVA_FILE_SCOPE)
31 | )
32 | }
33 |
34 |
35 | override fun getApplicableUastTypes(): List>? {
36 | return listOf(UBinaryExpressionWithType::class.java)
37 | }
38 |
39 | override fun createUastHandler(context: JavaContext): UElementHandler? {
40 | return object : UElementHandler() {
41 | override fun visitBinaryExpressionWithType(node: UBinaryExpressionWithType) {
42 | if (node.operand.getExpressionType()?.canonicalText == "android.content.Context" &&
43 | (node.operationKind is UastBinaryExpressionWithTypeKind.TypeCast || node.operationKind is UastBinaryExpressionWithTypeKind.InstanceCheck) &&
44 | node.type.canonicalText != "android.content.ContextWrapper" &&
45 | node.type.canonicalText != "android.app.Application"
46 | ) {
47 | val location = context.getLocation(node)
48 | context.report(ISSUE, location, ISSUE.getBriefDescription(TextFormat.TEXT))
49 | }
50 | }
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/lint_sample/src/main/java/com/kornos/lint/demo/DynamicLint.kt:
--------------------------------------------------------------------------------
1 | package com.kornos.lint.demo
2 |
3 | import com.android.tools.lint.client.api.UElementHandler
4 | import com.android.tools.lint.detector.api.*
5 | import com.kornos.lint.demo.entity.DynamicConfigEntity
6 | import com.kornos.lint.demo.entity.GsonUtils
7 | import com.kornos.lint.demo.entity.LintMatcher
8 | import org.jetbrains.uast.UCallExpression
9 | import org.jetbrains.uast.UClass
10 | import org.jetbrains.uast.UElement
11 | import org.jetbrains.uast.util.isConstructorCall
12 | import org.jetbrains.uast.util.isMethodCall
13 | import com.kornos.lint.demo.entity.report
14 |
15 | /**
16 | *
17 | * @Author LiABao
18 | * @Since 2021/7/20
19 | *
20 | */
21 | class DynamicLint : Detector(), Detector.UastScanner {
22 |
23 | lateinit var globalConfig: DynamicConfigEntity
24 |
25 |
26 | override fun beforeCheckRootProject(context: Context) {
27 | super.beforeCheckRootProject(context)
28 | globalConfig = GsonUtils.inflate(context.project.dir)
29 | }
30 |
31 | override fun getApplicableUastTypes(): List>? {
32 | return listOf(UCallExpression::class.java, UClass::class.java)
33 | }
34 |
35 | override fun createUastHandler(context: JavaContext): UElementHandler? {
36 | return object : UElementHandler() {
37 |
38 | override fun visitCallExpression(node: UCallExpression) {
39 | if (node.isMethodCall()) {
40 | checkMethodCall(context, node)
41 | } else if (node.isConstructorCall()) {
42 | checkConstructorCall(context, node)
43 | }
44 | }
45 |
46 | override fun visitClass(node: UClass) {
47 | checkInheritClass(context, node)
48 | }
49 | }
50 | }
51 |
52 | private fun checkMethodCall(context: JavaContext, node: UCallExpression) {
53 | globalConfig.methods.forEach {
54 | if (LintMatcher.matchMethod(it, node)) {
55 | context.report(ISSUE, context.getLocation(node), it)
56 | return
57 | }
58 | }
59 | }
60 |
61 | private fun checkConstructorCall(context: JavaContext, node: UCallExpression) {
62 | globalConfig.constructions.forEach {
63 | if (LintMatcher.matchConstruction(it, node)) {
64 | context.report(ISSUE, context.getLocation(node), it)
65 | return
66 | }
67 | }
68 |
69 | }
70 |
71 | private fun checkInheritClass(context: JavaContext, node: UClass) {
72 | /* globalConfig.inherit.forEach { avoidInheritClass ->
73 | if (LintMatcher.matchInheritClass(
74 | avoidInheritClass,
75 | node
76 | )
77 | ) {
78 | context.report(
79 | ISSUE,
80 | context.getLocation(node as UElement),
81 | avoidInheritClass
82 | )
83 | return
84 | }
85 | }*/
86 | }
87 |
88 | companion object {
89 | val ISSUE = Issue.create(
90 | "file_path_issue", //唯一 ID
91 | "该路径是保护路径,当前不允许直接使用", //简单描述
92 | "当前项目已经集成了图片中间件,所有图片操作全部移动到中间件操作", //详细描述
93 | Category.CORRECTNESS, //问题种类(正确性、安全性等)
94 | 6, Severity.ERROR, //问题严重程度(忽略、警告、错误)
95 | Implementation( //实现,包括处理实例和作用域
96 | DynamicLint::class.java,
97 | Scope.JAVA_FILE_SCOPE
98 | )
99 | )
100 | }
101 |
102 | }
--------------------------------------------------------------------------------
/lint_sample/src/main/java/com/kornos/lint/demo/EventSpaceDetector.java:
--------------------------------------------------------------------------------
1 | package com.kornos.lint.demo;
2 |
3 | import com.android.tools.lint.client.api.UElementHandler;
4 | import com.android.tools.lint.detector.api.Category;
5 | import com.android.tools.lint.detector.api.Detector;
6 | import com.android.tools.lint.detector.api.Implementation;
7 | import com.android.tools.lint.detector.api.Issue;
8 | import com.android.tools.lint.detector.api.JavaContext;
9 | import com.android.tools.lint.detector.api.Scope;
10 | import com.android.tools.lint.detector.api.Severity;
11 |
12 | import org.jetbrains.annotations.NotNull;
13 | import org.jetbrains.uast.UCallExpression;
14 | import org.jetbrains.uast.UElement;
15 | import org.jetbrains.uast.UExpression;
16 | import org.jetbrains.uast.UField;
17 | import org.jetbrains.uast.ULiteralExpression;
18 | import org.jetbrains.uast.UReferenceExpression;
19 | import org.jetbrains.uast.UastUtils;
20 | import org.jetbrains.uast.util.UastExpressionUtils;
21 |
22 | import java.util.ArrayList;
23 | import java.util.List;
24 |
25 | public class EventSpaceDetector extends Detector implements Detector.UastScanner {
26 |
27 | static final Issue ISSUE = Issue.create(
28 | "event_space_issue", //唯一 ID
29 | "埋点不允许出现空格", //简单描述
30 | "你不知道有时候卵用空格会出问题的吗", //详细描述
31 | Category.CORRECTNESS, //问题种类(正确性、安全性等)
32 | 6, //权重
33 | Severity.WARNING, //问题严重程度(忽略、警告、错误)
34 | new Implementation( //实现,包括处理实例和作用域
35 | EventSpaceDetector.class,
36 | Scope.JAVA_FILE_SCOPE));
37 | private final String packageName = "com.kronos.sample";
38 |
39 |
40 | @Override
41 | public List> getApplicableUastTypes() {
42 | List> types = new ArrayList<>();
43 | types.add(UCallExpression.class);
44 | return types;
45 | }
46 |
47 | @Override
48 | public UElementHandler createUastHandler(@NotNull JavaContext context) {
49 | return new UElementHandler() {
50 |
51 | @Override
52 | public void visitCallExpression(@NotNull UCallExpression node) {
53 | checkIsConstructorCall(node);
54 | }
55 |
56 |
57 | private void checkIsConstructorCall(UCallExpression node) {
58 | if (!UastExpressionUtils.isConstructorCall(node)) {
59 | return;
60 | }
61 | UReferenceExpression classRef = node.getClassReference();
62 | try {
63 | if (classRef != null) {
64 | String className = UastUtils.getQualifiedName(classRef);
65 | String value = packageName + ".Event";
66 | List args = node.getValueArguments();
67 | for (UExpression element : args) {
68 | if (element instanceof ULiteralExpression) {
69 | Object stringValue = ((ULiteralExpression) element).getValue();
70 | if (stringValue instanceof String && stringValue.toString().contains(" ")) {
71 | if (value != null && className.equals(value)) {
72 | context.report(ISSUE, node, context.getLocation(node),
73 | "谁给你的胆子用空格的");
74 | }
75 | }
76 | }
77 | element.getExpressionType();
78 | }
79 | }
80 | } catch (Exception e) {
81 | e.printStackTrace();
82 | }
83 | }
84 | };
85 | }
86 |
87 | }
88 |
89 |
--------------------------------------------------------------------------------
/lint_sample/src/main/java/com/kornos/lint/demo/FragmentHiddenDetector.kt:
--------------------------------------------------------------------------------
1 | package com.kornos.lint.demo
2 |
3 | import com.android.tools.lint.detector.api.*
4 | import com.intellij.psi.JavaElementVisitor
5 | import com.intellij.psi.PsiMethod
6 | import com.intellij.psi.PsiMethodCallExpression
7 | import org.jetbrains.uast.UCallExpression
8 | import org.jetbrains.uast.UClass
9 | import org.jetbrains.uast.UElement
10 |
11 | /**
12 | *
13 | * @Author LiABao
14 | * @Since 2022/11/9
15 | *
16 | */
17 | class FragmentHiddenDetector : Detector(), Detector.UastScanner {
18 |
19 | override fun applicableSuperClasses(): List {
20 | return mutableListOf().apply {
21 | add("androidx.fragment.app.Fragment")
22 | }
23 | }
24 |
25 | override fun visitClass(context: JavaContext, declaration: UClass) {
26 | super.visitClass(context, declaration)
27 | declaration.methods.forEach {
28 | if (it.name == "onHiddenChanged" && it.uastParameters.size == 1) {
29 | context.report(
30 | ISSUE, context.getLocation(it),
31 | "凉凉了啊"
32 | )
33 | }
34 | }
35 | }
36 |
37 |
38 | companion object {
39 | val ISSUE = Issue.create(
40 | "FragmentHidden",
41 | "我不想让你们用呢",
42 | "从View、Fragment等处获取的Context对象,有时候不等于其所处的Activity对象,而是一个包裹了Activity的ContextWrapper。" +
43 | "这时,直接对Context引用进行类型转换往往会产生不正确的结果。" +
44 | "使用Context.findActivityOrNull()或Context.requireActivity()来获取Context中对应的Activity对象。",
45 | Category.CORRECTNESS,
46 | 7,
47 | Severity.ERROR,
48 | Implementation(FragmentHiddenDetector::class.java, Scope.JAVA_FILE_SCOPE)
49 | )
50 | }
51 | }
--------------------------------------------------------------------------------
/lint_sample/src/main/java/com/kornos/lint/demo/GlideUnusedDetector.java:
--------------------------------------------------------------------------------
1 | package com.kornos.lint.demo;
2 |
3 | import com.android.tools.lint.client.api.UElementHandler;
4 | import com.android.tools.lint.detector.api.Category;
5 | import com.android.tools.lint.detector.api.Detector;
6 | import com.android.tools.lint.detector.api.Implementation;
7 | import com.android.tools.lint.detector.api.Issue;
8 | import com.android.tools.lint.detector.api.JavaContext;
9 | import com.android.tools.lint.detector.api.Scope;
10 | import com.android.tools.lint.detector.api.Severity;
11 | import com.intellij.psi.PsiMethod;
12 |
13 | import org.jetbrains.annotations.NotNull;
14 | import org.jetbrains.uast.UCallExpression;
15 | import org.jetbrains.uast.UElement;
16 | import org.jetbrains.uast.util.UastExpressionUtils;
17 |
18 | import java.util.ArrayList;
19 | import java.util.List;
20 |
21 | public class GlideUnusedDetector extends Detector implements Detector.UastScanner {
22 |
23 | static final Issue ISSUE = Issue.create(
24 | "glide_code_issue", //唯一 ID
25 | "不允许直接使用glide", //简单描述
26 | "当前项目已经集成了图片中间件,所有图片操作全部移动到中间件操作", //详细描述
27 | Category.CORRECTNESS, //问题种类(正确性、安全性等)
28 | 6, //权重
29 | Severity.ERROR, //问题严重程度(忽略、警告、错误)
30 | new Implementation( //实现,包括处理实例和作用域
31 | GlideUnusedDetector.class,
32 | Scope.JAVA_FILE_SCOPE));
33 |
34 | @Override
35 | public List> getApplicableUastTypes() {
36 | List> types = new ArrayList<>();
37 | types.add(UCallExpression.class);
38 | return types;
39 | }
40 |
41 |
42 |
43 | @Override
44 | public UElementHandler createUastHandler(@NotNull JavaContext context) {
45 | return new UElementHandler() {
46 |
47 | @Override
48 | public void visitCallExpression(@NotNull UCallExpression node) {
49 | checkIsConstructorCall(node);
50 | }
51 |
52 | private void checkIsConstructorCall(UCallExpression node) {
53 | if (!UastExpressionUtils.isMethodCall(node)) {
54 | return;
55 | }
56 | try {
57 | if (node.getReceiver() != null && node.getMethodName() != null) {
58 | String methodName = node.getMethodName();
59 | if (methodName.equals("with")) {
60 | PsiMethod method = node.resolve();
61 | String value = "com.bumptech.glide.Glide";
62 | if (context.getEvaluator().isMemberInClass(method, value)) {
63 | context.report(ISSUE, node, context.getLocation(node),
64 | "请使用项目提供的路由中间件");
65 | }
66 | }
67 | if (methodName.equals("decodeFile") || methodName.equals("decodeResourceStream")
68 | || methodName.equals("decodeResource")) {
69 | PsiMethod method = node.resolve();
70 | String value = "android.graphics.BitmapFactory";
71 | if (context.getEvaluator().isMemberInClass(method, value)) {
72 | context.report(ISSUE, node, context.getLocation(node),
73 | "请使用项目提供的路由中间件");
74 | }
75 | }
76 | }
77 | } catch (Exception e) {
78 | e.printStackTrace();
79 | }
80 | }
81 |
82 | };
83 | }
84 |
85 | }
86 |
--------------------------------------------------------------------------------
/lint_sample/src/main/java/com/kornos/lint/demo/KAEDetector.kt:
--------------------------------------------------------------------------------
1 | package com.kornos.lint.demo
2 |
3 | import com.android.tools.lint.detector.api.*
4 |
5 | /**
6 | *
7 | * @Author LiABao
8 | * @Since 2022/11/10
9 | *
10 | */
11 | class KAEDetector : Detector(), Detector.GradleScanner {
12 |
13 | override fun checkMethodCall(
14 | context: GradleContext,
15 | statement: String,
16 | parent: String?,
17 | namedArguments: Map,
18 | unnamedArguments: List,
19 | cookie: Any
20 | ) {
21 | val plugin = namedArguments["plugin"]
22 | if (statement == "apply" && parent == null) {
23 | if (plugin == "kotlin-android-extensions") {
24 | report(context, cookie, ISSUE, "boom")
25 | return
26 | }
27 | }
28 | if (parent == "plugins") {
29 | if (statement == "id") {
30 | unnamedArguments.forEach {
31 | if (it.contains("kotlin-android-extensions")) {
32 | report(context, cookie, ISSUE, "boom")
33 | }
34 | }
35 | }
36 | }
37 | }
38 |
39 | private fun report(
40 | context: Context,
41 | cookie: Any,
42 | issue: Issue,
43 | message: String,
44 | fix: LintFix? = null
45 | ) {
46 | // Some methods in GradleDetector are run without the PSI read lock in order
47 | // to accommodate network requests, so we grab the read lock here.
48 | context.client.runReadAction(Runnable {
49 | if (context.isEnabled(issue) && context is GradleContext) {
50 | // Suppressed?
51 | // Temporarily unconditionally checking for suppress comments in Gradle files
52 | // since Studio insists on an AndroidLint id prefix
53 | val checkComments = /*context.getClient().checkForSuppressComments() &&*/
54 | context.containsCommentSuppress()
55 | if (checkComments && context.isSuppressedWithComment(cookie, issue)) {
56 | return@Runnable
57 | }
58 | val location = context.getLocation(cookie)
59 | context.report(issue, location, message, fix)
60 | }
61 | })
62 | }
63 |
64 |
65 | companion object {
66 | @JvmField
67 | val ISSUE = Issue.create(
68 | "KAEDetector",
69 | "我不想让你们用呢",
70 | "从View、Fragment等处获取的Context对象,有时候不等于其所处的Activity对象,而是一个包裹了Activity的ContextWrapper。" + "这时,直接对Context引用进行类型转换往往会产生不正确的结果。" + "使用Context.findActivityOrNull()或Context.requireActivity()来获取Context中对应的Activity对象。",
71 | Category.CORRECTNESS,
72 | 7,
73 | Severity.ERROR,
74 | Implementation(KAEDetector::class.java, Scope.GRADLE_SCOPE)
75 | )
76 | }
77 | }
--------------------------------------------------------------------------------
/lint_sample/src/main/java/com/kornos/lint/demo/LogDetector.java:
--------------------------------------------------------------------------------
1 | package com.kornos.lint.demo;
2 |
3 | import com.android.tools.lint.client.api.UElementHandler;
4 | import com.android.tools.lint.detector.api.Category;
5 | import com.android.tools.lint.detector.api.Detector;
6 | import com.android.tools.lint.detector.api.Implementation;
7 | import com.android.tools.lint.detector.api.Issue;
8 | import com.android.tools.lint.detector.api.JavaContext;
9 | import com.android.tools.lint.detector.api.Scope;
10 | import com.android.tools.lint.detector.api.Severity;
11 | import com.intellij.psi.PsiMethod;
12 |
13 | import org.jetbrains.annotations.NotNull;
14 | import org.jetbrains.annotations.Nullable;
15 | import org.jetbrains.uast.UCallExpression;
16 | import org.jetbrains.uast.UElement;
17 | import org.jetbrains.uast.util.UastExpressionUtils;
18 |
19 | import java.util.Collections;
20 | import java.util.List;
21 |
22 | /**
23 | * Created by Omooo
24 | * Date:2019-07-04
25 | * Desc:Avoid using Log class directly
26 | */
27 | @SuppressWarnings("UnstableApiUsage")
28 | public class LogDetector extends Detector implements Detector.UastScanner {
29 |
30 | public static final Issue ISSUE = Issue.create(
31 | "LogUsage",
32 | "Log Usage",
33 | "Please use the unified LogUtil class!",
34 | Category.CORRECTNESS,
35 | 6,
36 | Severity.ERROR,
37 | new Implementation(LogDetector.class, Scope.JAVA_FILE_SCOPE)
38 | );
39 |
40 | @Nullable
41 | @Override
42 | public List> getApplicableUastTypes() {
43 | return Collections.singletonList(UCallExpression.class);
44 | }
45 |
46 | @Nullable
47 | @Override
48 | public UElementHandler createUastHandler(@NotNull JavaContext context) {
49 | return new LogHandler(context);
50 | }
51 |
52 | class LogHandler extends UElementHandler {
53 |
54 | private final JavaContext context;
55 |
56 | LogHandler(JavaContext context) {
57 | this.context = context;
58 | }
59 |
60 | @Override
61 | public void visitCallExpression(@NotNull UCallExpression node) {
62 | if (!UastExpressionUtils.isMethodCall(node)) return;
63 | try {
64 | if (node.getReceiver() != null
65 | && node.getMethodName() != null) {
66 | String methodName = node.getMethodName();
67 | if (methodName.equals("i")
68 | || methodName.equals("d")
69 | || methodName.equals("e")
70 | || methodName.equals("v")
71 | || methodName.equals("w")
72 | || methodName.equals("wtf")) {
73 | PsiMethod method = node.resolve();
74 | if (context.getEvaluator().isMemberInClass(method, "android.util.Log")) {
75 | reportAllocation(context, node);
76 | }
77 | }
78 | }
79 | } catch (Exception e) {
80 | e.printStackTrace();
81 | }
82 | }
83 | }
84 |
85 | private void reportAllocation(JavaContext context, UCallExpression node) {
86 | context.report(ISSUE,
87 | node,
88 | context.getLocation(node),
89 | "\u21E2 请使用Logger替换Log"
90 | );
91 |
92 |
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/lint_sample/src/main/java/com/kornos/lint/demo/PngResourceDetector.java:
--------------------------------------------------------------------------------
1 | package com.kornos.lint.demo;
2 |
3 | import com.android.resources.ResourceFolderType;
4 | import com.android.tools.lint.detector.api.Category;
5 | import com.android.tools.lint.detector.api.Detector;
6 | import com.android.tools.lint.detector.api.Implementation;
7 | import com.android.tools.lint.detector.api.Issue;
8 | import com.android.tools.lint.detector.api.Location;
9 | import com.android.tools.lint.detector.api.ResourceContext;
10 | import com.android.tools.lint.detector.api.Scope;
11 | import com.android.tools.lint.detector.api.Severity;
12 |
13 | import org.jetbrains.annotations.NotNull;
14 |
15 | import java.io.File;
16 |
17 | public class PngResourceDetector extends Detector implements Detector.ResourceFolderScanner {
18 |
19 | public static final Issue ISSUE = Issue.create(
20 | "image too large",
21 | "Log Usage",
22 | "Please use the unified LogUtil class!",
23 | Category.CORRECTNESS,
24 | 6,
25 | Severity.ERROR,
26 | new Implementation(PngResourceDetector.class, Scope.RESOURCE_FOLDER_SCOPE)
27 | );
28 |
29 |
30 | @Override
31 | public boolean appliesToResourceRefs() {
32 | return super.appliesToResourceRefs();
33 | }
34 |
35 |
36 | @Override
37 | public void checkFolder(@NotNull ResourceContext context, @NotNull String folderName) {
38 | super.checkFolder(context, folderName);
39 | File parent = context.file;
40 | for (File file : parent.listFiles()) {
41 | if (file.isFile()) {
42 | long length = file.length();
43 | if (length > 10) {
44 | // System.out.print(file.toString() + "\n");
45 | context.report(ISSUE, Location.create(file),
46 | "This code mentions `lint`: **Congratulations**");
47 | }
48 | }
49 | }
50 | }
51 |
52 | @Override
53 | public boolean appliesTo(@NotNull ResourceFolderType folderType) {
54 | System.out.print(folderType.name());
55 | // return true;
56 | return folderType.compareTo(ResourceFolderType.DRAWABLE) == 0 || folderType.compareTo(ResourceFolderType.MIPMAP) == 0;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/lint_sample/src/main/java/com/kornos/lint/demo/PreDrawDetector.kt:
--------------------------------------------------------------------------------
1 | package com.kornos.lint.demo
2 |
3 | import com.android.tools.lint.detector.api.*
4 | import org.jetbrains.uast.*
5 | import java.util.ArrayList
6 |
7 | /**
8 | *
9 | * @Author LiABao
10 | * @Since 2022/1/13
11 | *
12 | */
13 | class PreDrawDetector : Detector(), SourceCodeScanner {
14 |
15 |
16 | companion object {
17 | val ISSUE = Issue.create(
18 | "PreDrawDetector",
19 | "我不想让你们用呢",
20 | "从View、Fragment等处获取的Context对象,有时候不等于其所处的Activity对象,而是一个包裹了Activity的ContextWrapper。" +
21 | "这时,直接对Context引用进行类型转换往往会产生不正确的结果。" +
22 | "使用Context.findActivityOrNull()或Context.requireActivity()来获取Context中对应的Activity对象。",
23 | Category.CORRECTNESS,
24 | 7,
25 | Severity.WARNING,
26 | Implementation(PreDrawDetector::class.java, Scope.JAVA_FILE_SCOPE)
27 | )
28 | }
29 |
30 | override fun applicableSuperClasses(): List {
31 | return mutableListOf().apply {
32 | add("android.view.ViewTreeObserver.OnPreDrawListener")
33 | }
34 | }
35 |
36 | override fun visitClass(context: JavaContext, declaration: UClass) {
37 | super.visitClass(context, declaration)
38 | context.report(
39 | ISSUE,
40 | context.getLocation(declaration as UElement),
41 | "请使用项目提供的路由中间件"
42 | )
43 |
44 | }
45 |
46 | override fun visitClass(context: JavaContext, lambda: ULambdaExpression) {
47 | super.visitClass(context, lambda)
48 | context.report(
49 | ISSUE,
50 | context.getLocation(lambda as UElement),
51 | "请使用项目提供的路由中间件"
52 | )
53 | }
54 | }
--------------------------------------------------------------------------------
/lint_sample/src/main/java/com/kornos/lint/demo/PrivacyClassDetector.kt:
--------------------------------------------------------------------------------
1 | package com.kornos.lint.demo
2 |
3 | import com.android.tools.lint.detector.api.*
4 | import com.kornos.lint.demo.privacy.PrivacyAsmEntity
5 | import com.kornos.lint.demo.privacy.PrivacyHelper
6 | import org.objectweb.asm.tree.AbstractInsnNode
7 | import org.objectweb.asm.tree.ClassNode
8 | import org.objectweb.asm.tree.MethodInsnNode
9 | import org.objectweb.asm.tree.MethodNode
10 |
11 | /**
12 | *
13 | * @Author LiABao
14 | * @Since 2021/8/4
15 | *
16 | */
17 | class PrivacyClassDetector : Detector(), Detector.ClassScanner {
18 |
19 | override fun checkClass(context: ClassContext, classNode: ClassNode) {
20 | super.checkClass(context, classNode)
21 | }
22 |
23 | override fun checkCall(
24 | context: ClassContext,
25 | classNode: ClassNode,
26 | method: MethodNode,
27 | call: MethodInsnNode
28 | ) {
29 | super.checkCall(context, classNode, method, call)
30 | }
31 |
32 | override fun getApplicableAsmNodeTypes(): IntArray? {
33 | return intArrayOf(AbstractInsnNode.METHOD_INSN)
34 | }
35 |
36 |
37 | override fun checkInstruction(
38 | context: ClassContext,
39 | classNode: ClassNode,
40 | method: MethodNode,
41 | instruction: AbstractInsnNode
42 | ) {
43 | super.checkInstruction(context, classNode, method, instruction)
44 | if (instruction is MethodInsnNode) {
45 | if (instruction.isPrivacy() != null) {
46 | print("checkInstruction AbstractInsnNode:${instruction.opcode} \r\n")
47 | context.report(
48 | ISSUE, context.getLocation(instruction),
49 | "外圈q+内圈"
50 | )
51 |
52 | }
53 | }
54 |
55 | }
56 |
57 | private fun MethodInsnNode.isPrivacy(): PrivacyAsmEntity? {
58 | val pair = PrivacyHelper.privacyList.firstOrNull {
59 | val first = it
60 | first.owner == owner && first.code == opcode && first.name == name && first.desc == desc
61 | }
62 | return pair
63 |
64 | }
65 |
66 | companion object {
67 | val ISSUE = Issue.create(
68 | "ClassSampleDetector", //唯一 ID
69 | "咦 ", //简单描述
70 | "我只是想让你报错而已", //详细描述
71 | Category.CORRECTNESS, //问题种类(正确性、安全性等)
72 | 6, Severity.ERROR, //问题严重程度(忽略、警告、错误)
73 | Implementation( //实现,包括处理实例和作用域
74 | PrivacyClassDetector::class.java,
75 | Scope.CLASS_FILE_SCOPE
76 | )
77 | )
78 | }
79 | }
--------------------------------------------------------------------------------
/lint_sample/src/main/java/com/kornos/lint/demo/RouteDetector.java:
--------------------------------------------------------------------------------
1 | package com.kornos.lint.demo;
2 |
3 | import com.android.tools.lint.client.api.UElementHandler;
4 | import com.android.tools.lint.detector.api.*;
5 | import com.intellij.psi.PsiMethod;
6 |
7 | import org.jetbrains.annotations.NotNull;
8 | import org.jetbrains.uast.*;
9 | import org.jetbrains.uast.util.UastExpressionUtils;
10 |
11 | import java.util.ArrayList;
12 | import java.util.List;
13 |
14 | public class RouteDetector extends Detector implements Detector.UastScanner {
15 | private final String WM_ROUTER_PACKAGE = "com.sankuai.waimai.router";
16 | private final String WM_ROUTER_ANNOTATION = WM_ROUTER_PACKAGE + ".annotation.RouterPage";
17 | private final String WM_ROUTER_CALL = WM_ROUTER_PACKAGE + ".Router";
18 |
19 | static final Issue ISSUE = Issue.create(
20 | "router_annotation_issue", //唯一 ID
21 | "不允许使用该注解", //简单描述
22 | "全局项目不允许使用该注解 请更换RouterUri", //详细描述
23 | Category.CORRECTNESS, //问题种类(正确性、安全性等)
24 | 6, //权重
25 | Severity.WARNING, //问题严重程度(忽略、警告、错误)
26 | new Implementation( //实现,包括处理实例和作用域
27 | RouteDetector.class,
28 | Scope.JAVA_FILE_SCOPE));
29 |
30 | static final Issue CALL_ISSUE = Issue.create("router_call_issue", //唯一 ID
31 | "不要直接引用WM router", //简单描述
32 | "使用项目封装的路由中间件完成跳转", //详细描述
33 | Category.CORRECTNESS, //问题种类(正确性、安全性等)
34 | 6, //权重
35 | Severity.WARNING, //问题严重程度(忽略、警告、错误)
36 | new Implementation( //实现,包括处理实例和作用域
37 | RouteDetector.class,
38 | Scope.JAVA_FILE_SCOPE));
39 |
40 | @Override
41 | public List> getApplicableUastTypes() {
42 | List> types = new ArrayList<>();
43 | types.add(UAnnotation.class);
44 | types.add(UCallExpression.class);
45 | return types;
46 | }
47 |
48 | @Override
49 | public UElementHandler createUastHandler(@NotNull JavaContext context) {
50 | return new UElementHandler() {
51 |
52 | @Override
53 | public void visitAnnotation(@NotNull UAnnotation node) {
54 | isAnnotation(node);
55 | }
56 |
57 | private void isAnnotation(UAnnotation node) {
58 | String type = node.getQualifiedName();
59 | if (WM_ROUTER_ANNOTATION.equals(type)) {
60 | context.report(ISSUE, node, context.getLocation(node),
61 | "该注解不允许使用");
62 | }
63 | }
64 |
65 | @Override
66 | public void visitClass(@NotNull UClass node) {
67 | super.visitClass(node);
68 | }
69 |
70 | @Override
71 | public void visitCallExpression(@NotNull UCallExpression node) {
72 | checkIsMethod(node);
73 | checkIsConstructorCall(node);
74 | }
75 |
76 | private void checkIsConstructorCall(UCallExpression node) {
77 | if (!UastExpressionUtils.isConstructorCall(node)) {
78 | return;
79 | }
80 | try {
81 | UReferenceExpression classRef = node.getClassReference();
82 | if (classRef != null) {
83 | String className = UastUtils.getQualifiedName(classRef);
84 | String uriValue = WM_ROUTER_PACKAGE + ".common.DefaultUriRequest";
85 | String pageValue = WM_ROUTER_PACKAGE + ".common.DefaultPageUriRequest";
86 |
87 | if (className.equals(uriValue) || className.equals(pageValue)) {
88 | context.report(CALL_ISSUE, node, context.getLocation(node),
89 | "请使用项目提供的路由中间件 ");
90 | }
91 | }
92 | } catch (Exception e) {
93 | e.printStackTrace();
94 | }
95 | }
96 |
97 | private void checkIsMethod(UCallExpression node) {
98 | try {
99 | if (UastExpressionUtils.isMethodCall(node)) {
100 | if (node.getReceiver() != null && node.getMethodName() != null) {
101 | PsiMethod method = node.resolve();
102 | if (context.getEvaluator().isMemberInClass(method, WM_ROUTER_CALL)) {
103 | context.report(CALL_ISSUE, node, context.getLocation(node),
104 | "请使用项目提供的路由中间件");
105 | }
106 | }
107 | }
108 | } catch (Exception e) {
109 | e.printStackTrace();
110 | }
111 | }
112 |
113 | };
114 | }
115 |
116 | }
117 |
--------------------------------------------------------------------------------
/lint_sample/src/main/java/com/kornos/lint/demo/SafeFileDetector.kt:
--------------------------------------------------------------------------------
1 | package com.kornos.lint.demo
2 |
3 | import com.android.tools.lint.client.api.UElementHandler
4 | import com.android.tools.lint.detector.api.*
5 | import com.android.tools.lint.detector.api.Category.Companion.CORRECTNESS
6 | import com.android.tools.lint.detector.api.Scope.Companion.JAVA_FILE_SCOPE
7 | import com.kornos.lint.demo.entity.GsonUtils
8 | import org.jetbrains.uast.*
9 | import org.jetbrains.uast.util.isMethodCall
10 |
11 | /**
12 | *
13 | * @Author LiABao
14 | * @Since 2021/6/18
15 | *
16 | */
17 | class SafeFileDetector : Detector(), Detector.UastScanner {
18 |
19 | override fun getApplicableUastTypes(): List> {
20 | val types: MutableList> = mutableListOf()
21 | types.add(UCallExpression::class.java)
22 | types.add(UVariable::class.java)
23 | return types
24 | }
25 |
26 | override fun createUastHandler(context: JavaContext): UElementHandler? {
27 | return SafeFileHandler(context)
28 | }
29 |
30 | override fun beforeCheckFile(context: Context) {
31 | super.beforeCheckFile(context)
32 | println("beforeCheckFile:" + context.project + "\r\n")
33 | }
34 |
35 | override fun beforeCheckEachProject(context: Context) {
36 | super.beforeCheckEachProject(context)
37 | println("beforeCheckEachProject:" + context.project + "\r\n")
38 | }
39 |
40 | override fun beforeCheckRootProject(context: Context) {
41 | super.beforeCheckRootProject(context)
42 | println("beforeCheckRootProject:" + context.mainProject + "\r\n")
43 | val dynamicConfig = GsonUtils.inflate(context.project.dir)
44 | println("dynamicConfig:" + dynamicConfig.methods.size + "\r\n")
45 | }
46 |
47 | companion object {
48 | val ISSUE = Issue.create(
49 | "file_path_issue", //唯一 ID
50 | "该路径是保护路径,当前不允许直接使用", //简单描述
51 | "当前项目已经集成了图片中间件,所有图片操作全部移动到中间件操作", //详细描述
52 | CORRECTNESS, //问题种类(正确性、安全性等)
53 | 6, Severity.ERROR, //问题严重程度(忽略、警告、错误)
54 | Implementation( //实现,包括处理实例和作用域
55 | SafeFileDetector::class.java,
56 | JAVA_FILE_SCOPE
57 | )
58 | )
59 | }
60 | }
61 |
62 | class SafeFileHandler(private val context: JavaContext) : UElementHandler() {
63 | override fun visitCallExpression(node: UCallExpression) {
64 | checkIsMethodCall(node)
65 | checkIsConstructorCall(node)
66 | }
67 |
68 | override fun visitVariable(node: UVariable) {
69 | try {
70 | val value = node.uastInitializer
71 | value?.apply {
72 | checkUQualifiedReferenceExpression(node, value)
73 | if (this is ULiteralExpression) {
74 | checkULiteralExpression(node, this)
75 | }
76 | }
77 | } catch (e: Exception) {
78 |
79 | }
80 | }
81 |
82 |
83 | private fun checkIsConstructorCall(node: UCallExpression) {
84 | val classRef = node.classReference
85 | try {
86 | if (classRef != null) {
87 | val args = node.valueArguments
88 | for (element in args) {
89 | if (element is ULiteralExpression) {
90 | checkULiteralExpression(node, element)
91 | } else {
92 | // print("\r\n ---------visitVariable:${element} value:${element}--------- \r\n")
93 | }
94 | }
95 | }
96 | } catch (e: java.lang.Exception) {
97 | e.printStackTrace()
98 | }
99 | }
100 |
101 | private fun checkIsMethodCall(node: UCallExpression) {
102 | if (!node.isMethodCall()) {
103 | return
104 | }
105 | try {
106 | if (node.receiver != null && !node.methodName.isNullOrBlank()) {
107 | node.methodName
108 | val method = node.resolve()
109 | val value = "android.os.Environment"
110 | // if (methodName == "getExternalStorageDirectory") {
111 | if (context.evaluator.isMemberInClass(method, value)) {
112 | context.report(
113 | SafeFileDetector.ISSUE, node, context.getLocation(node),
114 | "请使用项目提供的路由中间件"
115 | )
116 | }
117 | // }
118 |
119 | }
120 | } catch (e: Exception) {
121 | e.printStackTrace()
122 | }
123 | }
124 |
125 | private fun checkUQualifiedReferenceExpression(uVariable: UVariable, node: UExpression) {
126 | if (node is UQualifiedReferenceExpression) {
127 | val text = uVariable.text
128 | if (text.contains("Environment.DIRECTORY_PICTURES")) {
129 | uVariable.toUElement()?.apply {
130 | context.report(
131 | SafeFileDetector.ISSUE, this, context.getLocation(this),
132 | "请使用项目提供的路由中间件"
133 | )
134 | }
135 |
136 | }
137 | }
138 | }
139 |
140 | private fun checkULiteralExpression(node: UElement, element: ULiteralExpression) {
141 | val stringValue = element.value
142 | if (stringValue == "Pictures") {
143 | context.report(
144 | SafeFileDetector.ISSUE, node, context.getLocation(node),
145 | "谁给你的胆子用空格的"
146 | )
147 | }
148 | }
149 |
150 |
151 | }
--------------------------------------------------------------------------------
/lint_sample/src/main/java/com/kornos/lint/demo/TestGradleDetector.kt:
--------------------------------------------------------------------------------
1 | package com.kornos.lint.demo
2 |
3 | import com.android.tools.lint.detector.api.Detector
4 | import com.android.tools.lint.detector.api.GradleContext
5 |
6 | /**
7 | *
8 | * @Author LiABao
9 | * @Since 2022/2/15
10 | *
11 | */
12 | class TestGradleDetector : Detector(), Detector.GradleScanner {
13 |
14 | override fun checkDslPropertyAssignment(
15 | context: GradleContext,
16 | property: String,
17 | value: String,
18 | parent: String,
19 | parentParent: String?,
20 | valueCookie: Any,
21 | statementCookie: Any
22 | ) {
23 |
24 | }
25 |
26 |
27 | }
--------------------------------------------------------------------------------
/lint_sample/src/main/java/com/kornos/lint/demo/TestIssueRegistry.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.kornos.lint.demo
17 |
18 | import com.android.tools.lint.client.api.IssueRegistry
19 | import com.android.tools.lint.detector.api.Issue
20 |
21 | /*
22 | * The list of issues that will be checked when running lint
.
23 | */
24 | class TestIssueRegistry : IssueRegistry() {
25 |
26 | override val issues: List
27 | get() {
28 | val lintList = mutableListOf(
29 | RouteDetector.ISSUE,
30 | RouteDetector.CALL_ISSUE,
31 | LogDetector.ISSUE,
32 | GlideUnusedDetector.ISSUE,
33 | ThreadDetector.ISSUE,
34 | PngResourceDetector.ISSUE,
35 | EventSpaceDetector.ISSUE,
36 | SafeFileDetector.ISSUE,
37 | DynamicLint.ISSUE,
38 | PrivacyClassDetector.ISSUE,
39 | ContextCastDetector.ISSUE,
40 | PreDrawDetector.ISSUE,
41 | AlertDialogDetector.ISSUE,
42 | FragmentHiddenDetector.ISSUE,
43 | KAEDetector.ISSUE
44 | )
45 | /* ServiceLoader.load(LintSpi::class.java).forEach {
46 | it.issue().forEach { issue ->
47 | lintList.add(issue)
48 | }
49 | }*/
50 | return lintList
51 | }
52 |
53 | }
--------------------------------------------------------------------------------
/lint_sample/src/main/java/com/kornos/lint/demo/ThreadDetector.java:
--------------------------------------------------------------------------------
1 | package com.kornos.lint.demo;
2 |
3 | import com.android.tools.lint.client.api.UElementHandler;
4 | import com.android.tools.lint.detector.api.Category;
5 | import com.android.tools.lint.detector.api.Detector;
6 | import com.android.tools.lint.detector.api.Implementation;
7 | import com.android.tools.lint.detector.api.Issue;
8 | import com.android.tools.lint.detector.api.JavaContext;
9 | import com.android.tools.lint.detector.api.Scope;
10 | import com.android.tools.lint.detector.api.Severity;
11 |
12 | import org.jetbrains.annotations.NotNull;
13 | import org.jetbrains.annotations.Nullable;
14 | import org.jetbrains.uast.UCallExpression;
15 | import org.jetbrains.uast.UElement;
16 | import org.jetbrains.uast.UReferenceExpression;
17 | import org.jetbrains.uast.UastUtils;
18 | import org.jetbrains.uast.util.UastExpressionUtils;
19 |
20 | import java.util.Collections;
21 | import java.util.List;
22 |
23 | /**
24 | * Author: Omooo
25 | * Date: 2019/7/5
26 | * Desc: Avoid call new Thread() directly
27 | */
28 | @SuppressWarnings("UnstableApiUsage")
29 | public class ThreadDetector extends Detector implements Detector.UastScanner {
30 |
31 | private final String NEW_THREAD = "java.lang.Thread";
32 | public static final Issue ISSUE = Issue.create(
33 | "ThreadUsage",
34 | "Thread Usage",
35 | "Please use ThreadPool,such as AsyncTask.SERIAL_EXECUTOR",
36 | Category.CORRECTNESS,
37 | 6,
38 | Severity.WARNING,
39 | new Implementation(ThreadDetector.class, Scope.JAVA_FILE_SCOPE)
40 | );
41 |
42 | @Nullable
43 | @Override
44 | public List> getApplicableUastTypes() {
45 | return Collections.singletonList(UCallExpression.class);
46 | }
47 |
48 | @Nullable
49 | @Override
50 | public UElementHandler createUastHandler(@NotNull JavaContext context) {
51 | return new UElementHandler() {
52 | @Override
53 | public void visitCallExpression(@NotNull UCallExpression node) {
54 | if (!UastExpressionUtils.isConstructorCall(node)) {
55 | return;
56 | }
57 | String className;
58 | UReferenceExpression classRef = node.getClassReference();
59 | if (classRef != null) {
60 | className = UastUtils.getQualifiedName(classRef);
61 | if (NEW_THREAD.equals(className) && context.getProject().isAndroidProject()) {
62 | context.report(
63 | ISSUE,
64 | node,
65 | context.getLocation(node),
66 | "\u21E2 Avoid call new Thread() directly"
67 | );
68 | }
69 | }
70 | }
71 | };
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/lint_sample/src/main/java/com/kornos/lint/demo/entity/DynamicConfigEntity.kt:
--------------------------------------------------------------------------------
1 | package com.kornos.lint.demo.entity
2 |
3 | /**
4 | * @Author LiABao
5 | * @Since 2021/7/20
6 | */
7 | class DynamicConfigEntity {
8 | val methods = mutableListOf()
9 |
10 | val constructions = mutableListOf()
11 | }
12 |
13 | data class DynamicEntity(
14 | val name_regex: String,
15 | val message: String,
16 | val excludes: MutableList?
17 | )
18 |
19 |
--------------------------------------------------------------------------------
/lint_sample/src/main/java/com/kornos/lint/demo/entity/GsonUtils.kt:
--------------------------------------------------------------------------------
1 | package com.kornos.lint.demo.entity
2 |
3 | import com.google.gson.Gson
4 | import java.io.File
5 |
6 | /**
7 | *
8 | * @Author LiABao
9 | * @Since 2021/4/20
10 | *
11 | */
12 | object GsonUtils {
13 |
14 | fun inflate(projectDir: File): DynamicConfigEntity {
15 | val gson = Gson()
16 | val f = getFile(projectDir)
17 | return f?.let { file ->
18 | gson.fromJson(file.bufferedReader(), DynamicConfigEntity::class.java).apply {
19 | }
20 | } ?: DynamicConfigEntity()
21 | }
22 |
23 |
24 | private fun getFile(projectDir: File): File? {
25 | var f = File(projectDir.parentFile, DYNAMIC_FILE_NAME)
26 | if (f.exists()) {
27 | return f
28 | }
29 | f = File(projectDir, DYNAMIC_FILE_NAME)
30 | if (f.exists()) {
31 | return f
32 | }
33 | val codeQualifiedFile = findCodeQuality(projectDir) ?: return null
34 | println("codeQualifiedFile:${codeQualifiedFile.path} \r\n")
35 | f = File(codeQualifiedFile, DYNAMIC_FILE_NAME)
36 | if (f.exists()) {
37 | return f
38 | }
39 | return null
40 | }
41 |
42 | private fun findCodeQuality(projectDir: File): File? {
43 | if (projectDir.parent != null) {
44 | val parent = projectDir.parentFile
45 | val file = parent.listFiles()?.firstOrNull {
46 | it.name == ".codequality" && it.isDirectory
47 | }
48 | return file ?: findCodeQuality(parent)
49 | }
50 | return null
51 | }
52 |
53 |
54 | private const val DYNAMIC_FILE_NAME = "dynamic.json"
55 | }
--------------------------------------------------------------------------------
/lint_sample/src/main/java/com/kornos/lint/demo/entity/LintEx.kt:
--------------------------------------------------------------------------------
1 | package com.kornos.lint.demo.entity
2 |
3 | import com.android.tools.lint.detector.api.*
4 | import org.jetbrains.uast.UCallExpression
5 |
6 | /**
7 | * User: Rocket
8 | * Date: 2020/6/11
9 | * Time: 4:37 PM
10 | */
11 |
12 | /**
13 | * 获取该表达式的标准名称
14 | * 例:android.content.ContextWrapper.getSharedPreferences
15 | */
16 | fun UCallExpression.getQualifiedName(): String {
17 | return resolve()?.containingClass?.qualifiedName + "." + resolve()?.name
18 | }
19 |
20 | fun Context.report(issue: Issue, location: Location, baseProperty: DynamicEntity) {
21 | this.report(
22 | Issue.create(
23 | issue.id, baseProperty.message,
24 | issue.getExplanation(TextFormat.TEXT),
25 | issue.category, issue.priority,
26 | Severity.ERROR, issue.implementation
27 | ),
28 | location,
29 | baseProperty.message
30 | )
31 | }
--------------------------------------------------------------------------------
/lint_sample/src/main/java/com/kornos/lint/demo/entity/LintMatcher.kt:
--------------------------------------------------------------------------------
1 | package com.kornos.lint.demo.entity
2 |
3 | import com.intellij.psi.PsiClass
4 | import org.jetbrains.uast.UCallExpression
5 | import org.jetbrains.uast.UClass
6 | import org.jetbrains.uast.getContainingUClass
7 | import org.jetbrains.uast.getQualifiedName
8 | import java.util.regex.Pattern
9 |
10 | /**
11 | * lint 名字匹配器
12 | * User: Rocket
13 | * Date: 2020/6/12
14 | * Time: 4:38 PM
15 | */
16 | class LintMatcher {
17 | companion object {
18 | /**
19 | * 匹配方法
20 | */
21 | fun matchMethod(baseConfig: DynamicEntity, node: UCallExpression): Boolean {
22 | return match(
23 | baseConfig.name_regex,
24 | node.getQualifiedName(),
25 | node.getContainingUClass()?.qualifiedName,
26 | baseConfig.excludes?.apply {
27 | // print("excludes :$this \r\n")
28 | } ?: emptyList(), ""
29 | )
30 | }
31 |
32 | /**
33 | * 匹配构造方法
34 | */
35 | fun matchConstruction(baseConfig: DynamicEntity, node: UCallExpression): Boolean {
36 | return match(
37 | baseConfig.name_regex,
38 | node.classReference.getQualifiedName(),
39 | //不要使用node.resolve()获取构造方法,在没定义构造方法使用默认构造的时候返回值为null
40 | node.getContainingUClass()?.qualifiedName,
41 | emptyList(), ""
42 | )
43 | }
44 |
45 | /**
46 | * 匹配继承或实现类
47 | */
48 | fun matchInheritClass(
49 | baseConfig: DynamicEntity,
50 | node: UClass
51 | ): Boolean {
52 | node.supers.forEach {
53 | if (match(
54 | baseConfig.name_regex,
55 | it.qualifiedName, node.qualifiedName,
56 | emptyList(), ""
57 | )
58 | ) return true
59 | }
60 | return false
61 | }
62 |
63 | /**
64 | * 匹配文件名
65 | */
66 | fun matchFileName(
67 | baseConfig: DynamicEntity,
68 | fileName: String
69 | ) = match(
70 | baseConfig.name_regex,
71 | fileName
72 | )
73 |
74 | /**
75 | * 匹配类
76 | */
77 | fun matchClass(
78 | baseConfig: DynamicEntity,
79 | node: PsiClass
80 | ): Boolean {
81 | return match(
82 | baseConfig.name_regex,
83 | node.qualifiedName,
84 | node.containingClass?.qualifiedName,
85 | emptyList(), ""
86 | )
87 | }
88 |
89 |
90 | /**
91 | * name是完全匹配,nameRegex是正则匹配,匹配优先级上name > nameRegex
92 | * inClassName是当前需要匹配的方法所在类
93 | * exclude是要排除匹配的类(目前以类的粒度去排除)
94 | */
95 | private fun match(
96 | nameRegex: String?,
97 | qualifiedName: String?,
98 | inClassName: String? = null,
99 | exclude: List = emptyList(),
100 | excludeRegex: String? = null
101 | ): Boolean {
102 | qualifiedName ?: return false
103 |
104 | //排除
105 |
106 | if (inClassName != null && inClassName.isNotEmpty()) {
107 | if (exclude.contains(inClassName)) return false
108 |
109 | if (excludeRegex != null &&
110 | excludeRegex.isNotEmpty() &&
111 | Pattern.compile(excludeRegex).matcher(inClassName).find()
112 | ) {
113 | return false
114 | }
115 | }
116 |
117 | if (nameRegex != null && nameRegex.isNotEmpty() &&
118 | Pattern.compile(nameRegex).matcher(qualifiedName).find()
119 | ) {//在匹配nameRegex
120 | return true
121 | }
122 | return false
123 | }
124 | }
125 | }
--------------------------------------------------------------------------------
/lint_sample/src/main/java/com/kornos/lint/demo/privacy/PrivacyAsmEntity.kt:
--------------------------------------------------------------------------------
1 | package com.kornos.lint.demo.privacy
2 |
3 | class PrivacyAsmEntity(
4 | val code: Int,
5 | val owner: String,
6 | val name: String,
7 | val desc: String
8 | )
--------------------------------------------------------------------------------
/lint_sample/src/main/java/com/kornos/lint/demo/privacy/PrivacyHelper.kt:
--------------------------------------------------------------------------------
1 | package com.kornos.lint.demo.privacy
2 |
3 | import org.objectweb.asm.Opcodes
4 |
5 | /**
6 | *
7 | * @Author LiABao
8 | * @Since 2021/9/2
9 | *
10 | */
11 | object PrivacyHelper {
12 |
13 | val privacyList = mutableListOf().apply {
14 | add(
15 | PrivacyAsmEntity(
16 | Opcodes.INVOKEVIRTUAL, "android/telephony/TelephonyManager",
17 | "getDeviceId", "()Ljava/lang/String;"
18 | )
19 |
20 | )
21 | add(
22 | PrivacyAsmEntity(
23 | Opcodes.INVOKEVIRTUAL, "android/net/wifi/WifiInfo",
24 | "getSSID", "()Ljava/lang/String;"
25 | )
26 | )
27 | add(
28 | PrivacyAsmEntity(
29 | Opcodes.INVOKEVIRTUAL, "android/net/wifi/WifiInfo",
30 | "getBSSID", "()Ljava/lang/String;"
31 | )
32 | )
33 | add(
34 | PrivacyAsmEntity(
35 | Opcodes.INVOKESTATIC,
36 | "android/provider/Settings\$Secure",
37 | "getString",
38 | "(Landroid/content/ContentResolver;Ljava/lang/String;)Ljava/lang/String;"
39 | )
40 | )
41 | add(
42 | PrivacyAsmEntity(
43 | Opcodes.INVOKEVIRTUAL, "android/content/pm/PackageManager",
44 | "getInstalledPackages", "(I)Ljava/util/List;"
45 | )
46 | )
47 |
48 | add(
49 | PrivacyAsmEntity(
50 | Opcodes.INVOKEVIRTUAL, "android/content/pm/PackageManager",
51 | "getInstalledApplications", "(I)Ljava/util/List;"
52 | )
53 | )
54 |
55 |
56 | }
57 |
58 |
59 | val whiteList = mutableListOf("com/wallstreetcn", "leakcanary", "shark").apply {
60 |
61 | }
62 | }
63 |
64 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':lint_sample'
2 | include ':demo1'
3 | include ':subSample:demo2'
4 |
--------------------------------------------------------------------------------
/subSample/demo2/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/subSample/demo2/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.library'
3 | id 'kotlin-android'
4 | }
5 |
6 | android {
7 | compileSdkVersion 30
8 | buildToolsVersion "30.0.3"
9 |
10 | defaultConfig {
11 | minSdkVersion 21
12 | targetSdkVersion 30
13 |
14 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
15 | consumerProguardFiles "consumer-rules.pro"
16 | }
17 |
18 | buildTypes {
19 | release {
20 | minifyEnabled false
21 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
22 | }
23 | }
24 | compileOptions {
25 | sourceCompatibility JavaVersion.VERSION_1_8
26 | targetCompatibility JavaVersion.VERSION_1_8
27 | }
28 | kotlinOptions {
29 | jvmTarget = '1.8'
30 | }
31 | }
32 |
33 | dependencies {
34 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
35 | implementation 'androidx.core:core-ktx:1.5.0'
36 | implementation 'androidx.appcompat:appcompat:1.3.0'
37 | implementation 'com.google.android.material:material:1.3.0'
38 | testImplementation 'junit:junit:4.+'
39 | androidTestImplementation 'androidx.test.ext:junit:1.1.2'
40 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
41 | lintChecks project(":lint_sample")
42 | }
--------------------------------------------------------------------------------
/subSample/demo2/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Leifzhang/AndroidLint/84a4e3cd98e0d3098a832030780d5c4576b8bf75/subSample/demo2/consumer-rules.pro
--------------------------------------------------------------------------------
/subSample/demo2/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
--------------------------------------------------------------------------------
/subSample/demo2/src/androidTest/java/com/kronos/demo2/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.kronos.demo2
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22 | assertEquals("com.kronos.demo2.test", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/subSample/demo2/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/subSample/demo2/src/test/java/com/kronos/demo2/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.kronos.demo2
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
--------------------------------------------------------------------------------