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,315 @@
1
+ ---
2
+ name: glance
3
+ description: >
4
+ Jetpack Glance for building App Widgets with Compose-like API.
5
+ Load this skill when creating home screen or lock screen widgets,
6
+ updating widget content, or handling widget interactions.
7
+ ---
8
+
9
+ # Glance
10
+
11
+ ## Overview
12
+ Jetpack Glance provides a Compose-like API for building Android App Widgets. Instead of XML RemoteViews, Glance lets you write widget UI in a declarative style. Glance is built on top of RemoteViews internally — it translates Composable-like functions to RemoteViews at runtime.
13
+
14
+ ---
15
+
16
+ ## Core Principles
17
+
18
+ - Glance is **not Compose** — it only supports a subset of Compose-like APIs
19
+ - Widget UI runs in a **different process** — no direct state sharing with the app
20
+ - Use **GlanceStateDefinition** for widget state — not ViewModel or StateFlow
21
+ - Widget updates must be triggered explicitly — they don't auto-update on app state change
22
+ - Glance components are a **limited subset** — not all Compose components are available
23
+
24
+ ---
25
+
26
+ ## Setup
27
+
28
+ ```toml
29
+ [versions]
30
+ glance = "1.1.0"
31
+
32
+ [libraries]
33
+ glance-appwidget = { module = "androidx.glance:glance-appwidget", version.ref = "glance" }
34
+ glance-material3 = { module = "androidx.glance:glance-material3", version.ref = "glance" }
35
+ ```
36
+
37
+ ```kotlin
38
+ dependencies {
39
+ implementation(libs.glance.appwidget)
40
+ implementation(libs.glance.material3)
41
+ }
42
+ ```
43
+
44
+ ---
45
+
46
+ ## Widget Definition
47
+
48
+ ```kotlin
49
+ // ✅ Define the widget
50
+ class MyAppWidget : GlanceAppWidget() {
51
+
52
+ override val stateDefinition = MyWidgetStateDefinition
53
+
54
+ @Composable
55
+ override fun Content() {
56
+ val state = currentState<MyWidgetState>()
57
+ val context = LocalContext.current
58
+
59
+ GlanceTheme {
60
+ MyWidgetContent(
61
+ state = state,
62
+ onRefreshClick = actionRunCallback<RefreshAction>()
63
+ )
64
+ }
65
+ }
66
+ }
67
+
68
+ // ✅ Composable widget UI
69
+ @Composable
70
+ fun MyWidgetContent(
71
+ state: MyWidgetState,
72
+ onRefreshClick: Action
73
+ ) {
74
+ Column(
75
+ modifier = GlanceModifier
76
+ .fillMaxSize()
77
+ .background(GlanceTheme.colors.surface)
78
+ .padding(16.dp)
79
+ .cornerRadius(16.dp),
80
+ verticalAlignment = Alignment.CenterVertically
81
+ ) {
82
+ Text(
83
+ text = state.title,
84
+ style = TextStyle(
85
+ color = GlanceTheme.colors.onSurface,
86
+ fontSize = 16.sp,
87
+ fontWeight = FontWeight.Bold
88
+ )
89
+ )
90
+
91
+ Spacer(modifier = GlanceModifier.height(8.dp))
92
+
93
+ Text(
94
+ text = state.subtitle,
95
+ style = TextStyle(
96
+ color = GlanceTheme.colors.onSurface,
97
+ fontSize = 12.sp
98
+ )
99
+ )
100
+
101
+ Spacer(modifier = GlanceModifier.defaultWeight())
102
+
103
+ Button(
104
+ text = "Refresh",
105
+ onClick = onRefreshClick
106
+ )
107
+ }
108
+ }
109
+ ```
110
+
111
+ ---
112
+
113
+ ## Widget State
114
+
115
+ ```kotlin
116
+ // ✅ Define state model
117
+ data class MyWidgetState(
118
+ val title: String = "",
119
+ val subtitle: String = "",
120
+ val isLoading: Boolean = false
121
+ )
122
+
123
+ // ✅ State definition using DataStore
124
+ val MyWidgetStateDefinition = object : GlanceStateDefinition<MyWidgetState> {
125
+
126
+ override suspend fun getDataStore(
127
+ context: Context,
128
+ fileKey: String
129
+ ): DataStore<MyWidgetState> = DataStoreFactory.create(
130
+ serializer = MyWidgetStateSerializer,
131
+ produceFile = { context.dataStoreFile(fileKey) }
132
+ )
133
+
134
+ override fun getLocation(context: Context, fileKey: String): File =
135
+ context.dataStoreFile(fileKey)
136
+ }
137
+
138
+ // ✅ Serializer for widget state
139
+ object MyWidgetStateSerializer : Serializer<MyWidgetState> {
140
+ override val defaultValue = MyWidgetState()
141
+
142
+ override suspend fun readFrom(input: InputStream): MyWidgetState = try {
143
+ Json.decodeFromString(input.readBytes().decodeToString())
144
+ } catch (e: Exception) {
145
+ defaultValue
146
+ }
147
+
148
+ override suspend fun writeTo(t: MyWidgetState, output: OutputStream) {
149
+ output.write(Json.encodeToString(t).toByteArray())
150
+ }
151
+ }
152
+ ```
153
+
154
+ ---
155
+
156
+ ## Widget Receiver
157
+
158
+ ```kotlin
159
+ // ✅ GlanceAppWidgetReceiver — entry point for the widget
160
+ class MyAppWidgetReceiver : GlanceAppWidgetReceiver() {
161
+ override val glanceAppWidget = MyAppWidget()
162
+ }
163
+ ```
164
+
165
+ ```xml
166
+ <!-- AndroidManifest.xml -->
167
+ <receiver
168
+ android:name=".widget.MyAppWidgetReceiver"
169
+ android:exported="true">
170
+ <intent-filter>
171
+ <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
172
+ </intent-filter>
173
+ <meta-data
174
+ android:name="android.appwidget.provider"
175
+ android:resource="@xml/my_widget_info" />
176
+ </receiver>
177
+ ```
178
+
179
+ ```xml
180
+ <!-- res/xml/my_widget_info.xml -->
181
+ <appwidget-provider
182
+ xmlns:android="http://schemas.android.com/apk/res/android"
183
+ android:minWidth="250dp"
184
+ android:minHeight="110dp"
185
+ android:targetCellWidth="4"
186
+ android:targetCellHeight="2"
187
+ android:resizeMode="horizontal|vertical"
188
+ android:updatePeriodMillis="1800000"
189
+ android:widgetCategory="home_screen"
190
+ android:initialLayout="@layout/glance_default_loading_layout" />
191
+ ```
192
+
193
+ ---
194
+
195
+ ## Updating Widget State
196
+
197
+ ```kotlin
198
+ // ✅ Update widget from app — e.g., from ViewModel or Worker
199
+ suspend fun updateWidget(context: Context, newState: MyWidgetState) {
200
+ // Update state in all widget instances
201
+ GlanceAppWidgetManager(context)
202
+ .getGlanceIds(MyAppWidget::class.java)
203
+ .forEach { glanceId ->
204
+ updateAppWidgetState(context, MyWidgetStateDefinition, glanceId) {
205
+ newState
206
+ }
207
+ MyAppWidget().update(context, glanceId)
208
+ }
209
+ }
210
+
211
+ // ✅ Update from WorkManager for periodic refresh
212
+ class WidgetUpdateWorker(
213
+ context: Context,
214
+ params: WorkerParameters
215
+ ) : CoroutineWorker(context, params) {
216
+
217
+ override suspend fun doWork(): Result {
218
+ val data = repository.fetchLatestData()
219
+ updateWidget(
220
+ applicationContext,
221
+ MyWidgetState(title = data.title, subtitle = data.subtitle)
222
+ )
223
+ return Result.success()
224
+ }
225
+ }
226
+ ```
227
+
228
+ ---
229
+
230
+ ## Widget Actions
231
+
232
+ ```kotlin
233
+ // ✅ Handle button clicks via ActionCallback
234
+ class RefreshAction : ActionCallback {
235
+ override suspend fun onAction(
236
+ context: Context,
237
+ glanceId: GlanceId,
238
+ parameters: ActionParameters
239
+ ) {
240
+ // Update widget state on click
241
+ updateAppWidgetState(context, MyWidgetStateDefinition, glanceId) { state ->
242
+ state.copy(isLoading = true)
243
+ }
244
+ MyAppWidget().update(context, glanceId)
245
+
246
+ // Fetch new data
247
+ val newData = repository.fetchData()
248
+ updateAppWidgetState(context, MyWidgetStateDefinition, glanceId) {
249
+ MyWidgetState(title = newData.title, subtitle = newData.subtitle)
250
+ }
251
+ MyAppWidget().update(context, glanceId)
252
+ }
253
+ }
254
+
255
+ // ✅ Open app on widget click
256
+ val openAppAction = actionStartActivity<MainActivity>()
257
+
258
+ // ✅ Open specific screen via deep link
259
+ val openDetailAction = actionStartActivity(
260
+ Intent(context, MainActivity::class.java).apply {
261
+ putExtra("destination", "detail")
262
+ }
263
+ )
264
+ ```
265
+
266
+ ---
267
+
268
+ ## Glance Components Reference
269
+
270
+ ```kotlin
271
+ // ✅ Available layout components
272
+ Row { }
273
+ Column { }
274
+ Box { }
275
+ LazyColumn { } // for lists
276
+
277
+ // ✅ Available UI components
278
+ Text(text = "Hello")
279
+ Button(text = "Click", onClick = action)
280
+ Image(provider = ImageProvider(R.drawable.icon), contentDescription = null)
281
+ CircularProgressIndicator()
282
+ LinearProgressIndicator(progress = 0.5f)
283
+ Spacer(modifier = GlanceModifier.height(8.dp))
284
+ Switch(checked = true, onCheckedChange = action)
285
+ CheckBox(checked = false, onCheckedChange = action)
286
+
287
+ // ✅ GlanceModifier (not Compose Modifier)
288
+ GlanceModifier
289
+ .fillMaxSize()
290
+ .fillMaxWidth()
291
+ .wrapContentSize()
292
+ .padding(16.dp)
293
+ .background(Color.White)
294
+ .clickable(onClick = action)
295
+ .cornerRadius(12.dp)
296
+ ```
297
+
298
+ ---
299
+
300
+ ## Anti-Patterns
301
+
302
+ - Using Compose components not available in Glance — runtime crash
303
+ - Sharing ViewModel state directly with widget — different processes
304
+ - Not calling `widget.update()` after state change — UI stays stale
305
+ - Complex animations in widgets — not supported, RemoteViews limitation
306
+ - Making widgets too complex — widgets should show summary, not replace the app
307
+ - Not handling widget resize — use `SizeMode.Responsive` for multiple sizes
308
+
309
+ ---
310
+
311
+ ## Related Skills
312
+ - `compose` — Glance has similar but different API
313
+ - `datastore` — widget state persistence
314
+ - `workmanager` — periodic widget updates
315
+ - `app-widget` — traditional RemoteViews-based widgets
@@ -0,0 +1,201 @@
1
+ ---
2
+ name: configuration
3
+ description: >
4
+ Android Configuration changes handling — screen rotation, locale change,
5
+ dark mode, font scale, and other runtime configuration events.
6
+ Load this skill when handling configuration changes, deciding what to retain
7
+ across rotation, or supporting dynamic locale/theme switching.
8
+ ---
9
+
10
+ # Configuration
11
+
12
+ ## Overview
13
+
14
+ Android Configuration represents the current state of the device — screen orientation, locale, font scale, night mode, and more. When configuration changes, Android destroys and recreates the Activity by default. Proper handling ensures seamless UX without data loss or memory leaks.
15
+
16
+ ---
17
+
18
+ ## Core Principles
19
+
20
+ - Never store UI state in Activity — use ViewModel to survive configuration changes
21
+ - Never use `android:configChanges` to avoid recreating Activity unless absolutely necessary
22
+ - Handle configuration-dependent resources via the resource qualifier system — not in code
23
+ - Use `SavedStateHandle` for state that must survive both configuration change and process death
24
+ - Test rotation and locale change explicitly — they expose the most lifecycle bugs
25
+
26
+ ---
27
+
28
+ ## What Happens on Configuration Change
29
+
30
+ ```
31
+ User rotates screen
32
+ → Activity.onPause()
33
+ → Activity.onStop()
34
+ → Activity.onDestroy() ← Activity is destroyed
35
+ → ViewModel stays alive ← ViewModel is NOT destroyed
36
+ → new Activity.onCreate()
37
+ → new Activity.onStart()
38
+ → new Activity.onResume()
39
+ ```
40
+
41
+ ---
42
+
43
+ ## ViewModel — Surviving Configuration Change
44
+
45
+ ```kotlin
46
+ // ✅ ViewModel survives rotation — store all UI state here
47
+ class UserViewModel : ViewModel() {
48
+ private val _state = MutableStateFlow(UserUiState())
49
+ val state: StateFlow<UserUiState> = _state.asStateFlow()
50
+ }
51
+
52
+ // ✅ Activity/Fragment — just observe, never store state locally
53
+ class UserFragment : Fragment() {
54
+ private val viewModel: UserViewModel by viewModels()
55
+
56
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
57
+ viewLifecycleOwner.lifecycleScope.launch {
58
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
59
+ viewModel.state.collect { render(it) }
60
+ }
61
+ }
62
+ }
63
+ }
64
+ ```
65
+
66
+ ---
67
+
68
+ ## Detecting Configuration in Code
69
+
70
+ ```kotlin
71
+ // ✅ Read current configuration
72
+ val config = resources.configuration
73
+
74
+ // Night mode
75
+ val isNightMode = config.uiMode and Configuration.UI_MODE_NIGHT_MASK ==
76
+ Configuration.UI_MODE_NIGHT_YES
77
+
78
+ // Orientation
79
+ val isLandscape = config.orientation == Configuration.ORIENTATION_LANDSCAPE
80
+
81
+ // Locale
82
+ val locale = config.locales[0]
83
+
84
+ // Screen size class
85
+ val isTablet = config.smallestScreenWidthDp >= 600
86
+
87
+ // ✅ In Compose
88
+ val isSystemInDarkTheme = isSystemInDarkTheme()
89
+ val windowSizeClass = calculateWindowSizeClass(activity)
90
+ ```
91
+
92
+ ---
93
+
94
+ ## Dynamic Locale Change (API 33+)
95
+
96
+ ```kotlin
97
+ // ✅ API 33+ — use LocaleManager
98
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
99
+ getSystemService(LocaleManager::class.java)
100
+ .applicationLocales = LocaleList(Locale.forLanguageTag("fa"))
101
+ }
102
+
103
+ // ✅ API < 33 — use AppCompatDelegate
104
+ AppCompatDelegate.setApplicationLocales(
105
+ LocaleListCompat.forLanguageTags("fa")
106
+ )
107
+ ```
108
+
109
+ ---
110
+
111
+ ## Dynamic Dark Mode
112
+
113
+ ```kotlin
114
+ // ✅ Change theme at runtime
115
+ AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
116
+ AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
117
+ AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
118
+
119
+ // ✅ Persist preference and apply on app start
120
+ class ThemeManager(private val prefs: DataStore<Preferences>) {
121
+ suspend fun applyTheme() {
122
+ val isDark = prefs.data.first()[DARK_MODE_KEY] ?: false
123
+ AppCompatDelegate.setDefaultNightMode(
124
+ if (isDark) AppCompatDelegate.MODE_NIGHT_YES
125
+ else AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
126
+ )
127
+ }
128
+ }
129
+ ```
130
+
131
+ ---
132
+
133
+ ## configChanges — When to Use
134
+
135
+ ```xml
136
+ <!-- ✅ Only for cases where recreation is genuinely harmful -->
137
+ <!-- Example: video player, camera, map — recreation causes flicker/reset -->
138
+ <activity
139
+ android:name=".PlayerActivity"
140
+ android:configChanges="orientation|screenSize|keyboardHidden" />
141
+ ```
142
+
143
+ ```kotlin
144
+ // ✅ Handle manually when configChanges is declared
145
+ override fun onConfigurationChanged(newConfig: Configuration) {
146
+ super.onConfigurationChanged(newConfig)
147
+ if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
148
+ adjustLayoutForLandscape()
149
+ }
150
+ }
151
+
152
+ // ❌ Don't use configChanges just to avoid writing proper ViewModel code
153
+ ```
154
+
155
+ ---
156
+
157
+ ## Window Size Classes (Adaptive UI)
158
+
159
+ ```kotlin
160
+ // ✅ Use WindowSizeClass for adaptive layouts
161
+ @Composable
162
+ fun AdaptiveScreen(windowSizeClass: WindowSizeClass) {
163
+ when (windowSizeClass.widthSizeClass) {
164
+ WindowWidthSizeClass.Compact -> PhoneLayout()
165
+ WindowWidthSizeClass.Medium -> TabletLayout()
166
+ WindowWidthSizeClass.Expanded -> DesktopLayout()
167
+ }
168
+ }
169
+
170
+ // Setup in Activity
171
+ class MainActivity : ComponentActivity() {
172
+ override fun onCreate(savedInstanceState: Bundle?) {
173
+ super.onCreate(savedInstanceState)
174
+ val windowSizeClass = calculateWindowSizeClass(this)
175
+ setContent {
176
+ AdaptiveScreen(windowSizeClass)
177
+ }
178
+ }
179
+ }
180
+ ```
181
+
182
+ ---
183
+
184
+ ## Anti-Patterns
185
+
186
+ - Storing UI state in Activity fields — lost on rotation
187
+ - Using `android:configChanges` to avoid ViewModel — just delays the problem
188
+ - Reading configuration in ViewModel — ViewModel should be config-independent
189
+ - Hardcoding layout logic for orientation in code instead of resource qualifiers
190
+ - Not testing rotation — the most common source of state loss bugs
191
+ - Using `onRetainNonConfigurationInstance()` — replaced by ViewModel
192
+
193
+ ---
194
+
195
+ ## Related Skills
196
+
197
+ - `savedstatehandle` — persisting state across process death
198
+ - `lifecycle` — Activity/Fragment lifecycle during config change
199
+ - `viewmodel` — surviving configuration changes
200
+ - `resources` — configuration qualifiers in res/
201
+ - `adaptive-ui` — WindowSizeClass and responsive layouts