android-sdd 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (176) hide show
  1. package/dist/index.js +143 -0
  2. package/package.json +27 -0
  3. package/skills/Android Ecosystem/Baseline Profile Generator/SKILL.md +277 -0
  4. package/skills/Android Ecosystem/Glance/SKILL.md +315 -0
  5. package/skills/Android Platform/Configuration/SKILL.md +201 -0
  6. package/skills/Android Platform/Filesystem/SKILL.md +216 -0
  7. package/skills/Android Platform/Lifecycle/SKILL.md +233 -0
  8. package/skills/Android Platform/Manifest/SKILL.md +226 -0
  9. package/skills/Android Platform/Process Death Recovery/SKILL.md +214 -0
  10. package/skills/Android Platform/Resources/SKILL.md +234 -0
  11. package/skills/Android Platform/SavedStateHandle/SKILL.md +217 -0
  12. package/skills/Android Platform/State Restoration/SKILL.md +210 -0
  13. package/skills/Architecture/Bounded Context/SKILL.md +207 -0
  14. package/skills/Architecture/Clean Architecture/SKILL.md +229 -0
  15. package/skills/Architecture/Domain Modeling/SKILL.md +236 -0
  16. package/skills/Architecture/Entity Design/SKILL.md +243 -0
  17. package/skills/Architecture/Feature Isolation/SKILL.md +216 -0
  18. package/skills/Architecture/MVI/SKILL.md +224 -0
  19. package/skills/Architecture/MVVM/SKILL.md +198 -0
  20. package/skills/Architecture/Modularization/SKILL.md +194 -0
  21. package/skills/Architecture/Offline First/SKILL.md +249 -0
  22. package/skills/Architecture/Repository Pattern/SKILL.md +216 -0
  23. package/skills/Architecture/Side Effect Management/SKILL.md +278 -0
  24. package/skills/Architecture/State Management/SKILL.md +229 -0
  25. package/skills/Architecture/Unidirectional Data Flow/SKILL.md +196 -0
  26. package/skills/Architecture/Use Case Design/SKILL.md +244 -0
  27. package/skills/Architecture/Value Object/SKILL.md +226 -0
  28. package/skills/Build Infrastructure/Build Orchestration/SKILL.md +257 -0
  29. package/skills/Build Infrastructure/Dependency Compatibility Resolver/SKILL.md +259 -0
  30. package/skills/Build Infrastructure/Environment Validator/SKILL.md +311 -0
  31. package/skills/Build System/Build Cache/SKILL.md +233 -0
  32. package/skills/Build System/Build Flavor Strategy/SKILL.md +171 -0
  33. package/skills/Build System/Build Variant/SKILL.md +215 -0
  34. package/skills/Build System/Convention Plugin/SKILL.md +288 -0
  35. package/skills/Build System/Dependency Management/SKILL.md +261 -0
  36. package/skills/Build System/Gradle/SKILL.md +284 -0
  37. package/skills/Build System/Incremental Build/SKILL.md +199 -0
  38. package/skills/Build System/KAPT/SKILL.md +198 -0
  39. package/skills/Build System/KSP/SKILL.md +263 -0
  40. package/skills/Build System/Module Dependency Graph Validation/SKILL.md +223 -0
  41. package/skills/Build System/Specialized/C++/SKILL.md +308 -0
  42. package/skills/Build System/Specialized/JNI/SKILL.md +306 -0
  43. package/skills/Build System/Specialized/NDK/SKILL.md +264 -0
  44. package/skills/Build System/Version Catalog/SKILL.md +304 -0
  45. package/skills/Concurrency/Background Processing/SKILL.md +185 -0
  46. package/skills/Concurrency/Channel/SKILL.md +207 -0
  47. package/skills/Concurrency/Coroutine/SKILL.md +200 -0
  48. package/skills/Concurrency/Flow/SKILL.md +179 -0
  49. package/skills/Concurrency/Mutex Strategy/SKILL.md +185 -0
  50. package/skills/Concurrency/SharedFlow/SKILL.md +171 -0
  51. package/skills/Concurrency/StateFlow/SKILL.md +175 -0
  52. package/skills/Concurrency/Structured Concurrency/SKILL.md +197 -0
  53. package/skills/Concurrency/Synchronization Policy/SKILL.md +192 -0
  54. package/skills/Core Language/Annotation Processing/SKILL.md +224 -0
  55. package/skills/Core Language/DSL/SKILL.md +186 -0
  56. package/skills/Core Language/Extension Functions Design/SKILL.md +191 -0
  57. package/skills/Core Language/Immutability/SKILL.md +156 -0
  58. package/skills/Core Language/KMP/SKILL.md +182 -0
  59. package/skills/Core Language/Kotlin/SKILL.md +187 -0
  60. package/skills/Core Language/Reactive State Management/SKILL.md +228 -0
  61. package/skills/Core Language/Reactive Streams/SKILL.md +235 -0
  62. package/skills/Core Language/Serialization/SKILL.md +191 -0
  63. package/skills/Data Layer/Cache Strategy/SKILL.md +261 -0
  64. package/skills/Data Layer/Conflict Resolution/SKILL.md +248 -0
  65. package/skills/Data Layer/DAO/SKILL.md +225 -0
  66. package/skills/Data Layer/DTO Mapping/SKILL.md +269 -0
  67. package/skills/Data Layer/DataStore/SKILL.md +264 -0
  68. package/skills/Data Layer/Database Versioning Strategy/SKILL.md +215 -0
  69. package/skills/Data Layer/Encrypted Database/SKILL.md +212 -0
  70. package/skills/Data Layer/File Storage/SKILL.md +247 -0
  71. package/skills/Data Layer/Indexing/SKILL.md +184 -0
  72. package/skills/Data Layer/Key-Value Store Strategy/SKILL.md +185 -0
  73. package/skills/Data Layer/Merge Strategy/SKILL.md +240 -0
  74. package/skills/Data Layer/Migration/SKILL.md +243 -0
  75. package/skills/Data Layer/Paging/SKILL.md +264 -0
  76. package/skills/Data Layer/Proto DataStore/SKILL.md +250 -0
  77. package/skills/Data Layer/Room/SKILL.md +244 -0
  78. package/skills/Data Layer/SQLite/SKILL.md +255 -0
  79. package/skills/Data Layer/Sync Engine/SKILL.md +268 -0
  80. package/skills/Dependency Injection/Dagger/SKILL.md +283 -0
  81. package/skills/Dependency Injection/Hilt/SKILL.md +345 -0
  82. package/skills/Dependency Injection/Koin/SKILL.md +282 -0
  83. package/skills/Developer Experience/Detekt/SKILL.md +272 -0
  84. package/skills/Developer Experience/Lint Rule/SKILL.md +281 -0
  85. package/skills/Google Ecosystem/Analytics/SKILL.md +281 -0
  86. package/skills/Google Ecosystem/Crashlytics/SKILL.md +234 -0
  87. package/skills/Google Ecosystem/Firebase/SKILL.md +200 -0
  88. package/skills/Google Ecosystem/Firebase Messaging/SKILL.md +266 -0
  89. package/skills/Media/Audio/SKILL.md +257 -0
  90. package/skills/Media/Camera/SKILL.md +229 -0
  91. package/skills/Media/CameraX/SKILL.md +295 -0
  92. package/skills/Media/ExoPlayer/SKILL.md +258 -0
  93. package/skills/Media/Video/SKILL.md +228 -0
  94. package/skills/Meta Skills/Domain Error Model/SKILL.md +238 -0
  95. package/skills/Meta Skills/Error Handling/SKILL.md +255 -0
  96. package/skills/Meta Skills/Error Mapping/SKILL.md +232 -0
  97. package/skills/Meta Skills/Failure Strategy/SKILL.md +294 -0
  98. package/skills/Meta Skills/Migration Strategy/SKILL.md +305 -0
  99. package/skills/Meta Skills/User Friendly Errors/SKILL.md +334 -0
  100. package/skills/Navigation/Deep Navigation/SKILL.md +209 -0
  101. package/skills/Navigation/Navigation/SKILL.md +215 -0
  102. package/skills/Navigation/Nested Navigation/SKILL.md +214 -0
  103. package/skills/Networking/API Contract/SKILL.md +220 -0
  104. package/skills/Networking/Authentication/SKILL.md +210 -0
  105. package/skills/Networking/Certificate Pinning/SKILL.md +167 -0
  106. package/skills/Networking/Fallback Strategy/SKILL.md +182 -0
  107. package/skills/Networking/Ktor/SKILL.md +219 -0
  108. package/skills/Networking/Multipart Upload/SKILL.md +213 -0
  109. package/skills/Networking/OkHttp/SKILL.md +193 -0
  110. package/skills/Networking/REST/SKILL.md +178 -0
  111. package/skills/Networking/Rate Limiting/SKILL.md +170 -0
  112. package/skills/Networking/Retrofit/SKILL.md +241 -0
  113. package/skills/Networking/Retry-Backoff/SKILL.md +181 -0
  114. package/skills/Networking/Server-Sent Events (SSE)/SKILL.md +196 -0
  115. package/skills/Networking/WebSocket/SKILL.md +224 -0
  116. package/skills/Observability/Crash Reporting/SKILL.md +219 -0
  117. package/skills/Observability/Logging/SKILL.md +168 -0
  118. package/skills/Observability/Metrics/SKILL.md +227 -0
  119. package/skills/Observability/Structured Logging/SKILL.md +234 -0
  120. package/skills/Performance/ANR Prevention/SKILL.md +192 -0
  121. package/skills/Performance/Allocation Optimization/SKILL.md +179 -0
  122. package/skills/Performance/App Startup/SKILL.md +183 -0
  123. package/skills/Performance/Baseline Profile/SKILL.md +205 -0
  124. package/skills/Performance/Battery Optimization/SKILL.md +192 -0
  125. package/skills/Performance/Benchmark/SKILL.md +182 -0
  126. package/skills/Performance/Bitmap Optimization/SKILL.md +178 -0
  127. package/skills/Performance/Compose Optimization/SKILL.md +187 -0
  128. package/skills/Performance/Heap Management/SKILL.md +184 -0
  129. package/skills/Performance/Macrobenchmark/SKILL.md +214 -0
  130. package/skills/Performance/Memory Leak Prevention/SKILL.md +218 -0
  131. package/skills/Performance/Rendering Performance/SKILL.md +205 -0
  132. package/skills/Performance/Startup Optimization/SKILL.md +219 -0
  133. package/skills/Security/Biometric/SKILL.md +224 -0
  134. package/skills/Security/Certificate Transparency/SKILL.md +158 -0
  135. package/skills/Security/Cryptography/SKILL.md +244 -0
  136. package/skills/Security/Encrypted Storage/SKILL.md +273 -0
  137. package/skills/Security/Frida Detection/SKILL.md +230 -0
  138. package/skills/Security/Hook Detection/SKILL.md +197 -0
  139. package/skills/Security/Keystore/SKILL.md +272 -0
  140. package/skills/Security/Network Security Config/SKILL.md +186 -0
  141. package/skills/Security/Obfuscation/SKILL.md +226 -0
  142. package/skills/Security/Proguard/SKILL.md +202 -0
  143. package/skills/Security/R8/SKILL.md +234 -0
  144. package/skills/Security/Reverse Engineering Resistance/SKILL.md +267 -0
  145. package/skills/Security/Root Detection/SKILL.md +220 -0
  146. package/skills/Security/Secure Networking/SKILL.md +220 -0
  147. package/skills/System Integration/AlarmManager/SKILL.md +182 -0
  148. package/skills/System Integration/App Widget/SKILL.md +182 -0
  149. package/skills/System Integration/Deep Link/SKILL.md +187 -0
  150. package/skills/System Integration/Foreground Service/SKILL.md +212 -0
  151. package/skills/System Integration/Notification/SKILL.md +237 -0
  152. package/skills/System Integration/WorkManager/SKILL.md +256 -0
  153. package/skills/System Integration/clipboard/SKILL.md +155 -0
  154. package/skills/System Integration/share-intent/SKILL.md +182 -0
  155. package/skills/Testing/Compose Testing/SKILL.md +296 -0
  156. package/skills/Testing/Espresso/SKILL.md +292 -0
  157. package/skills/Testing/Fake Data/SKILL.md +245 -0
  158. package/skills/Testing/Integration Testing/SKILL.md +288 -0
  159. package/skills/Testing/Mocking/SKILL.md +229 -0
  160. package/skills/Testing/Snapshot Testing/SKILL.md +259 -0
  161. package/skills/Testing/UI Testing/SKILL.md +293 -0
  162. package/skills/Testing/Unit Testing/SKILL.md +309 -0
  163. package/skills/UI System/Bottom Sheet Patterns/SKILL.md +279 -0
  164. package/skills/UI System/Compose/SKILL.md +296 -0
  165. package/skills/UI System/Compose Animation/SKILL.md +281 -0
  166. package/skills/UI System/Compose Multiplatform/SKILL.md +261 -0
  167. package/skills/UI System/Compose Navigation/SKILL.md +255 -0
  168. package/skills/UI System/Compose Performance/SKILL.md +274 -0
  169. package/skills/UI System/Design System/SKILL.md +217 -0
  170. package/skills/UI System/Empty State Strategy/SKILL.md +208 -0
  171. package/skills/UI System/Keyboard Navigation/SKILL.md +214 -0
  172. package/skills/UI System/Loading Strategy/SKILL.md +254 -0
  173. package/skills/UI System/Material 3/SKILL.md +279 -0
  174. package/skills/UI System/RTL/SKILL.md +179 -0
  175. package/src/index.ts +182 -0
  176. package/tsconfig.json +19 -0
