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,182 @@
1
+ ---
2
+ name: app-widget
3
+ description: >
4
+ Home screen App Widget implementation in Android.
5
+ Load this skill when building home screen widgets, updating widget
6
+ content, handling widget interactions, using Glance for Compose-based
7
+ widgets, or configuring widget metadata.
8
+ ---
9
+
10
+ # App Widget
11
+
12
+ ## Overview
13
+ App Widgets are miniature app views displayed on the home screen or lock screen. They update periodically and respond to user interactions. Modern widgets are best built with **Glance** (Jetpack Compose-based API). Legacy widgets use `RemoteViews`.
14
+
15
+ ---
16
+
17
+ ## Core Principles
18
+
19
+ - Use **Glance** for new widgets — it provides a Compose-like API over RemoteViews
20
+ - Widgets run in a separate process — keep updates lightweight and fast
21
+ - Update widgets via `AppWidgetManager` — not direct state mutation
22
+ - Provide a `configure` activity if the widget needs user setup
23
+ - Minimize update frequency — excessive updates drain battery
24
+
25
+ ---
26
+
27
+ ## Glance Widget (Recommended)
28
+
29
+ ```kotlin
30
+ // ✅ Glance widget — Compose-like API
31
+ class StatsWidget : GlanceAppWidget() {
32
+
33
+ override suspend fun provideGlance(context: Context, id: GlanceId) {
34
+ // Load data before composing
35
+ val stats = statsRepository.getStats()
36
+
37
+ provideContent {
38
+ StatsWidgetContent(stats)
39
+ }
40
+ }
41
+ }
42
+
43
+ @Composable
44
+ fun StatsWidgetContent(stats: Stats) {
45
+ Column(
46
+ modifier = GlanceModifier
47
+ .fillMaxSize()
48
+ .background(GlanceTheme.colors.widgetBackground)
49
+ .padding(16.dp),
50
+ verticalAlignment = Alignment.CenterVertically
51
+ ) {
52
+ Text(
53
+ text = stats.title,
54
+ style = TextStyle(
55
+ color = GlanceTheme.colors.onSurface,
56
+ fontSize = 16.sp,
57
+ fontWeight = FontWeight.Bold
58
+ )
59
+ )
60
+ Spacer(modifier = GlanceModifier.height(8.dp))
61
+ Text(
62
+ text = stats.value,
63
+ style = TextStyle(
64
+ color = GlanceTheme.colors.primary,
65
+ fontSize = 32.sp
66
+ )
67
+ )
68
+ Button(
69
+ text = "Refresh",
70
+ onClick = actionRunCallback<RefreshAction>()
71
+ )
72
+ }
73
+ }
74
+ ```
75
+
76
+ ---
77
+
78
+ ## Glance Action
79
+
80
+ ```kotlin
81
+ // ✅ Handle widget button tap
82
+ class RefreshAction : ActionCallback {
83
+ override suspend fun onAction(
84
+ context: Context,
85
+ glanceId: GlanceId,
86
+ parameters: ActionParameters
87
+ ) {
88
+ // Trigger data refresh then update widget
89
+ statsRepository.refresh()
90
+ StatsWidget().update(context, glanceId)
91
+ }
92
+ }
93
+ ```
94
+
95
+ ---
96
+
97
+ ## Glance Widget Receiver
98
+
99
+ ```kotlin
100
+ // ✅ AppWidgetReceiver for Glance widgets
101
+ class StatsWidgetReceiver : GlanceAppWidgetReceiver() {
102
+ override val glanceAppWidget: GlanceAppWidget = StatsWidget()
103
+ }
104
+ ```
105
+
106
+ ---
107
+
108
+ ## Widget Metadata (XML)
109
+
110
+ ```xml
111
+ <!-- res/xml/stats_widget_info.xml -->
112
+ <appwidget-provider
113
+ xmlns:android="http://schemas.android.com/apk/res/android"
114
+ android:minWidth="180dp"
115
+ android:minHeight="110dp"
116
+ android:targetCellWidth="2"
117
+ android:targetCellHeight="2"
118
+ android:updatePeriodMillis="1800000"
119
+ android:initialLayout="@layout/widget_loading"
120
+ android:description="@string/widget_description"
121
+ android:previewImage="@drawable/widget_preview"
122
+ android:widgetCategory="home_screen" />
123
+ ```
124
+
125
+ ---
126
+
127
+ ## Manifest Registration
128
+
129
+ ```xml
130
+ <receiver
131
+ android:name=".widget.StatsWidgetReceiver"
132
+ android:exported="true">
133
+ <intent-filter>
134
+ <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
135
+ </intent-filter>
136
+ <meta-data
137
+ android:name="android.appwidget.provider"
138
+ android:resource="@xml/stats_widget_info" />
139
+ </receiver>
140
+ ```
141
+
142
+ ---
143
+
144
+ ## Updating Widget from App
145
+
146
+ ```kotlin
147
+ // ✅ Update all instances of a widget
148
+ suspend fun updateAllWidgets(context: Context) {
149
+ val manager = GlanceAppWidgetManager(context)
150
+ val glanceIds = manager.getGlanceIds(StatsWidget::class.java)
151
+ glanceIds.forEach { glanceId ->
152
+ StatsWidget().update(context, glanceId)
153
+ }
154
+ }
155
+
156
+ // ✅ Trigger update from WorkManager for periodic refresh
157
+ class WidgetUpdateWorker(context: Context, params: WorkerParameters) :
158
+ CoroutineWorker(context, params) {
159
+ override suspend fun doWork(): Result {
160
+ updateAllWidgets(applicationContext)
161
+ return Result.success()
162
+ }
163
+ }
164
+ ```
165
+
166
+ ---
167
+
168
+ ## Anti-Patterns
169
+
170
+ - Doing heavy work directly in `provideGlance` on the main thread — use `withContext(Dispatchers.IO)`
171
+ - Setting `updatePeriodMillis` too low — minimum enforced is 30 minutes
172
+ - Using `RemoteViews` for new widgets when Glance is available — harder to maintain
173
+ - Not providing a preview image — widget looks broken in the picker
174
+ - Keeping large bitmaps in widget state — causes TransactionTooLargeException
175
+
176
+ ---
177
+
178
+ ## Related Skills
179
+ - `glance` — detailed Glance API patterns
180
+ - `workmanager` — scheduling periodic widget updates
181
+ - `notification` — alternative for time-sensitive updates
182
+ - `compose` — Compose fundamentals that inform Glance's API
@@ -0,0 +1,187 @@
1
+ ---
2
+ name: deep-link
3
+ description: >
4
+ Deep link handling at the Android system level.
5
+ Load this skill when configuring intent filters, handling incoming URIs
6
+ in the Activity, setting up App Links with digital asset links,
7
+ or processing deep links from notifications and external apps.
8
+ ---
9
+
10
+ # Deep Link
11
+
12
+ ## Overview
13
+ Deep links allow external sources — browsers, other apps, notifications — to open a specific screen inside your app via a URI. Android supports two types: custom scheme deep links (`myapp://`) and App Links (`https://`). App Links require domain verification and are more secure.
14
+
15
+ ---
16
+
17
+ ## Core Principles
18
+
19
+ - Prefer **App Links** (HTTPS) over custom schemes — they can't be intercepted by other apps
20
+ - Verify App Links with `assetlinks.json` hosted on your domain
21
+ - Parse the URI in the Activity and delegate to the navigation system — don't handle in the manifest
22
+ - Validate all incoming URI parameters — treat them as untrusted input
23
+ - Test deep links with `adb` before release
24
+
25
+ ---
26
+
27
+ ## Manifest Intent Filters
28
+
29
+ ```xml
30
+ <!-- AndroidManifest.xml -->
31
+ <activity
32
+ android:name=".MainActivity"
33
+ android:exported="true"
34
+ android:launchMode="singleTask">
35
+
36
+ <!-- App Link (HTTPS — requires domain verification) -->
37
+ <intent-filter android:autoVerify="true">
38
+ <action android:name="android.intent.action.VIEW" />
39
+ <category android:name="android.intent.category.DEFAULT" />
40
+ <category android:name="android.intent.category.BROWSABLE" />
41
+ <data
42
+ android:scheme="https"
43
+ android:host="example.com"
44
+ android:pathPrefix="/users" />
45
+ </intent-filter>
46
+
47
+ <!-- Custom scheme (no verification required) -->
48
+ <intent-filter>
49
+ <action android:name="android.intent.action.VIEW" />
50
+ <category android:name="android.intent.category.DEFAULT" />
51
+ <category android:name="android.intent.category.BROWSABLE" />
52
+ <data android:scheme="myapp" />
53
+ </intent-filter>
54
+
55
+ </activity>
56
+ ```
57
+
58
+ ---
59
+
60
+ ## App Links Verification (assetlinks.json)
61
+
62
+ ```json
63
+ // Hosted at: https://example.com/.well-known/assetlinks.json
64
+ [{
65
+ "relation": ["delegate_permission/common.handle_all_urls"],
66
+ "target": {
67
+ "namespace": "android_app",
68
+ "package_name": "com.example.app",
69
+ "sha256_cert_fingerprints": [
70
+ "AA:BB:CC:DD:..."
71
+ ]
72
+ }
73
+ }]
74
+ ```
75
+
76
+ ```bash
77
+ # Get your app's SHA-256 fingerprint
78
+ keytool -list -v -keystore release.keystore -alias my_alias
79
+ ```
80
+
81
+ ---
82
+
83
+ ## Handling the Deep Link in Activity
84
+
85
+ ```kotlin
86
+ // ✅ Navigation Compose handles deep links automatically
87
+ // Just ensure your NavHost is set up with deep links on destinations (see deep-navigation skill)
88
+
89
+ // ✅ Manual handling if needed
90
+ class MainActivity : ComponentActivity() {
91
+ override fun onCreate(savedInstanceState: Bundle?) {
92
+ super.onCreate(savedInstanceState)
93
+ handleDeepLinkIntent(intent)
94
+ setContent { AppNavHost() }
95
+ }
96
+
97
+ override fun onNewIntent(intent: Intent) {
98
+ super.onNewIntent(intent)
99
+ handleDeepLinkIntent(intent)
100
+ }
101
+
102
+ private fun handleDeepLinkIntent(intent: Intent?) {
103
+ val uri = intent?.data ?: return
104
+ // Navigation Compose picks this up automatically via NavHost
105
+ // Manual parsing only needed outside of Navigation Compose
106
+ }
107
+ }
108
+ ```
109
+
110
+ ---
111
+
112
+ ## URI Validation
113
+
114
+ ```kotlin
115
+ // ✅ Validate and sanitize deep link parameters
116
+ fun parseUserDeepLink(uri: Uri): String? {
117
+ if (uri.host != "example.com") return null
118
+ if (!uri.path.orEmpty().startsWith("/users/")) return null
119
+
120
+ val userId = uri.lastPathSegment ?: return null
121
+ if (userId.isBlank() || userId.length > 64) return null
122
+ if (!userId.matches(Regex("[a-zA-Z0-9_-]+"))) return null
123
+
124
+ return userId
125
+ }
126
+ ```
127
+
128
+ ---
129
+
130
+ ## Testing Deep Links
131
+
132
+ ```bash
133
+ # Test App Link
134
+ adb shell am start \
135
+ -W -a android.intent.action.VIEW \
136
+ -d "https://example.com/users/123" \
137
+ com.example.app
138
+
139
+ # Test custom scheme
140
+ adb shell am start \
141
+ -W -a android.intent.action.VIEW \
142
+ -d "myapp://users/123" \
143
+ com.example.app
144
+
145
+ # Verify App Link association
146
+ adb shell pm get-app-links com.example.app
147
+ ```
148
+
149
+ ---
150
+
151
+ ## Deep Link from Notification
152
+
153
+ ```kotlin
154
+ // ✅ Build notification with deep link PendingIntent
155
+ fun buildDeepLinkPendingIntent(context: Context, userId: String): PendingIntent {
156
+ val deepLinkUri = Uri.parse("https://example.com/users/$userId")
157
+ val intent = Intent(Intent.ACTION_VIEW, deepLinkUri).apply {
158
+ setPackage(context.packageName)
159
+ flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
160
+ }
161
+ return TaskStackBuilder.create(context).run {
162
+ addNextIntentWithParentStack(intent)
163
+ getPendingIntent(
164
+ userId.hashCode(),
165
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
166
+ )!!
167
+ }
168
+ }
169
+ ```
170
+
171
+ ---
172
+
173
+ ## Anti-Patterns
174
+
175
+ - Using only custom scheme deep links for sensitive actions — other apps can register the same scheme
176
+ - Not verifying App Links — browser asks user to choose the app instead of opening directly
177
+ - Not validating URI parameters — path traversal or injection via malicious links
178
+ - Not handling `onNewIntent` — deep links sent to an already-running activity are ignored
179
+ - Trusting all incoming URIs without validation — treat as untrusted user input
180
+
181
+ ---
182
+
183
+ ## Related Skills
184
+ - `deep-navigation` — wiring deep links into Navigation Compose destinations
185
+ - `notification` — launching deep links from notifications
186
+ - `manifest` — intent filter configuration
187
+ - `nested-navigation` — deep linking into nested navigation graphs
@@ -0,0 +1,212 @@
1
+ ---
2
+ name: foreground-service
3
+ description: >
4
+ Foreground Service implementation in Android.
5
+ Load this skill when running long-running user-visible operations like
6
+ media playback, file upload/download, location tracking, or any task
7
+ that must run while the app is in background with a visible notification.
8
+ ---
9
+
10
+ # Foreground Service
11
+
12
+ ## Overview
13
+ A Foreground Service runs in the background while showing a persistent notification. It survives app backgrounding and is used for operations the user is actively aware of — music playback, navigation, ongoing uploads. Since Android 14, foreground service types must be declared explicitly.
14
+
15
+ ---
16
+
17
+ ## Core Principles
18
+
19
+ - Always show a **meaningful notification** — it's required and user-facing
20
+ - Declare the correct **foreground service type** in the manifest
21
+ - Stop the service when the work is done — `stopSelf()` or `stopForeground()`
22
+ - Use `CoroutineScope` inside the service for async work
23
+ - Prefer WorkManager for deferrable work — ForegroundService is for user-visible ongoing tasks
24
+
25
+ ---
26
+
27
+ ## Manifest Declaration
28
+
29
+ ```xml
30
+ <!-- AndroidManifest.xml -->
31
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
32
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
33
+ <!-- Add the type matching your use case -->
34
+
35
+ <application>
36
+ <service
37
+ android:name=".service.UploadService"
38
+ android:foregroundServiceType="dataSync"
39
+ android:exported="false" />
40
+ </application>
41
+ ```
42
+
43
+ ---
44
+
45
+ ## Service Types Reference
46
+
47
+ | Type | Use Case |
48
+ |------|----------|
49
+ | `dataSync` | Upload / download / sync |
50
+ | `mediaPlayback` | Music / podcast playback |
51
+ | `location` | Navigation, location tracking |
52
+ | `camera` | Video recording |
53
+ | `microphone` | Audio recording |
54
+ | `connectedDevice` | Bluetooth device communication |
55
+
56
+ ---
57
+
58
+ ## Basic Foreground Service
59
+
60
+ ```kotlin
61
+ // ✅ Foreground service with coroutine scope
62
+ class UploadService : Service() {
63
+
64
+ private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
65
+ private var uploadJob: Job? = null
66
+
67
+ override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
68
+ when (intent?.action) {
69
+ ACTION_START -> {
70
+ val fileUri = intent.getStringExtra(EXTRA_FILE_URI) ?: run {
71
+ stopSelf()
72
+ return START_NOT_STICKY
73
+ }
74
+ startForegroundWithNotification()
75
+ startUpload(fileUri)
76
+ }
77
+ ACTION_CANCEL -> cancelUpload()
78
+ }
79
+ return START_NOT_STICKY
80
+ }
81
+
82
+ private fun startForegroundWithNotification() {
83
+ val notification = buildNotification("Starting upload…")
84
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
85
+ startForeground(NOTIFICATION_ID, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC)
86
+ } else {
87
+ startForeground(NOTIFICATION_ID, notification)
88
+ }
89
+ }
90
+
91
+ private fun startUpload(fileUri: String) {
92
+ uploadJob = scope.launch {
93
+ try {
94
+ uploadRepository.upload(Uri.parse(fileUri)) { progress ->
95
+ updateNotification("Uploading… $progress%")
96
+ }
97
+ updateNotification("Upload complete")
98
+ delay(2_000)
99
+ } catch (e: CancellationException) {
100
+ updateNotification("Upload cancelled")
101
+ } catch (e: Exception) {
102
+ updateNotification("Upload failed: ${e.message}")
103
+ } finally {
104
+ stopForeground(STOP_FOREGROUND_REMOVE)
105
+ stopSelf()
106
+ }
107
+ }
108
+ }
109
+
110
+ private fun cancelUpload() {
111
+ uploadJob?.cancel()
112
+ }
113
+
114
+ override fun onDestroy() {
115
+ scope.cancel()
116
+ super.onDestroy()
117
+ }
118
+
119
+ override fun onBind(intent: Intent?): IBinder? = null
120
+
121
+ private fun buildNotification(text: String): Notification {
122
+ createNotificationChannel()
123
+ return NotificationCompat.Builder(this, CHANNEL_ID)
124
+ .setContentTitle("File Upload")
125
+ .setContentText(text)
126
+ .setSmallIcon(R.drawable.ic_upload)
127
+ .setOngoing(true)
128
+ .build()
129
+ }
130
+
131
+ private fun updateNotification(text: String) {
132
+ val manager = getSystemService(NotificationManager::class.java)
133
+ manager.notify(NOTIFICATION_ID, buildNotification(text))
134
+ }
135
+
136
+ private fun createNotificationChannel() {
137
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
138
+ val channel = NotificationChannel(
139
+ CHANNEL_ID, "Upload", NotificationManager.IMPORTANCE_LOW
140
+ )
141
+ getSystemService(NotificationManager::class.java).createNotificationChannel(channel)
142
+ }
143
+ }
144
+
145
+ companion object {
146
+ const val ACTION_START = "action_start"
147
+ const val ACTION_CANCEL = "action_cancel"
148
+ const val EXTRA_FILE_URI = "file_uri"
149
+ private const val NOTIFICATION_ID = 1001
150
+ private const val CHANNEL_ID = "upload_channel"
151
+
152
+ fun startIntent(context: Context, fileUri: Uri): Intent =
153
+ Intent(context, UploadService::class.java).apply {
154
+ action = ACTION_START
155
+ putExtra(EXTRA_FILE_URI, fileUri.toString())
156
+ }
157
+
158
+ fun cancelIntent(context: Context): Intent =
159
+ Intent(context, UploadService::class.java).apply {
160
+ action = ACTION_CANCEL
161
+ }
162
+ }
163
+ }
164
+ ```
165
+
166
+ ---
167
+
168
+ ## Starting the Service
169
+
170
+ ```kotlin
171
+ // ✅ Start from ViewModel via event
172
+ class UploadViewModel : ViewModel() {
173
+ private val _events = Channel<UploadEvent>(Channel.BUFFERED)
174
+ val events = _events.receiveAsFlow()
175
+
176
+ fun onUploadClick(fileUri: Uri) {
177
+ viewModelScope.launch {
178
+ _events.send(UploadEvent.StartService(fileUri))
179
+ }
180
+ }
181
+ }
182
+
183
+ // ✅ Handle in composable / activity
184
+ LaunchedEffect(Unit) {
185
+ viewModel.events.collect { event ->
186
+ when (event) {
187
+ is UploadEvent.StartService -> {
188
+ val intent = UploadService.startIntent(context, event.fileUri)
189
+ context.startForegroundService(intent)
190
+ }
191
+ }
192
+ }
193
+ }
194
+ ```
195
+
196
+ ---
197
+
198
+ ## Anti-Patterns
199
+
200
+ - Starting a ForegroundService for short tasks — use coroutines or WorkManager
201
+ - Not calling `stopSelf()` when work completes — service stays alive indefinitely
202
+ - Missing `foregroundServiceType` in manifest on Android 10+ — SecurityException
203
+ - Doing heavy work on the main thread inside `onStartCommand` — use a coroutine scope
204
+ - Not cancelling the coroutine scope in `onDestroy` — coroutine leak
205
+
206
+ ---
207
+
208
+ ## Related Skills
209
+ - `workmanager` — deferrable background work without a notification requirement
210
+ - `notification` — building and updating notifications
211
+ - `background-processing` — choosing the right background mechanism
212
+ - `coroutine` — coroutine scope inside services