<!-- skill: Android Native | version: 1.0.0 | description: Build native Android apps with Kotlin, Compose, Room and Play release | category: Mobile -->
## Instructions
- Kotlin first: null-safety (`?.`/`?:`), `data class` models, coroutines + `Flow` for async (never blocking main thread), `sealed` hierarchies for UI state (`Loading/Success/Error`).
- Jetpack Compose basics: `@Composable` functions, `remember`/`mutableStateOf`/`collectAsState`, `Scaffold + TopAppBar + NavHost`, `LazyColumn` for lists, Material 3 theming (`MaterialTheme.colorScheme`).
- Navigation: `NavHost(navController, startDestination)` + typed routes (`composable("detail/{id}")` + `navArgument`); single-Activity architecture, screens as composables.
- Activities: one `MainActivity : ComponentActivity` with `setContent {}`; config changes via ViewModel, never `android:configChanges` hacks.
- Networking: Retrofit + OkHttp (logging interceptor in debug only) + Moshi/kotlinx.serialization; repository pattern mapping DTO→domain; `Result<T>` returns, errors to UI state.
- Local DB: Room (`@Entity/@Dao/@Database`), DAO returns `Flow<List<T>>`, migrations for every schema bump (`autoMigrations` where possible); DataStore for prefs, never bare SharedPreferences for new code.
- DI: Hilt (`@HiltAndroidApp`, `@HiltViewModel`, `@Inject`) to keep ViewModels testable; ViewModel holds `StateFlow<UiState>`, UI only collects.
- Gradle flavors: `free/paid` or `dev/prod` flavors with `applicationIdSuffix`, `buildConfigField` base URLs, separate `src/free/` resources; version in `versionCode/versionName` bumped per release.
- Permissions: declare in manifest, request at runtime with Accompanist/Activity-Result APIs, explain-before-ask for location/camera; handle permanent denial with settings deep-link.
- Signing: release keystore in `~/.keystore` (NOT in repo), `signingConfigs` reading env (`KEYSTORE_PASSWORD`), `minifyEnabled + shrinkResources` on release, R8 rules for Retrofit/Room.
- Play release checklist: versionCode bump, release notes per locale, feature graphic + screenshots (phone + 7" + 10"), content rating questionnaire, data-safety form, staged rollout 20%→100%.
- Testing: JUnit + Turbine for Flow, MockK for repos, Compose UI tests + Firebase Test Lab run before Play submit.
## Examples
- Notes app: Room `Note` entity + DAO Flow, `NotesViewModel`, Compose list + add screen, NavHost list→detail, DataStore theme pref.
- API client app: Retrofit `ApiService`, repository with `Result`, ViewModel StateFlow, Compose Loading/Error/Success, Hilt injection, dev/prod flavors.
- Play release: bump versionCode, build `bundleRelease`, upload to internal track, 20% staged rollout, promote after crash-free 48h.
## Workflows
1. Scaffold (Activity + theme + NavHost) → flavor/dimension setup.
2. Data layer: Retrofit service + Room DB + repository mapping.
3. ViewModel StateFlow + Compose screens per route → wire navigation.
4. Permissions + error/empty states + dark-mode + accessibility labels.
5. Release build (`bundleRelease`) → Test Lab → internal track dogfood.
6. Play submit: notes, graphics, rating, data-safety → staged rollout → monitor vitals.
## Templates
- Composable + ViewModel:
```kotlin
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import javax.inject.Inject
sealed interface NotesUiState {
    data object Loading : NotesUiState
    data class Success(val notes: List<Note>) : NotesUiState
    data class Error(val msg: String) : NotesUiState
}
@HiltViewModel
class NotesViewModel @Inject constructor(
    private val repo: NotesRepository
) : ViewModel() {
    private val _ui = MutableStateFlow<NotesUiState>(NotesUiState.Loading)
    val ui: StateFlow<NotesUiState> = _ui.asStateFlow()
    init { viewModelScope.launch {
        repo.notes().catch { _ui.value = NotesUiState.Error(it.message ?: "load failed") }
            .collect { _ui.value = NotesUiState.Success(it) }
    } }
}
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.hilt.navigation.compose.hiltViewModel
@Composable
fun NotesScreen(vm: NotesViewModel = hiltViewModel(), onOpen: (Long) -> Unit) {
    val ui by vm.ui.collectAsState()
    Scaffold(topBar = { TopAppBar(title = { Text("Notes") }) }) { pad ->
        when (val s = ui) {
            is NotesUiState.Loading -> CircularProgressIndicator()
            is NotesUiState.Error -> Text("Error: ${s.msg}")
            is NotesUiState.Success -> LazyColumn(contentPadding = pad) {
                items(s.notes, key = { it.id }) { n ->
                    ListItem(headlineContent = { Text(n.title) },
                        modifier = androidx.compose.foundation.clickable(
                            onClick = { onOpen(n.id) }))
                }
            }
        }
    }
}
```
- Retrofit + Room:
```kotlin
import retrofit2.http.GET
import retrofit2.http.Path
interface ApiService {
    @GET("notes/{id}") suspend fun note(@Path("id") id: Long): NoteDto
}
import androidx.room.*
@Dao interface NoteDao {
    @Query("SELECT * FROM notes ORDER BY updated DESC")
    fun observe(): kotlinx.coroutines.flow.Flow<List<Note>>
    @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun upsert(n: Note)
}
```
- Gradle flavors:
```groovy
flavorDimensions += "tier"
productFlavors {
    create("dev") { dimension = "tier"; applicationIdSuffix = ".dev"
        buildConfigField("String", "BASE_URL", "\"https://dev.api.example.com/\"") }
    create("prod") { dimension = "tier";
        buildConfigField("String", "BASE_URL", "\"https://api.example.com/\"") }
}
```
## Tools
- SARAH shell_run for `./gradlew assembleDevDebug/bundleProdRelease/test/lint`, adb install/logcat.
- SARAH file_write for screens/viewmodels/dao/service scaffolds; web_fetch for AndroidX docs.
## Advanced
- Perf: Baseline Profiles + Macrobenchmark, `remember` expensive lambdas, `key()` in lazy lists, Coil for images with disk cache; StrictMode in dev to catch main-thread I/O.
- WorkManager for background sync (constraints: charging/unmetered), exact alarms only with `SCHEDULE_EXACT_ALARM` justification for Play review.
- Security: EncryptedFile/DataStore for tokens, Play Integrity for tamper checks, no API keys in `strings.xml`.
## Documentation
- https://developer.android.com/jetpack/compose/documentation
- https://developer.android.com/training/data-storage/room
- https://square.github.io/retrofit/
- https://developer.android.com/studio/publish
- https://dagger.dev/hilt/