@@ -0,0 +1,272 @@
1
+ ---
2
+ name: detekt
3
+ description: >
4
+ Detekt static analysis for Kotlin Android projects.
5
+ Load this skill when setting up Detekt, configuring rules,
6
+ writing custom Detekt rules, suppressing false positives,
7
+ integrating Detekt into CI, or generating reports.
8
+ ---
9
+
10
+ # Detekt
11
+
12
+ ## Overview
13
+ Detekt is a static code analysis tool for Kotlin. It detects code smells, complexity issues, style violations, and potential bugs. Unlike Android Lint (which targets Android-specific patterns), Detekt focuses on general Kotlin code quality. Both should be used together.
14
+
15
+ ---
16
+
17
+ ## Core Principles
18
+
19
+ - Detekt runs on **Kotlin source** — not bytecode; catches issues before compilation
20
+ - Configure via **`detekt.yml`** — enable/disable rules, set thresholds
21
+ - **Baseline** for existing codebases — suppress existing violations, only check new code
22
+ - Run in **CI** — fail the build on new violations
23
+ - **Type resolution** mode gives more accurate results but is slower
24
+
25
+ ---
26
+
27
+ ## Setup
28
+
29
+ ```kotlin
30
+ // root build.gradle.kts
31
+ plugins {
32
+ alias(libs.plugins.detekt) apply false
33
+ }
34
+
35
+ // app/build.gradle.kts (or in convention plugin)
36
+ plugins {
37
+ alias(libs.plugins.detekt)
38
+ }
39
+
40
+ detekt {
41
+ config.setFrom(files("$rootDir/config/detekt/detekt.yml"))
42
+ baseline = file("$rootDir/config/detekt/baseline.xml")
43
+ buildUponDefaultConfig = true // start from default, override in yml
44
+ allRules = false // don't enable all rules by default
45
+ autoCorrect = false // set true to auto-fix style issues
46
+ }
47
+
48
+ dependencies {
49
+ detektPlugins(libs.detekt.formatting) // ktlint-based formatting rules
50
+ }
51
+ ```
52
+
53
+ ```toml
54
+ # libs.versions.toml
55
+ [versions]
56
+ detekt = "1.23.6"
57
+
58
+ [libraries]
59
+ detekt-formatting = { module = "io.gitlab.arturbosch.detekt:detekt-formatting", version.ref = "detekt" }
60
+
61
+ [plugins]
62
+ detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" }
63
+ ```
64
+
65
+ ---
66
+
67
+ ## detekt.yml Configuration
68
+
69
+ ```yaml
70
+ # config/detekt/detekt.yml
71
+
72
+ build:
73
+ maxIssues: 0 # fail on any new issue
74
+
75
+ complexity:
76
+ LongMethod:
77
+ threshold: 40 # max lines per function
78
+ LongParameterList:
79
+ functionThreshold: 6
80
+ constructorThreshold: 7
81
+ TooManyFunctions:
82
+ thresholdInFiles: 20
83
+ thresholdInClasses: 15
84
+ CyclomaticComplexMethod:
85
+ threshold: 15
86
+
87
+ naming:
88
+ FunctionNaming:
89
+ functionPattern: '[a-z][a-zA-Z0-9]*'
90
+ excludes: ['**/test/**', '**/androidTest/**']
91
+ ClassNaming:
92
+ classPattern: '[A-Z][a-zA-Z0-9]*'
93
+ VariableNaming:
94
+ variablePattern: '[a-z][A-Za-z0-9]*'
95
+
96
+ style:
97
+ MagicNumber:
98
+ active: true
99
+ ignoreNumbers: ['-1', '0', '1', '2']
100
+ ignoreHashCodeFunction: true
101
+ ignorePropertyDeclaration: true
102
+ WildcardImport:
103
+ active: true
104
+ UnusedPrivateMember:
105
+ active: true
106
+ ForbiddenComment:
107
+ active: true
108
+ values: ['FIXME:', 'STOPSHIP:']
109
+
110
+ exceptions:
111
+ TooGenericExceptionCaught:
112
+ active: true
113
+ exceptionNames: ['Exception', 'Throwable']
114
+ allowedExceptionNameRegex: '_|(ignore|expected).*'
115
+ SwallowedException:
116
+ active: true
117
+
118
+ coroutines:
119
+ GlobalCoroutineUsage:
120
+ active: true
121
+ RedundantSuspendModifier:
122
+ active: true
123
+ SuspendFunWithFlowReturnType:
124
+ active: true
125
+
126
+ formatting:
127
+ MaximumLineLength:
128
+ maxLineLength: 120
129
+ NoUnusedImports:
130
+ active: true
131
+ TrailingCommaOnCallSite:
132
+ active: false
133
+ ```
134
+
135
+ ---
136
+
137
+ ## Running Detekt
138
+
139
+ ```bash
140
+ # ✅ Run all checks
141
+ ./gradlew detekt
142
+
143
+ # ✅ Run with type resolution (more accurate, slower)
144
+ ./gradlew detektMain
145
+
146
+ # ✅ Auto-fix formatting issues
147
+ ./gradlew detekt --auto-correct
148
+
149
+ # ✅ Generate HTML report
150
+ ./gradlew detekt
151
+ # Report at: app/build/reports/detekt/detekt.html
152
+ ```
153
+
154
+ ---
155
+
156
+ ## Baseline (for existing projects)
157
+
158
+ ```bash
159
+ # ✅ Generate baseline from current violations
160
+ ./gradlew detektBaseline
161
+
162
+ # This creates baseline.xml with all current issues
163
+ # Future runs only fail on NEW violations
164
+ ```
165
+
166
+ ---
167
+
168
+ ## Suppressing Violations
169
+
170
+ ```kotlin
171
+ // ✅ Suppress a specific rule inline
172
+ @Suppress("MagicNumber")
173
+ val timeout = 30_000L
174
+
175
+ // ✅ Suppress multiple rules
176
+ @Suppress("LongMethod", "ComplexMethod")
177
+ fun complexLegacyFunction() { ... }
178
+
179
+ // ✅ Suppress in detekt.yml for entire paths
180
+ style:
181
+ MagicNumber:
182
+ excludes: ['**/test/**', '**/androidTest/**', '**/Preview*.kt']
183
+ ```
184
+
185
+ ---
186
+
187
+ ## Custom Detekt Rule
188
+
189
+ ```kotlin
190
+ // ✅ Custom rule — detect hardcoded strings in Compose
191
+ class HardcodedStringInComposeRule(config: Config) : Rule(config) {
192
+
193
+ override val issue = Issue(
194
+ id = "HardcodedStringInCompose",
195
+ severity = Severity.Warning,
196
+ description = "Hardcoded strings in Compose should use string resources",
197
+ debt = Debt.FIVE_MINS
198
+ )
199
+
200
+ override fun visitCallExpression(expression: KtCallExpression) {
201
+ super.visitCallExpression(expression)
202
+
203
+ // Check if it's a Text() composable with a hardcoded string
204
+ if (expression.calleeExpression?.text == "Text") {
205
+ val firstArg = expression.valueArguments.firstOrNull()
206
+ val argText = firstArg?.getArgumentExpression()?.text
207
+ if (argText != null && argText.startsWith("\"")) {
208
+ report(CodeSmell(
209
+ issue = issue,
210
+ entity = Entity.from(expression),
211
+ message = "Use stringResource() instead of hardcoded string: $argText"
212
+ ))
213
+ }
214
+ }
215
+ }
216
+ }
217
+
218
+ // Register in custom rule set provider
219
+ class CustomRuleSetProvider : RuleSetProvider {
220
+ override val ruleSetId: String = "custom-rules"
221
+ override fun instance(config: Config) = RuleSet(
222
+ ruleSetId,
223
+ listOf(HardcodedStringInComposeRule(config))
224
+ )
225
+ }
226
+ ```
227
+
228
+ ---
229
+
230
+ ## CI Integration
231
+
232
+ ```yaml
233
+ # .github/workflows/quality.yml
234
+ - name: Run Detekt
235
+ run: ./gradlew detekt
236
+
237
+ - name: Upload Detekt report
238
+ if: failure()
239
+ uses: actions/upload-artifact@v3
240
+ with:
241
+ name: detekt-report
242
+ path: '**/build/reports/detekt/'
243
+ ```
244
+
245
+ ---
246
+
247
+ ## Detekt vs Lint
248
+
249
+ | | Detekt | Android Lint |
250
+ |---|---|---|
251
+ | Target | Kotlin code quality | Android-specific issues |
252
+ | Runs on | JVM, CI, pre-commit | Build, CI |
253
+ | Custom rules | Kotlin DSL | PSI/UAST |
254
+ | Auto-fix | ✅ (formatting) | Limited |
255
+ | Best for | Code smells, complexity | Android anti-patterns |
256
+
257
+ ---
258
+
259
+ ## Anti-Patterns
260
+
261
+ - Ignoring all violations with a baseline — baseline is for migration, not ongoing suppression
262
+ - `allRules = true` — too noisy; causes over-suppression
263
+ - Not running Detekt in CI — violations accumulate silently
264
+ - Per-file `@Suppress` without a comment explaining why — future maintainers won't understand
265
+ - Skipping `detekt-formatting` plugin — misses import order and whitespace issues
266
+
267
+ ---
268
+
269
+ ## Related Skills
270
+ - `lint-rule` — Android Lint for Android-specific checks
271
+ - `gradle` — build integration
272
+ - `convention-plugin` — sharing Detekt config across modules
@@ -0,0 +1,281 @@
1
+ ---
2
+ name: lint-rule
3
+ description: >
4
+ Custom Android Lint rules for enforcing project conventions.
5
+ Load this skill when writing custom Lint checks, detecting forbidden
6
+ patterns in code, enforcing naming conventions automatically,
7
+ or integrating custom rules into the build pipeline.
8
+ ---
9
+
10
+ # Lint Rule
11
+
12
+ ## Overview
13
+ Android Lint is a static analysis tool that inspects source code for bugs, style violations, and custom project conventions. You can write custom Lint rules to enforce patterns specific to your project — preventing anti-patterns before they reach code review.
14
+
15
+ ---
16
+
17
+ ## Core Principles
18
+
19
+ - Custom rules run at **build time** — catch issues before they reach review
20
+ - Rules target **PSI (Program Structure Interface)** — the AST of Kotlin/Java files
21
+ - **UAST (Universal AST)** works for both Kotlin and Java — prefer it over PSI
22
+ - Each rule has a unique **Issue ID** — used in `@SuppressLint` and baseline files
23
+ - Rules are packaged in a separate **lint module** — consumed by the app module
24
+
25
+ ---
26
+
27
+ ## Module Setup
28
+
29
+ ```
30
+ project/
31
+ ├── app/
32
+ ├── lint/ # custom lint rules module
33
+ │ ├── build.gradle.kts
34
+ │ └── src/main/kotlin/
35
+ │ └── com/example/lint/
36
+ │ ├── MyDetector.kt
37
+ │ └── MyIssueRegistry.kt
38
+ ```
39
+
40
+ ```kotlin
41
+ // lint/build.gradle.kts
42
+ plugins {
43
+ alias(libs.plugins.kotlin.jvm)
44
+ }
45
+
46
+ dependencies {
47
+ compileOnly(libs.lint.api)
48
+ compileOnly(libs.lint.checks)
49
+ testImplementation(libs.lint.tests)
50
+ testImplementation(libs.junit)
51
+ }
52
+ ```
53
+
54
+ ```kotlin
55
+ // app/build.gradle.kts
56
+ dependencies {
57
+ lintChecks(project(":lint"))
58
+ }
59
+ ```
60
+
61
+ ---
62
+
63
+ ## Basic Detector Structure
64
+
65
+ ```kotlin
66
+ // ✅ Detector that finds a forbidden pattern
67
+ class ForbiddenCallDetector : Detector(), Detector.UastScanners {
68
+
69
+ override fun getApplicableMethodNames(): List<String> = listOf("printStackTrace")
70
+
71
+ override fun visitMethodCall(
72
+ context: JavaContext,
73
+ node: UCallExpression,
74
+ method: PsiMethod
75
+ ) {
76
+ context.report(
77
+ issue = ISSUE,
78
+ scope = node,
79
+ location = context.getLocation(node),
80
+ message = "Use structured logging instead of `printStackTrace()`"
81
+ )
82
+ }
83
+
84
+ companion object {
85
+ val ISSUE = Issue.create(
86
+ id = "ForbiddenPrintStackTrace",
87
+ briefDescription = "Use structured logging",
88
+ explanation = """
89
+ `printStackTrace()` logs to stdout which is not captured by crash reporters.
90
+ Use the project's Logger instead.
91
+ """.trimIndent(),
92
+ category = Category.CORRECTNESS,
93
+ priority = 7,
94
+ severity = Severity.ERROR,
95
+ implementation = Implementation(
96
+ ForbiddenCallDetector::class.java,
97
+ Scope.JAVA_FILE_SCOPE
98
+ )
99
+ )
100
+ }
101
+ }
102
+ ```
103
+
104
+ ---
105
+
106
+ ## Issue Registry
107
+
108
+ ```kotlin
109
+ // ✅ Register all custom issues
110
+ class MyIssueRegistry : IssueRegistry() {
111
+ override val issues: List<Issue> = listOf(
112
+ ForbiddenCallDetector.ISSUE,
113
+ DirectColorUsageDetector.ISSUE,
114
+ MissingTestTagDetector.ISSUE
115
+ )
116
+
117
+ override val api: Int = CURRENT_API
118
+ override val minApi: Int = 8
119
+ }
120
+ ```
121
+
122
+ ```
123
+ # lint/src/main/resources/META-INF/services/
124
+ # File: com.android.tools.lint.client.api.IssueRegistry
125
+ com.example.lint.MyIssueRegistry
126
+ ```
127
+
128
+ ---
129
+
130
+ ## Practical Examples
131
+
132
+ ```kotlin
133
+ // ✅ Detect direct color usage (enforce design tokens)
134
+ class DirectColorUsageDetector : ResourceXmlDetector() {
135
+
136
+ override fun getApplicableAttributes(): Collection<String> =
137
+ listOf("textColor", "background", "backgroundTint", "tint")
138
+
139
+ override fun visitAttribute(context: XmlContext, attribute: Attr) {
140
+ val value = attribute.value
141
+ if (value.startsWith("#") || value.startsWith("@android:color/")) {
142
+ context.report(
143
+ issue = ISSUE,
144
+ location = context.getLocation(attribute),
145
+ message = "Use design token colors instead of direct color values (`$value`)"
146
+ )
147
+ }
148
+ }
149
+
150
+ companion object {
151
+ val ISSUE = Issue.create(
152
+ id = "DirectColorUsage",
153
+ briefDescription = "Use design token colors",
154
+ explanation = "Direct color values bypass the design system. Use theme attributes.",
155
+ category = Category.CORRECTNESS,
156
+ priority = 6,
157
+ severity = Severity.WARNING,
158
+ implementation = Implementation(
159
+ DirectColorUsageDetector::class.java,
160
+ Scope.RESOURCE_FILE_SCOPE
161
+ )
162
+ )
163
+ }
164
+ }
165
+
166
+ // ✅ Detect GlobalScope usage
167
+ class GlobalScopeDetector : Detector(), Detector.UastScanners {
168
+
169
+ override fun getApplicableReferenceNames(): List<String> = listOf("GlobalScope")
170
+
171
+ override fun visitReference(
172
+ context: JavaContext,
173
+ reference: UReferenceExpression,
174
+ referenced: PsiElement
175
+ ) {
176
+ context.report(
177
+ issue = ISSUE,
178
+ scope = reference,
179
+ location = context.getLocation(reference),
180
+ message = "Avoid `GlobalScope` — use `viewModelScope`, `lifecycleScope`, or an injected scope instead"
181
+ )
182
+ }
183
+
184
+ companion object {
185
+ val ISSUE = Issue.create(
186
+ id = "GlobalScopeUsage",
187
+ briefDescription = "Avoid GlobalScope",
188
+ explanation = "GlobalScope creates unstructured coroutines that leak and can't be cancelled.",
189
+ category = Category.CORRECTNESS,
190
+ priority = 8,
191
+ severity = Severity.ERROR,
192
+ implementation = Implementation(
193
+ GlobalScopeDetector::class.java,
194
+ Scope.JAVA_FILE_SCOPE
195
+ )
196
+ )
197
+ }
198
+ }
199
+ ```
200
+
201
+ ---
202
+
203
+ ## Testing Lint Rules
204
+
205
+ ```kotlin
206
+ // ✅ Test a custom lint rule
207
+ class ForbiddenCallDetectorTest {
208
+
209
+ @Test
210
+ fun `detects printStackTrace call`() {
211
+ lint()
212
+ .files(
213
+ kotlin("""
214
+ fun example() {
215
+ Exception("test").printStackTrace()
216
+ }
217
+ """).indented()
218
+ )
219
+ .issues(ForbiddenCallDetector.ISSUE)
220
+ .run()
221
+ .expect("""
222
+ src/test.kt:2: Error: Use structured logging instead of `printStackTrace()` [ForbiddenPrintStackTrace]
223
+ Exception("test").printStackTrace()
224
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
225
+ 1 errors, 0 warnings
226
+ """.trimIndent())
227
+ }
228
+
229
+ @Test
230
+ fun `clean code passes`() {
231
+ lint()
232
+ .files(
233
+ kotlin("""
234
+ fun example() {
235
+ Logger.e("Something failed")
236
+ }
237
+ """).indented()
238
+ )
239
+ .issues(ForbiddenCallDetector.ISSUE)
240
+ .run()
241
+ .expectClean()
242
+ }
243
+ }
244
+ ```
245
+
246
+ ---
247
+
248
+ ## Suppressing Rules
249
+
250
+ ```kotlin
251
+ // ✅ Suppress per call site when genuinely needed
252
+ @SuppressLint("GlobalScopeUsage")
253
+ fun legacyCode() {
254
+ GlobalScope.launch { /* unavoidable */ }
255
+ }
256
+
257
+ // ✅ Suppress in lint.xml (project-wide baseline)
258
+ // lint.xml
259
+ <lint>
260
+ <issue id="GlobalScopeUsage" severity="ignore">
261
+ <ignore path="src/legacy/**" />
262
+ </issue>
263
+ </lint>
264
+ ```
265
+
266
+ ---
267
+
268
+ ## Anti-Patterns
269
+
270
+ - Writing rules that are too strict — rules with too many false positives get suppressed everywhere
271
+ - Not writing tests for lint rules — untested rules break silently on API changes
272
+ - One giant detector class — one detector per concern
273
+ - Severity `ERROR` for style issues — use `WARNING`; save `ERROR` for correctness issues
274
+ - Forgetting to register the rule in `IssueRegistry` — rule silently never runs
275
+
276
+ ---
277
+
278
+ ## Related Skills
279
+ - `detekt` — static analysis for Kotlin code style
280
+ - `gradle` — integrating lint into the build pipeline
281
+ - `convention-plugin` — sharing lint config across modules