diff --git a/android-native-dev/SKILL.md b/android-native-dev/SKILL.md new file mode 100644 index 0000000..ddb3c28 --- /dev/null +++ b/android-native-dev/SKILL.md @@ -0,0 +1,883 @@ +--- +name: android-native-dev +description: Android native application development and UI design guide. Covers Material Design 3, Kotlin/Compose development, project configuration, accessibility, and build troubleshooting. Read this before Android native application development. +license: MIT +metadata: + version: "1.0.0" + category: mobile + sources: + - Material Design 3 Guidelines (material.io) + - Android Developer Documentation (developer.android.com) + - Google Play Quality Guidelines + - WCAG Accessibility Guidelines +--- + +## 1. Project Scenario Assessment + +Before starting development, assess the current project state: + +| Scenario | Characteristics | Approach | +|----------|-----------------|----------| +| **Empty Directory** | No files present | Full initialization required, including Gradle Wrapper | +| **Has Gradle Wrapper** | `gradlew` and `gradle/wrapper/` exist | Use `./gradlew` directly for builds | +| **Android Studio Project** | Complete project structure, may lack wrapper | Check wrapper, run `gradle wrapper` if needed | +| **Incomplete Project** | Partial files present | Check missing files, complete configuration | + +**Key Principles**: +- Before writing business logic, ensure `./gradlew assembleDebug` succeeds +- If `gradle.properties` is missing, create it first and configure AndroidX + +### 1.1 Required Files Checklist + +``` +MyApp/ +├── gradle.properties # Configure AndroidX and other settings +├── settings.gradle.kts +├── build.gradle.kts # Root level +├── gradle/wrapper/ +│ └── gradle-wrapper.properties +├── app/ +│ ├── build.gradle.kts # Module level +│ └── src/main/ +│ ├── AndroidManifest.xml +│ ├── java/com/example/myapp/ +│ │ └── MainActivity.kt +│ └── res/ +│ ├── values/ +│ │ ├── strings.xml +│ │ ├── colors.xml +│ │ └── themes.xml +│ └── mipmap-*/ # App icons +``` + +--- + +## 2. Project Configuration + +### 2.1 gradle.properties + +```properties +# Required configuration +android.useAndroidX=true +android.enableJetifier=true + +# Build optimization +org.gradle.parallel=true +kotlin.code.style=official + +# JVM memory settings (adjust based on project size) +# Small projects: 2048m, Medium: 4096m, Large: 8192m+ +# org.gradle.jvmargs=-Xmx4096m -Dfile.encoding=UTF-8 +``` + +> **Note**: If you encounter `OutOfMemoryError` during build, increase `-Xmx` value. Large projects with many dependencies may require 8GB or more. + +### 2.2 Dependency Declaration Standards + +```kotlin +dependencies { + // Use BOM to manage Compose versions + implementation(platform("androidx.compose:compose-bom:2024.02.00")) + implementation("androidx.compose.ui:ui") + implementation("androidx.compose.material3:material3") + + // Activity & ViewModel + implementation("androidx.activity:activity-compose:1.8.2") + implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.7.0") +} +``` + +### 2.3 Build Variants & Product Flavors + +Product Flavors allow you to create different versions of your app (e.g., free/paid, dev/staging/prod). + +**Configuration in app/build.gradle.kts**: + +```kotlin +android { + // Define flavor dimensions + flavorDimensions += "environment" + + productFlavors { + create("dev") { + dimension = "environment" + applicationIdSuffix = ".dev" + versionNameSuffix = "-dev" + + // Different config values per flavor + buildConfigField("String", "API_BASE_URL", "\"https://dev-api.example.com\"") + buildConfigField("Boolean", "ENABLE_LOGGING", "true") + + // Different resources + resValue("string", "app_name", "MyApp Dev") + } + + create("staging") { + dimension = "environment" + applicationIdSuffix = ".staging" + versionNameSuffix = "-staging" + + buildConfigField("String", "API_BASE_URL", "\"https://staging-api.example.com\"") + buildConfigField("Boolean", "ENABLE_LOGGING", "true") + resValue("string", "app_name", "MyApp Staging") + } + + create("prod") { + dimension = "environment" + // No suffix for production + + buildConfigField("String", "API_BASE_URL", "\"https://api.example.com\"") + buildConfigField("Boolean", "ENABLE_LOGGING", "false") + resValue("string", "app_name", "MyApp") + } + } + + buildTypes { + debug { + isDebuggable = true + isMinifyEnabled = false + } + release { + isDebuggable = false + isMinifyEnabled = true + proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") + } + } +} +``` + +**Build Variant Naming**: `{flavor}{BuildType}` → e.g., `devDebug`, `prodRelease` + +**Gradle Build Commands**: + +```bash +# List all available build variants +./gradlew tasks --group="build" + +# Build specific variant (flavor + buildType) +./gradlew assembleDevDebug # Dev flavor, Debug build +./gradlew assembleStagingDebug # Staging flavor, Debug build +./gradlew assembleProdRelease # Prod flavor, Release build + +# Build all variants of a specific flavor +./gradlew assembleDev # All Dev variants (debug + release) +./gradlew assembleProd # All Prod variants + +# Build all variants of a specific build type +./gradlew assembleDebug # All flavors, Debug build +./gradlew assembleRelease # All flavors, Release build + +# Install specific variant to device +./gradlew installDevDebug +./gradlew installProdRelease + +# Build and install in one command +./gradlew installDevDebug && adb shell am start -n com.example.myapp.dev/.MainActivity +``` + +**Access BuildConfig in Code**: + +> **Note**: Starting from AGP 8.0, `BuildConfig` is no longer generated by default. You must explicitly enable it in your `build.gradle.kts`: +> ```kotlin +> android { +> buildFeatures { +> buildConfig = true +> } +> } +> ``` + +```kotlin +// Use build config values in your code +val apiUrl = BuildConfig.API_BASE_URL +val isLoggingEnabled = BuildConfig.ENABLE_LOGGING + +if (BuildConfig.DEBUG) { + // Debug-only code +} +``` + +**Flavor-Specific Source Sets**: + +``` +app/src/ +├── main/ # Shared code for all flavors +├── dev/ # Dev-only code and resources +│ ├── java/ +│ └── res/ +├── staging/ # Staging-only code and resources +├── prod/ # Prod-only code and resources +├── debug/ # Debug build type code +└── release/ # Release build type code +``` + +**Multiple Flavor Dimensions** (e.g., environment + tier): + +```kotlin +android { + flavorDimensions += listOf("environment", "tier") + + productFlavors { + create("dev") { dimension = "environment" } + create("prod") { dimension = "environment" } + + create("free") { dimension = "tier" } + create("paid") { dimension = "tier" } + } +} +// Results in: devFreeDebug, devPaidDebug, prodFreeRelease, etc. +``` + +--- + +## 3. Kotlin Development Standards + +### 3.1 Naming Conventions + +| Type | Convention | Example | +|------|------------|---------| +| Class/Interface | PascalCase | `UserRepository`, `MainActivity` | +| Function/Variable | camelCase | `getUserName()`, `isLoading` | +| Constant | SCREAMING_SNAKE | `MAX_RETRY_COUNT` | +| Package | lowercase | `com.example.myapp` | +| Composable | PascalCase | `@Composable fun UserCard()` | + +### 3.2 Code Standards (Important) + +**Null Safety**: +```kotlin +// ❌ Avoid: Non-null assertion !! (may crash) +val name = user!!.name + +// ✅ Recommended: Safe call + default value +val name = user?.name ?: "Unknown" + +// ✅ Recommended: let handling +user?.let { processUser(it) } +``` + +**Exception Handling**: +```kotlin +// ❌ Avoid: Random try-catch in business layer swallowing exceptions +fun loadData() { + try { + val data = api.fetch() + } catch (e: Exception) { + // Swallowing exception, hard to debug + } +} + +// ✅ Recommended: Let exceptions propagate, handle at appropriate layer +suspend fun loadData(): Result { + return try { + Result.success(api.fetch()) + } catch (e: Exception) { + Result.failure(e) // Wrap and return, let caller decide handling + } +} + +// ✅ Recommended: Unified handling in ViewModel +viewModelScope.launch { + runCatching { repository.loadData() } + .onSuccess { _uiState.value = UiState.Success(it) } + .onFailure { _uiState.value = UiState.Error(it.message) } +} +``` + +### 3.3 Threading & Coroutines (Critical) + +**Thread Selection Principles**: + +| Operation Type | Thread | Description | +|----------------|--------|-------------| +| UI Updates | `Dispatchers.Main` | Update View, State, LiveData | +| Network Requests | `Dispatchers.IO` | HTTP calls, API requests | +| File I/O | `Dispatchers.IO` | Local storage, database operations | +| Compute Intensive | `Dispatchers.Default` | JSON parsing, sorting, encryption | + +**Correct Usage**: +```kotlin +// In ViewModel +viewModelScope.launch { + // Default Main thread, can update UI State + _uiState.value = UiState.Loading + + // Switch to IO thread for network request + val result = withContext(Dispatchers.IO) { + repository.fetchData() + } + + // Automatically returns to Main thread, update UI + _uiState.value = UiState.Success(result) +} + +// In Repository (suspend functions should be main-safe) +suspend fun fetchData(): Data = withContext(Dispatchers.IO) { + api.getData() +} +``` + +**Common Mistakes**: +```kotlin +// ❌ Wrong: Updating UI on IO thread +viewModelScope.launch(Dispatchers.IO) { + val data = api.fetch() + _uiState.value = data // Crash or warning! +} + +// ❌ Wrong: Executing time-consuming operation on Main thread +viewModelScope.launch { + val data = api.fetch() // Blocking main thread! ANR +} + +// ✅ Correct: Fetch on IO, update on Main +viewModelScope.launch { + val data = withContext(Dispatchers.IO) { api.fetch() } + _uiState.value = data +} +``` + +### 3.4 Visibility Rules + +```kotlin +// Default is public, declare explicitly when needed +class UserRepository { // public + private val cache = mutableMapOf() // Visible only within class + internal fun clearCache() {} // Visible only within module +} + +// data class properties are public by default, be careful when used across modules +data class User( + val id: String, // public + val name: String +) +``` + +### 3.5 Common Syntax Pitfalls + +```kotlin +// ❌ Wrong: Accessing uninitialized lateinit +class MyViewModel : ViewModel() { + lateinit var data: String + fun process() = data.length // May crash +} + +// ✅ Correct: Use nullable or default value +class MyViewModel : ViewModel() { + var data: String? = null + fun process() = data?.length ?: 0 +} + +// ❌ Wrong: Using return in lambda +list.forEach { item -> + if (item.isEmpty()) return // Returns from outer function! +} + +// ✅ Correct: Use return@forEach +list.forEach { item -> + if (item.isEmpty()) return@forEach +} +``` + +### 3.6 Server Response Data Class Fields Must Be Nullable + +```kotlin +// ❌ Wrong: Fields declared as non-null (server may not return them) +data class UserResponse( + val id: String = "", + val name: String = "", + val avatar: String = "" +) + +// ✅ Correct: All fields declared as nullable +data class UserResponse( + @SerializedName("id") + val id: String? = null, + @SerializedName("name") + val name: String? = null, + @SerializedName("avatar") + val avatar: String? = null +) +``` + +### 3.7 Lifecycle Resource Management + +```kotlin +// ❌ Wrong: Only adding Observer, not removing +class MyView : View { + override fun onAttachedToWindow() { + super.onAttachedToWindow() + activity?.lifecycle?.addObserver(this) + } + // Memory leak! +} + +// ✅ Correct: Paired add and remove +class MyView : View { + override fun onAttachedToWindow() { + super.onAttachedToWindow() + activity?.lifecycle?.addObserver(this) + } + + override fun onDetachedFromWindow() { + activity?.lifecycle?.removeObserver(this) + super.onDetachedFromWindow() + } +} +``` + +### 3.8 Logging Level Usage + +```kotlin +import android.util.Log + +// Info: Key checkpoints in normal flow +Log.i(TAG, "loadData: started, userId = $userId") + +// Warning: Abnormal but recoverable situations +Log.w(TAG, "loadData: cache miss, fallback to network") + +// Error: Failure/error situations +Log.e(TAG, "loadData failed: ${error.message}") +``` + +| Level | Use Case | +|-------|----------| +| `i` (Info) | Normal flow, method entry, key parameters | +| `w` (Warning) | Recoverable exceptions, fallback handling, null returns | +| `e` (Error) | Request failures, caught exceptions, unrecoverable errors | + +--- + +## 4. Jetpack Compose Standards + +### 4.1 @Composable Context Rules + +```kotlin +// ❌ Wrong: Calling Composable from non-Composable function +fun showError(message: String) { + Text(message) // Compile error! +} + +// ✅ Correct: Mark as @Composable +@Composable +fun ErrorMessage(message: String) { + Text(message) +} + +// ❌ Wrong: Using suspend outside LaunchedEffect +@Composable +fun MyScreen() { + val data = fetchData() // Error! +} + +// ✅ Correct: Use LaunchedEffect +@Composable +fun MyScreen() { + var data by remember { mutableStateOf(null) } + LaunchedEffect(Unit) { + data = fetchData() + } +} +``` + +### 4.2 State Management + +```kotlin +// Basic State +var count by remember { mutableStateOf(0) } + +// Derived State (avoid redundant computation) +val isEven by remember { derivedStateOf { count % 2 == 0 } } + +// Persist across recomposition (e.g., scroll position) +val scrollState = rememberScrollState() + +// State in ViewModel +class MyViewModel : ViewModel() { + private val _uiState = MutableStateFlow(UiState()) + val uiState: StateFlow = _uiState.asStateFlow() +} +``` + +### 4.3 Common Compose Mistakes + +```kotlin +// ❌ Wrong: Creating objects in Composable (created on every recomposition) +@Composable +fun MyScreen() { + val viewModel = MyViewModel() // Wrong! +} + +// ✅ Correct: Use viewModel() or remember +@Composable +fun MyScreen(viewModel: MyViewModel = viewModel()) { + // ... +} +``` + +--- + +## 5. Resources & Icons + +### 5.1 App Icon Requirements + +Must provide multi-resolution icons: + +| Directory | Size | Purpose | +|-----------|------|---------| +| mipmap-mdpi | 48x48 | Baseline | +| mipmap-hdpi | 72x72 | 1.5x | +| mipmap-xhdpi | 96x96 | 2x | +| mipmap-xxhdpi | 144x144 | 3x | +| mipmap-xxxhdpi | 192x192 | 4x | + +Recommended: Use Adaptive Icon (Android 8+): + +```xml + + + + + +``` + +### 5.2 Resource Naming Conventions + +| Type | Prefix | Example | +|------|--------|---------| +| Layout | layout_ | `layout_main.xml` | +| Image | ic_, img_, bg_ | `ic_user.png` | +| Color | color_ | `color_primary` | +| String | - | `app_name`, `btn_submit` | + +### 5.3 Avoid Android Reserved Names (Important) + +Variable names, resource IDs, colors, icons, and XML elements **must not** use Android reserved words or system resource names. Using reserved names causes build errors or resource conflicts. + +**Common Reserved Names to Avoid**: + +| Category | Reserved Names (Do NOT Use) | +|----------|----------------------------| +| Colors | `background`, `foreground`, `transparent`, `white`, `black` | +| Icons/Drawables | `icon`, `logo`, `image`, `drawable` | +| Views | `view`, `text`, `button`, `layout`, `container` | +| Attributes | `id`, `name`, `type`, `style`, `theme`, `color` | +| System | `app`, `android`, `content`, `data`, `action` | + +**Examples**: + +```xml + +#FFFFFF +#000000 + + +#FFFFFF +#000000 +``` + +```kotlin +// ❌ Wrong: Variable names conflict with system +val icon = R.drawable.my_icon +val background = Color.White + +// ✅ Correct: Use descriptive names +val appIcon = R.drawable.my_icon +val screenBackground = Color.White +``` + +```xml + + + + + +``` + +--- + +## 6. Build Error Diagnosis & Fixes + +### 6.1 Common Error Quick Reference + +| Error Keyword | Cause | Fix | +|---------------|-------|-----| +| `Unresolved reference` | Missing import or undefined | Check imports, verify dependencies | +| `Type mismatch` | Type incompatibility | Check parameter types, add conversion | +| `Cannot access` | Visibility issue | Check public/private/internal | +| `@Composable invocations` | Composable context error | Ensure caller is also @Composable | +| `Duplicate class` | Dependency conflict | Use `./gradlew dependencies` to investigate | +| `AAPT: error` | Resource file error | Check XML syntax and resource references | + +### 6.2 Fix Best Practices + +1. **Read the complete error message first**: Locate file and line number +2. **Check recent changes**: Problems usually in latest modifications +3. **Clean Build**: `./gradlew clean assembleDebug` +4. **Check dependency versions**: Version conflicts are common causes +5. **Refresh dependencies if needed**: Clear cache and rebuild + +### 6.3 Debugging Commands + +```bash +# Clean and build +./gradlew clean assembleDebug + +# View dependency tree (investigate conflicts) +./gradlew :app:dependencies + +# View detailed errors +./gradlew assembleDebug --stacktrace + +# Refresh dependencies +./gradlew --refresh-dependencies +``` + +--- + +## 7. Material Design 3 Guidelines + +Review Android UI files for compliance with Material Design 3 Guidelines and Android best practices. + +### Design Philosophy + +#### M3 Core Principles + +| Principle | Description | +|-----------|-------------| +| **Personal** | Dynamic color based on user preferences and wallpaper | +| **Adaptive** | Responsive across all screen sizes and form factors | +| **Expressive** | Bold colors and typography with personality | +| **Accessible** | Inclusive design for all users | + +#### M3 Expressive (Latest) + +The latest evolution adds emotion-driven UX through: +- Vibrant, dynamic colors +- Intuitive motion physics +- Adaptive components +- Flexible typography +- Contrasting shapes (35 new shape options) + +### App Style Selection + +**Critical Decision**: Match visual style to app category and target audience. + +| App Category | Visual Style | Key Characteristics | +|--------------|--------------|---------------------| +| Utility/Tool | Minimalist | Clean, efficient, neutral colors | +| Finance/Banking | Professional Trust | Conservative colors, security-focused | +| Health/Wellness | Calm & Natural | Soft colors, organic shapes | +| Kids (3-5) | Playful Simple | Bright colors, large targets (56dp+) | +| Kids (6-12) | Fun & Engaging | Vibrant, gamified feedback | +| Social/Entertainment | Expressive | Brand-driven, gesture-rich | +| Productivity | Clean & Focused | Minimal, high contrast | +| E-commerce | Conversion-focused | Clear CTAs, scannable | + +See [Design Style Guide](references/design-style-guide.md) for detailed style profiles. + +### Quick Reference: Key Specifications + +#### Color Contrast Requirements + +| Element | Minimum Ratio | +|---------|---------------| +| Body text | **4.5:1** | +| Large text (18sp+) | **3:1** | +| UI components | **3:1** | + +#### Touch Targets + +| Type | Size | +|------|------| +| Minimum | 48 × 48dp | +| Recommended (primary actions) | 56 × 56dp | +| Kids apps | 56dp+ | +| Spacing between targets | 8dp minimum | + +#### 8dp Grid System + +| Token | Value | Usage | +|-------|-------|-------| +| xs | 4dp | Icon padding | +| sm | 8dp | Tight spacing | +| md | 16dp | Default padding | +| lg | 24dp | Section spacing | +| xl | 32dp | Large gaps | +| xxl | 48dp | Screen margins | + +#### Typography Scale (Summary) + +| Category | Sizes | +|----------|-------| +| Display | 57sp, 45sp, 36sp | +| Headline | 32sp, 28sp, 24sp | +| Title | 22sp, 16sp, 14sp | +| Body | 16sp, 14sp, 12sp | +| Label | 14sp, 12sp, 11sp | + +#### Animation Duration + +| Type | Duration | +|------|----------| +| Micro (ripples) | 50-100ms | +| Short (simple) | 100-200ms | +| Medium (expand/collapse) | 200-300ms | +| Long (complex) | 300-500ms | + +#### Component Dimensions + +| Component | Height | Min Width | +|-----------|--------|-----------| +| Button | 40dp | 64dp | +| FAB | 56dp | 56dp | +| Text Field | 56dp | 280dp | +| App Bar | 64dp | - | +| Bottom Nav | 80dp | - | + +### Anti-Patterns (Must Avoid) + +#### UI Anti-Patterns +- More than 5 bottom navigation items +- Multiple FABs on same screen +- Touch targets smaller than 48dp +- Inconsistent spacing (non-8dp multiples) +- Missing dark theme support +- Text on colored backgrounds without contrast check + +#### Performance Anti-Patterns +- Startup time > 2 seconds without progress indicator +- Frame rate < 60 FPS (> 16ms per frame) +- Crash rate > 1.09% (Google Play threshold) +- ANR rate > 0.47% (Google Play threshold) + +#### Accessibility Anti-Patterns +- Missing contentDescription on interactive elements +- Element type in labels (e.g., "Save button" instead of "Save") +- Complex gestures in kids apps +- Text-only buttons for non-readers + +### Review Checklist + +- [ ] 8dp spacing grid compliance +- [ ] 48dp minimum touch targets +- [ ] Proper typography scale usage +- [ ] Color contrast compliance (4.5:1+ for text) +- [ ] Dark theme support +- [ ] contentDescription on all interactive elements +- [ ] Startup < 2 seconds or shows progress +- [ ] Visual style matches app category + +### Design References + +| Topic | Reference | +|-------|-----------| +| Colors, Typography, Spacing, Shapes | [Visual Design](references/visual-design.md) | +| Animation & Transitions | [Motion System](references/motion-system.md) | +| Accessibility Guidelines | [Accessibility](references/accessibility.md) | +| Large Screens & Foldables | [Adaptive Screens](references/adaptive-screens.md) | +| Android Vitals & Performance | [Performance & Stability](references/performance-stability.md) | +| Privacy & Security | [Privacy & Security](references/privacy-security.md) | +| Audio, Video, Notifications | [Functional Requirements](references/functional-requirements.md) | +| App Style by Category | [Design Style Guide](references/design-style-guide.md) | + +--- + +## 8. Testing + +> **Note**: Only add test dependencies when the user explicitly asks for testing. + +A well-tested Android app uses layered testing: fast local unit tests for logic, instrumentation tests for UI and integration, and Gradle Managed Devices to run emulators reproducibly on any machine — including CI. + +### 8.1 Test Dependencies + +Before adding test dependencies, inspect the project's existing versions to avoid conflicts: + +1. Check `gradle/libs.versions.toml` — if present, add test deps using the project's version catalog style +2. Check existing `build.gradle.kts` for already-pinned dependency versions +3. Match version families using the table below + +**Version Alignment Rules**: + +| Test Dependency | Must Align With | How to Check | +|----------------------------------------------|--------------------------------------------------|-----------------------------------------------------------------------| +| `kotlinx-coroutines-test` | Project's `kotlinx-coroutines-core` version | Search for `kotlinx-coroutines` in build files or version catalog | +| `compose-ui-test-junit4` | Project's Compose BOM or `compose-compiler` | Search for `compose-bom` or `compose.compiler` in build files | +| `espresso-*` | All Espresso artifacts must use the same version | Search for `espresso` in build files | +| `androidx.test:runner`, `rules`, `ext:junit` | Should use compatible AndroidX Test versions | Search for `androidx.test` in build files | +| `mockk` | Must support the project's Kotlin version | Check `kotlin` version in root `build.gradle.kts` or version catalog | + +**Dependencies Reference** — add only the groups you need: + +```kotlin +dependencies { + // --- Local unit tests (src/test/) --- + testImplementation("junit:junit:") // 4.13.2+ + testImplementation("org.robolectric:robolectric:") // 4.16.1+ + testImplementation("io.mockk:mockk:") // match Kotlin version + testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:") // match coroutines-core + testImplementation("androidx.arch.core:core-testing:") // InstantTaskExecutorRule for LiveData + testImplementation("app.cash.turbine:turbine:") // Flow/StateFlow testing + + // --- Instrumentation tests (src/androidTest/) --- + androidTestImplementation("androidx.test.ext:junit:") + androidTestImplementation("androidx.test:runner:") + androidTestImplementation("androidx.test:rules:") + androidTestImplementation("androidx.test.espresso:espresso-core:") + androidTestImplementation("androidx.test.espresso:espresso-contrib:") // RecyclerView, Drawer + androidTestImplementation("androidx.test.espresso:espresso-intents:") // Intent verification + androidTestImplementation("androidx.test.espresso:espresso-idling-resource:") + androidTestImplementation("androidx.test.uiautomator:uiautomator:") + + // --- Compose UI tests (only if project uses Compose) --- + androidTestImplementation("androidx.compose.ui:ui-test-junit4") // version from Compose BOM + debugImplementation("androidx.compose.ui:ui-test-manifest") // required for createComposeRule +} +``` + +> **Note**: If the project uses a Compose BOM, `ui-test-junit4` and `ui-test-manifest` don't need explicit versions — the BOM manages them. + +Enable Robolectric resource support in the `android` block: + +```kotlin +android { + testOptions { + unitTests.isIncludeAndroidResources = true // required for Robolectric + } +} +``` + +### 8.2 Testing by Layer + +| Layer | Location | Runs On | Speed | Use For | +|--------------------|--------------------|-------------------------|----------------------|--------------------------------------------------| +| Unit (JUnit) | `src/test/` | JVM | ~ms | ViewModels, repos, mappers, validators | +| Unit + Robolectric | `src/test/` | JVM + simulated Android | ~100ms | Code needing Context, resources, SharedPrefs | +| Compose UI (local) | `src/test/` | JVM + Robolectric | ~100ms | Composable rendering & interaction | +| Espresso | `src/androidTest/` | Device/Emulator | ~seconds | View-based UI flows, Intents, DB integration | +| Compose UI (device)| `src/androidTest/` | Device/Emulator | ~seconds | Full Compose UI flows with real rendering | +| UI Automator | `src/androidTest/` | Device/Emulator | ~seconds | System dialogs, notifications, multi-app | +| Managed Device | `src/androidTest/` | Gradle-managed AVD | ~minutes (first run) | CI, matrix testing across API levels | + +See [Testing](references/testing.md) for detailed examples, code patterns, and Gradle Managed Device configuration. + +### 8.3 Testing Commands + +```bash +# Local unit tests (fast, no emulator) +./gradlew test # all modules +./gradlew :app:testDebugUnitTest # app module, debug variant + +# Single test class +./gradlew :app:testDebugUnitTest --tests "com.example.myapp.CounterViewModelTest" + +# Instrumentation tests (requires device or managed device) +./gradlew connectedDebugAndroidTest # on connected device +./gradlew pixel6Api34DebugAndroidTest # on managed device + +# Both together +./gradlew test connectedDebugAndroidTest + +# Test with coverage report (JaCoCo) +./gradlew testDebugUnitTest jacocoTestReport +``` diff --git a/android-native-dev/references/accessibility.md b/android-native-dev/references/accessibility.md new file mode 100644 index 0000000..e1eba6b --- /dev/null +++ b/android-native-dev/references/accessibility.md @@ -0,0 +1,209 @@ +# Accessibility Guidelines + +Comprehensive accessibility requirements for Android applications. + +## Core Requirements + +### Minimum Standards + +| Requirement | Specification | +|-------------|---------------| +| Color contrast (text) | 4.5:1 minimum | +| Color contrast (large text) | 3:1 minimum | +| Color contrast (UI components) | 3:1 minimum | +| Touch targets | 48 × 48dp minimum | +| Content descriptions | All interactive elements | +| Focus indicators | Clearly visible | +| Screen reader support | Proper semantics | + +## Content Labels + +### contentDescription + +Use for non-text interactive elements. + +**When to use:** +- ImageView, ImageButton +- CheckBox, Switch (state description) +- Custom drawable views +- Icons that convey meaning + +**When NOT to use:** +- TextView (uses text content automatically) +- Decorative images (set to null) +- Elements with labelFor relationship + +### android:hint + +Use for editable text fields to show placeholder text. + +**Important**: Don't use contentDescription on EditText—it interferes with accessibility services. + +### android:labelFor + +Link labels to input fields by setting labelFor on the TextView to reference the EditText ID. + +## Label Best Practices + +### Do's + +| Practice | Example | +|----------|---------| +| Be concise | "Save" not "Click here to save" | +| Describe action/purpose | "Delete message" | +| Be unique in context | "Delete item 3" not just "Delete" | +| Update dynamically | "Pause" ↔ "Play" based on state | + +### Don'ts + +| Avoid | Reason | +|-------|--------| +| Include element type | TalkBack announces "button" automatically | +| Say "button", "image", etc. | Redundant with accessibility info | +| Use "click" or "tap" | Input method varies | +| Leave empty/generic | "Button" or "Image" is unhelpful | + +### Examples + +| Bad | Good | +|-----|------| +| "Save button" | "Save" | +| "Click here to submit" | "Submit" | +| "Image" | "Profile photo of John" | +| "Button 1" | "Add to cart" | + +## Focus and Navigation + +### Focus Groups + +Group related elements using `screenReaderFocusable="true"` on the container and `focusable="false"` on child elements. TalkBack will announce all children's content in a single utterance. + +### Headings + +Mark section headers with `accessibilityHeading="true"`. Users can navigate between headings for quick scanning. + +### Pane Titles + +Identify screen regions with `accessibilityPaneTitle`. Accessibility services announce pane changes. + +### Focus Order + +- Natural reading order (top-to-bottom, start-to-end) +- Use `accessibilityTraversalBefore/After` for custom order +- Ensure all interactive elements are focusable +- Skip decorative elements + +## Decorative Elements + +Skip elements that don't convey information: +- Set `contentDescription="@null"` +- Or set `importantForAccessibility="no"` + +## Custom Accessibility Actions + +### Adding Actions + +Provide alternatives for gesture-based interactions using `ViewCompat.addAccessibilityAction()`. This exposes swipe actions to accessibility services. + +### Replacing Action Labels + +Make default actions more descriptive using `ViewCompat.replaceAccessibilityAction()`. Example: "Double tap and hold to add to favorites" instead of generic "long press". + +## Color and Visual Cues + +### Don't Rely on Color Alone + +Combine color with other indicators: + +| Information | Color + Alternative | +|-------------|---------------------| +| Error state | Red + error icon + text | +| Success | Green + checkmark + text | +| Required field | Red asterisk + "Required" label | +| Selected item | Highlight + checkmark + bold | +| Link text | Blue + underline | + +### Contrast Testing + +Use tools to verify contrast: +- Android Accessibility Scanner +- Contrast Checker plugins +- Manual calculation: (L1 + 0.05) / (L2 + 0.05) + +## Touch Targets + +### Minimum Sizes + +| Element | Minimum | Recommended | +|---------|---------|-------------| +| Standard | 48 × 48dp | 48 × 48dp | +| Primary actions | 48 × 48dp | 56 × 56dp | +| Kids apps | 56 × 56dp | 64 × 64dp | + +### Spacing + +- Minimum 8dp between adjacent touch targets +- Visual element can be smaller if touch area is adequate (use padding) + +## Screen Reader Announcements + +### Live Regions + +Announce dynamic content changes using `accessibilityLiveRegion`: + +| Mode | Usage | +|------|-------| +| polite | Announces when user is idle | +| assertive | Interrupts current speech | +| none | No automatic announcements | + +### Custom Announcements + +Use `announceForAccessibility()` sparingly—prefer live regions. + +## Keyboard and Hardware Navigation + +### Focus Indicators + +- Visible focus state for all interactive elements +- Don't remove default focus indicators +- Custom focus: 2dp+ border or background change + +### Keyboard Shortcuts + +- Support Tab navigation +- Enter/Space for activation +- Arrow keys for lists/grids +- Escape for dismissal + +## Testing Accessibility + +### Manual Testing + +1. **TalkBack**: Navigate entire app with screen reader +2. **Switch Access**: Test with switch navigation +3. **Keyboard**: Navigate with external keyboard only +4. **Magnification**: Test with zoom enabled +5. **Large text**: Test with 200% font scale +6. **High contrast**: Test with high contrast mode + +### Automated Testing + +| Tool | Purpose | +|------|---------| +| Accessibility Scanner | On-device scanning | +| Espresso Accessibility Checks | Automated UI tests | +| Lint checks | Static analysis | + +### Checklist + +- [ ] All interactive elements have descriptions +- [ ] Touch targets are 48dp minimum +- [ ] Color contrast meets requirements +- [ ] Focus order is logical +- [ ] Headings are properly marked +- [ ] Custom actions have descriptive labels +- [ ] Live regions announce important changes +- [ ] Keyboard navigation works +- [ ] Works with TalkBack enabled +- [ ] Works with large text (200%) diff --git a/android-native-dev/references/adaptive-screens.md b/android-native-dev/references/adaptive-screens.md new file mode 100644 index 0000000..d371113 --- /dev/null +++ b/android-native-dev/references/adaptive-screens.md @@ -0,0 +1,231 @@ +# Adaptive Screens Guidelines + +Requirements for large screens, tablets, foldables, and multi-window support. + +## Adaptive Quality Tiers + +Google defines three progressive quality tiers for adaptive apps: + +### Tier 3: Adaptive Ready (Basic) + +Minimum requirements for all apps: + +| Requirement | Description | +|-------------|-------------| +| Full screen | App fills display, no letterboxing | +| Configuration changes | Handles rotation, folding, resizing | +| Multi-window | Supports split-screen mode | +| Basic input | Keyboard, mouse, trackpad support | + +### Tier 2: Adaptive Optimized (Better) + +Enhanced experience: + +| Requirement | Description | +|-------------|-------------| +| Layout optimization | Responsive layouts for all sizes | +| Enhanced input | Full keyboard shortcuts, mouse hover states | +| Continuity | Seamless state preservation | + +### Tier 1: Adaptive Differentiated (Best) + +Device-specific excellence: + +| Requirement | Description | +|-------------|-------------| +| Multitasking | Drag and drop, activity embedding | +| Foldable postures | Table-top mode, book mode support | +| Stylus | Full stylus input support | +| Desktop | Windowed mode optimization | + +## Screen Size Classes + +### Width-Based Classes + +| Class | Width | Typical Devices | +|-------|-------|-----------------| +| Compact | < 600dp | Phone portrait | +| Medium | 600-840dp | Tablet portrait, phone landscape | +| Expanded | > 840dp | Tablet landscape, desktop | + +### Layout Strategies + +| Screen Class | Navigation | Content Layout | +|--------------|------------|----------------| +| Compact | Bottom nav | Single pane | +| Medium | Nav rail | List-detail (optional) | +| Expanded | Nav drawer/rail | List-detail, multi-pane | + +## Configuration Changes + +### Must Handle + +| Change | Trigger | +|--------|---------| +| Rotation | Device rotated | +| Fold/Unfold | Foldable state change | +| Window resize | Multi-window adjustment | +| Split screen | Enter/exit split mode | +| Keyboard | External keyboard attach/detach | + +### Configuration Handling + +| Approach | Description | +|----------|-------------| +| Let system handle | Default, activity recreated | +| Handle manually | Declare configChanges, implement onConfigurationChanged | + +### State Preservation + +- Use ViewModel for UI state +- Use SavedStateHandle for process death +- Test with "Don't keep activities" enabled + +## Multi-Window Support + +### Requirements + +| Feature | Status | +|---------|--------| +| resizeableActivity | true (default API 24+) | +| Minimum size | Support 220dp width | +| State handling | Preserve across resize | + +### Best Practices + +- Don't assume full-screen ownership +- Handle onConfigurationChanged gracefully +- Test at minimum supported size +- Support free-form windows (desktop mode) + +## Foldable Devices + +### Postures + +| Posture | Description | Use Case | +|---------|-------------|----------| +| Flat | Fully open | Normal tablet use | +| Half-opened (tabletop) | Hinged at ~90° horizontal | Video calls, media | +| Half-opened (book) | Hinged at ~90° vertical | Reading, productivity | +| Folded | Closed | Compact phone mode | + +### Design Considerations + +- Avoid placing interactive elements on the fold +- Consider separate content for each screen segment +- Support continuity when fold state changes +- Use WindowInfoTracker to detect fold state + +## External Input Devices + +### Keyboard Support + +| Requirement | Implementation | +|-------------|----------------| +| Tab navigation | Focusable elements in order | +| Enter/Space | Activates focused element | +| Arrow keys | Navigate lists, grids | +| Shortcuts | Common actions (Ctrl+S, etc.) | +| Focus indicators | Visible focus states | + +### Mouse/Trackpad Support + +| Requirement | Implementation | +|-------------|----------------| +| Hover states | Visual feedback on hover | +| Right-click | Context menu support | +| Scroll | Smooth scrolling | +| Pointer cursor | Appropriate cursor types | + +### Stylus Support + +| Feature | Implementation | +|---------|----------------| +| Pressure sensitivity | Variable stroke width | +| Palm rejection | Ignore palm touches | +| Tilt detection | Shading effects | +| Hover preview | Show cursor before touch | + +## Navigation Patterns + +### By Screen Width + +| Width | Primary Nav | Secondary Nav | +|-------|-------------|---------------| +| < 600dp | Bottom nav (3-5 items) | Hamburger menu | +| 600-840dp | Navigation rail | Drawer on demand | +| > 840dp | Permanent drawer or rail | Drawer or none | + +### Navigation Rail Specs + +| Property | Value | +|----------|-------| +| Width | 80dp | +| Icon size | 24dp | +| Touch target | 56dp | +| Items | 3-7 destinations | +| FAB | Optional, at top | + +### Permanent Navigation Drawer + +| Property | Value | +|----------|-------| +| Width | 256-360dp | +| Position | Left edge (LTR) | +| Behavior | Always visible | +| Content | Full labels, icons | + +## Responsive Layouts + +### Breakpoints + +| Class | Width Range | +|-------|-------------| +| COMPACT | < 600dp | +| MEDIUM | 600-840dp | +| EXPANDED | > 840dp | + +Use WindowSizeClass to determine current breakpoint and adapt layout accordingly. + +## Content Considerations + +### Text Readability + +- Line length: 45-75 characters max +- Use multiple columns on wide screens +- Maintain hierarchy with consistent spacing + +### Media + +- Support multiple aspect ratios +- Provide high-resolution assets +- Consider picture-in-picture for video + +### Touch vs. Precise Input + +- Large screens often use mouse/keyboard +- Don't assume touch-only interaction +- Provide hover states and tooltips + +## Testing + +### Device Matrix + +| Device Type | Test Priority | +|-------------|---------------| +| Phone (portrait) | Required | +| Phone (landscape) | Required | +| Tablet (both orientations) | Required | +| Foldable (all postures) | High | +| Desktop/Chromebook | Medium | + +### Test Cases + +- [ ] App fills screen in all configurations +- [ ] No letterboxing or black bars +- [ ] State preserved across configuration changes +- [ ] Multi-window works at minimum size +- [ ] Keyboard navigation functional +- [ ] Mouse hover states present +- [ ] Foldable postures handled (if applicable) +- [ ] Navigation adapts to screen width diff --git a/android-native-dev/references/design-style-guide.md b/android-native-dev/references/design-style-guide.md new file mode 100644 index 0000000..7a17d06 --- /dev/null +++ b/android-native-dev/references/design-style-guide.md @@ -0,0 +1,365 @@ +# Design Style Guide + +Match visual design to app category and target audience for cohesive user experience. + +## Style Selection Principle + +> **The visual style must match the app's purpose and audience.** +> A finance app should feel trustworthy, not playful. +> A children's app should feel fun, not corporate. + +## Style Selection Matrix + +| App Category | Visual Style | Color Palette | Typography | Interaction | +|--------------|--------------|---------------|------------|-------------| +| Utility/Tool | Minimalist | Neutral + 1 accent | Clean sans-serif | Direct, efficient | +| Finance/Banking | Professional Trust | Blue/Green/Navy | Conservative | Secure, deliberate | +| Health/Wellness | Calm & Natural | Soft greens, earth tones | Rounded, friendly | Gentle, encouraging | +| Kids (3-5) | Playful Simple | Bright primary colors | Large, rounded | Big targets, forgiving | +| Kids (6-12) | Fun & Engaging | Vibrant, varied | Bold, readable | Gamified feedback | +| Social/Entertainment | Expressive | Brand-driven | Dynamic | Gesture-rich | +| Productivity | Clean & Focused | Minimal, high contrast | Professional | Keyboard-friendly | +| E-commerce | Conversion-focused | Brand + CTA colors | Scannable | Quick actions | +| Gaming | Immersive | Theme-driven | Stylized | Custom gestures | + +## Detailed Style Profiles + +### Minimalist / iOS-like (Utility Apps) + +**When to use**: Tools, utilities, calculators, file managers, settings apps + +**Visual Characteristics**: + +| Element | Specification | +|---------|---------------| +| Colors | 2-3 colors max, neutral base | +| Whitespace | Generous, 24-48dp margins | +| Typography | Single font family, clear hierarchy | +| Icons | Line-based, consistent stroke | +| Shadows | Subtle or none | +| Borders | Thin (1dp) or none | +| Shapes | Subtle corners (8-12dp) | + +**Interaction Style**: +- Direct manipulation +- Immediate feedback +- No unnecessary animations +- Efficient task completion + +**Color Palette**: + +| Role | Light Mode | Dark Mode | +|------|------------|-----------| +| Background | #FAFAFA | #1C1C1E | +| Surface | #FFFFFF | #2C2C2E | +| Primary | #007AFF | #0A84FF | +| Text | #000000 | #FFFFFF | +| Secondary | #8E8E93 | #8E8E93 | + +**Reference Apps**: iOS Settings, Apple Notes, Google Calculator + +--- + +### Professional Trust (Finance/Business) + +**When to use**: Banking, investment, enterprise, B2B applications + +**Visual Characteristics**: + +| Element | Specification | +|---------|---------------| +| Colors | Blues, greens, navy (trust colors) | +| Whitespace | Structured, grid-based | +| Typography | Formal, conservative weights | +| Icons | Filled or outlined, consistent | +| Data visualization | Clear, accurate charts | +| Security indicators | Prominent locks, badges | + +**Interaction Style**: +- Confirmatory (double-check important actions) +- Deliberate (not rushed) +- Secure-feeling +- Clear feedback on transactions + +**Color Palette**: + +| Role | Color | Name | +|------|-------|------| +| Primary | #00695C or #1565C0 | Teal 800 / Blue 800 | +| Secondary | #37474F | Blue Grey 800 | +| Accent | #FFC107 | Amber | +| Background | #ECEFF1 | Blue Grey 50 | +| Success | #2E7D32 | Green 800 | +| Error | #C62828 | Red 800 | + +**Key Patterns**: +- Balance summaries prominent +- Transaction history easily scannable +- Secure entry for sensitive data +- Biometric authentication prompts + +**Reference Apps**: Banking apps, Trading platforms, Enterprise tools + +--- + +### Calm & Wellness (Health Apps) + +**When to use**: Meditation, fitness tracking, health monitoring, therapy + +**Visual Characteristics**: + +| Element | Specification | +|---------|---------------| +| Colors | Soft, muted, natural | +| Whitespace | Abundant (breathing room) | +| Typography | Rounded, friendly fonts | +| Shapes | Organic, soft corners (16dp+) | +| Animation | Gentle, slow transitions | +| Imagery | Nature, soft gradients | + +**Interaction Style**: +- Encouraging, not demanding +- Progress-oriented +- Gentle reminders +- Celebration of achievements + +**Color Palette**: + +| Role | Color | Name | +|------|-------|------| +| Primary | #4CAF50 | Green 500 | +| Secondary | #81C784 | Green 300 | +| Tertiary | #B2DFDB | Teal 100 | +| Background | #F1F8E9 | Light Green 50 | +| Text | #33691E | Light Green 900 | +| Accent | #FFB74D | Orange 300 | + +**Key Patterns**: +- Progress rings and charts +- Streak tracking +- Motivational messages +- Quiet notification style + +**Reference Apps**: Headspace, Calm, Apple Fitness + +--- + +### Playful & Kid-Friendly (Children's Apps) + +**When to use**: Educational games, children's content, family apps + +#### Ages 3-5 + +**Visual Characteristics**: + +| Element | Specification | +|---------|---------------| +| Colors | Bright, saturated primary colors | +| Touch targets | 56dp minimum, 64dp recommended | +| Shapes | Very rounded (full radius) | +| Typography | Large (18sp+ minimum), simple fonts | +| Icons | Large, colorful, recognizable | +| Animation | Frequent, rewarding | + +**Interaction Style**: +- Simple gestures only (tap, drag) +- No multi-finger gestures +- Forgiving error handling +- Immediate, multi-sensory feedback (sound + visual + haptic) +- No text-only buttons + +**Color Palette**: + +| Role | Color | Name | +|------|-------|------| +| Primary | #F44336 | Red 500 | +| Secondary | #FFEB3B | Yellow 500 | +| Tertiary | #2196F3 | Blue 500 | +| Background | #FFFFFF | White or soft pastels | +| Accent | #4CAF50 | Green 500 | + +#### Ages 6-12 + +**Visual Characteristics**: + +| Element | Specification | +|---------|---------------| +| Colors | Vibrant, varied palette | +| Touch targets | 48dp minimum | +| Shapes | Rounded but can be varied | +| Typography | Bold, readable, can include text | +| Icons | Stylized, character-driven | +| Animation | Gamified, achievement-based | + +**Interaction Style**: +- Can introduce some complexity +- Gamification elements +- Progress and rewards +- Some text is acceptable + +**Key Patterns for All Kids Apps**: +- Icon-based navigation (no text-only) +- Home button always visible +- Back navigation clear +- Parent gate for settings (math problem, hold button) +- Multi-sensory feedback +- Encouraging error states (no punishment) +- Joint engagement opportunities with parents + +**Reference Apps**: PBS Kids, Khan Academy Kids, Duolingo ABC + +--- + +### Expressive & Social (Entertainment Apps) + +**When to use**: Social media, content creation, entertainment + +**Visual Characteristics**: + +| Element | Specification | +|---------|---------------| +| Colors | Bold brand colors | +| Typography | Dynamic, personality-driven | +| Media | Rich, prominent | +| Animation | Expressive, delightful | +| Shapes | Brand-specific | + +**Interaction Style**: +- Gesture-rich +- Quick actions +- Social interactions prominent +- Content-first design + +**Key Patterns**: +- Feed-based layouts +- Quick action buttons (like, share, comment) +- Stories/ephemeral content +- Creation tools accessible +- Notification badges + +**Reference Apps**: Instagram, TikTok, Snapchat + +--- + +### Clean & Focused (Productivity Apps) + +**When to use**: Note-taking, task management, email, documents + +**Visual Characteristics**: + +| Element | Specification | +|---------|---------------| +| Colors | High contrast, minimal | +| Whitespace | Strategic, content-focused | +| Typography | Highly readable, clear hierarchy | +| Icons | Functional, consistent | +| Density | Adjustable (compact to comfortable) | + +**Interaction Style**: +- Keyboard-friendly +- Batch operations +- Drag and drop +- Quick capture +- Search-centric + +**Color Palette**: + +| Role | Light Mode | Dark Mode | +|------|------------|-----------| +| Primary | #1976D2 | #64B5F6 | +| Background | #FFFFFF | #121212 | +| Surface | #F5F5F5 | #1E1E1E | +| Text | #212121 | #E0E0E0 | +| Accent/Priority | #FF5722 | #FF7043 | + +**Key Patterns**: +- List views with swipe actions +- Quick add buttons +- Checkbox interactions +- Due dates and reminders +- Tags and categories + +**Reference Apps**: Notion, Todoist, Google Tasks + +--- + +### Conversion-Focused (E-commerce) + +**When to use**: Shopping, marketplace, booking apps + +**Visual Characteristics**: + +| Element | Specification | +|---------|---------------| +| Colors | Brand + clear CTA colors | +| Images | High quality, zoomable | +| Typography | Scannable, price prominent | +| Cards | Product-focused | +| Badges | Sale, new, limited | + +**Interaction Style**: +- Quick add to cart +- Easy checkout flow +- Comparison features +- Reviews accessible +- Wishlist/save for later + +**Key Patterns**: +- Grid and list view toggle +- Filter and sort +- Product detail with gallery +- Cart always accessible +- One-tap purchase options + +**Reference Apps**: Amazon, Shopify apps, Booking.com + +--- + +## Consistency Principles + +### Match Style to Subject Matter + +| App Purpose | Style Should Feel | +|-------------|-------------------| +| Utility | Efficient, invisible | +| Finance | Trustworthy, secure | +| Health | Supportive, calm | +| Kids | Safe, fun | +| Social | Expressive, personal | +| Productivity | Focused, powerful | +| Shopping | Exciting, trustworthy | + +### Internal Consistency Rules + +| Rule | Implementation | +|------|----------------| +| Same icon style | All outlined OR all filled | +| Consistent color meaning | Red = destructive, Green = success | +| Uniform spacing | Use 8dp grid | +| Predictable interaction | Same gesture = same result | +| Typography system | Use M3 type scale | + +## Anti-Patterns: Style Mismatch + +| Mismatch | Problem | +|----------|---------| +| Playful colors in banking app | Undermines trust | +| Complex gestures in kids app | Frustrates young users | +| Cluttered UI in wellness app | Defeats calming purpose | +| Boring visuals in entertainment | Fails to engage | +| Aggressive CTAs in health app | Feels manipulative | +| Childish design in professional tool | Lacks credibility | +| Dense information in casual app | Overwhelms users | + +## Implementation Checklist + +- [ ] Identified app category and target audience +- [ ] Selected appropriate style profile +- [ ] Color palette matches style +- [ ] Typography matches style +- [ ] Interaction patterns match style +- [ ] Touch targets appropriate for audience +- [ ] Animation style consistent +- [ ] Internal consistency maintained +- [ ] No style mismatches +- [ ] Tested with target users diff --git a/android-native-dev/references/functional-requirements.md b/android-native-dev/references/functional-requirements.md new file mode 100644 index 0000000..2d19627 --- /dev/null +++ b/android-native-dev/references/functional-requirements.md @@ -0,0 +1,229 @@ +# Functional Requirements + +Audio, video, notifications, and other functional behavior requirements. + +## Audio + +### Playback Initialization + +| Requirement | Specification | +|-------------|---------------| +| Response time | < 1 second | +| If delayed | Show visual progress indicator | +| User feedback | Immediate acknowledgment of action | + +### Audio Focus Rules + +| Event | Required Action | +|-------|-----------------| +| Another app requests focus | Pause or reduce volume | +| Focus regained | Resume or restore volume | +| Playback stops | Abandon focus | + +### Audio Focus Handling + +| Focus Change | Action | +|--------------|--------| +| AUDIOFOCUS_LOSS | Stop playback | +| AUDIOFOCUS_LOSS_TRANSIENT | Pause playback | +| AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK | Reduce volume | +| AUDIOFOCUS_GAIN | Resume playback | + +### Background Playback + +| Requirement | Implementation | +|-------------|----------------| +| Continue when backgrounded | Use Foreground Service | +| Notification | MediaStyle notification required | +| Media controls | System media controls integration | +| Session | MediaSession for system integration | + +## Video + +### Picture-in-Picture (PiP) + +| Requirement | Specification | +|-------------|---------------| +| Video apps | Should support PiP | +| Aspect ratio | 16:9 to 2.39:1 | +| Auto-enter | When user navigates away during playback | + +### Video Encoding + +| Standard | Requirement | +|----------|-------------| +| Compression | HEVC (H.265) recommended | +| Fallback | H.264 for compatibility | +| Quality | Adaptive based on network | + +### Video Player Requirements + +| Feature | Implementation | +|---------|----------------| +| Fullscreen | Support landscape | +| Controls | Play, pause, seek, volume | +| Captions | Support closed captions | +| Resume | Remember playback position | + +## Notifications + +### Channel Best Practices + +| Practice | Reason | +|----------|--------| +| Multiple channels | User can control each type | +| Descriptive names | User understands purpose | +| Appropriate importance | Match user expectation | +| Don't share channels | Different content = different channel | + +### Notification Priority + +| Importance | Usage | +|------------|-------| +| HIGH | Time-sensitive (messages, calls) | +| DEFAULT | Normal notifications | +| LOW | Background info | +| MIN | Minimal interruption | + +### Notification Content Rules + +| Do | Don't | +|-----|-------| +| Relevant information | Cross-promotion | +| Clear, concise text | Advertising other products | +| Actionable content | Unnecessary interruptions | +| Set timeouts | Persistent non-ongoing notifications | + +### Messaging Apps Requirements + +| Feature | Description | +|---------|-------------| +| MessagingStyle | Use for conversation notifications | +| Direct reply | Support inline reply action | +| Conversation shortcuts | Enable direct share | +| Bubbles | Support floating conversations | + +### Notification Grouping + +Group related notifications together with a summary notification. Set appropriate group keys and summary flags. + +## Sharing + +### Android Sharesheet + +Use the system sharesheet for sharing content. Create an ACTION_SEND intent with appropriate type and extras, then use createChooser(). + +### Direct Share + +Provide conversation shortcuts for Direct Share ranking: +- Create ShortcutInfo for each conversation +- Set appropriate categories +- Push dynamic shortcuts + +## Background Services + +### Service Restrictions + +| Rule | Implementation | +|------|----------------| +| Avoid long-running services | Use WorkManager | +| No background starts (API 26+) | Use foreground service or JobScheduler | +| Battery-efficient | Batch work, respect Doze | + +### Poor Background Service Uses + +| Don't Use For | Alternative | +|---------------|-------------| +| Maintaining network connection | FCM (push notifications) | +| Persistent Bluetooth | Companion device manager | +| Keeping GPS on | Geofencing, fused location | +| Polling server | FCM or WorkManager | + +## State Management + +### State Preservation Requirements + +| Scenario | Required Behavior | +|----------|-------------------| +| App switcher return | Exact previous state | +| Device wake | Exact previous state | +| Process death | Restore critical state | +| Configuration change | Seamless transition | + +### State Categories + +| State Type | Storage | +|------------|---------| +| UI state (scroll, selection) | ViewModel + SavedState | +| User input (forms) | SavedState | +| Navigation | NavController state | +| Persistent data | Room database | + +## Navigation + +### Back Button/Gesture + +| Requirement | Implementation | +|-------------|----------------| +| System back | Navigate to previous screen | +| Gesture navigation | Support back gesture | +| No custom back buttons | Use system navigation | +| Predictable | User knows what back does | + +## Gestures + +### Gesture Navigation Support + +| Gesture | Default Action | +|---------|----------------| +| Swipe from left edge | Back | +| Swipe up from bottom | Home | +| Swipe up and hold | Recent apps | + +### Custom Gestures + +| Practice | Reason | +|----------|--------| +| Avoid edge swipes | Conflicts with navigation | +| Provide alternatives | Not all users gesture-capable | +| Test with gesture nav | Ensure no conflicts | + +Handle system gesture insets to avoid conflicts with edge gestures. + +## Functional Checklist + +### Audio +- [ ] Playback starts within 1 second +- [ ] Audio focus requested and released +- [ ] Responds to focus changes (duck/pause) +- [ ] Background playback with notification +- [ ] MediaSession integration + +### Video +- [ ] Picture-in-picture supported +- [ ] HEVC encoding used +- [ ] Playback position remembered +- [ ] Captions supported + +### Notifications +- [ ] Appropriate channels defined +- [ ] Correct importance levels +- [ ] No promotional content +- [ ] Grouped when appropriate +- [ ] Timeouts set where applicable + +### Messaging (if applicable) +- [ ] MessagingStyle used +- [ ] Direct reply supported +- [ ] Conversation shortcuts +- [ ] Bubbles supported + +### Background +- [ ] WorkManager for background work +- [ ] No long-running services +- [ ] Battery-efficient design + +### Navigation +- [ ] Standard back behavior +- [ ] Gesture navigation supported +- [ ] State preserved across lifecycle diff --git a/android-native-dev/references/motion-system.md b/android-native-dev/references/motion-system.md new file mode 100644 index 0000000..c0dd87e --- /dev/null +++ b/android-native-dev/references/motion-system.md @@ -0,0 +1,203 @@ +# Motion System Guidelines + +Animation and transition specifications for Material Design 3. + +## Motion Principles + +### Four Core Characteristics + +| Principle | Description | +|-----------|-------------| +| **Responsive** | Quickly responds to user input at the point of interaction | +| **Natural** | Follows real-world physics (gravity, friction, momentum) | +| **Aware** | Elements are aware of surroundings and other elements | +| **Intentional** | Guides focus to the right place at the right time | + +## Duration Guidelines + +### By Interaction Type + +| Type | Duration | Usage | +|------|----------|-------| +| Micro | 50-100ms | Ripples, state changes, hover | +| Short | 100-200ms | Simple transitions, toggles | +| Medium | 200-300ms | Expanding, collapsing, revealing | +| Long | 300-500ms | Complex choreography, page transitions | + +### By Device Type + +| Device | Typical Duration | Adjustment | +|--------|------------------|------------| +| Mobile | 300ms | Baseline | +| Tablet | 390ms | +30% slower | +| Desktop | 150-200ms | Faster, more responsive | +| Wearable | 210ms | -30% faster | + +### Duration Rules + +- **Maximum**: Keep under 400ms for most transitions +- **User-initiated**: Faster (closer to instant feedback) +- **System-initiated**: Can be slightly longer +- **Loading states**: Use indeterminate indicators for unknown duration + +## Easing Curves + +### Standard Curves + +| Curve | Usage | Characteristics | +|-------|-------|-----------------| +| **Standard** | Most common transitions | Quick acceleration, slow deceleration | +| **Emphasized** | Important/significant transitions | More dramatic curve | +| **Decelerate** | Elements entering screen | Starts fast, ends slow | +| **Accelerate** | Elements leaving screen permanently | Starts slow, ends fast | +| **Sharp** | Elements temporarily leaving | Quick, snappy motion | + +### Curve Values (Cubic Bezier) + +| Curve | Value | +|-------|-------| +| Standard | cubic-bezier(0.2, 0.0, 0.0, 1.0) | +| Emphasized | cubic-bezier(0.2, 0.0, 0.0, 1.0) | +| Decelerate | cubic-bezier(0.0, 0.0, 0.0, 1.0) | +| Accelerate | cubic-bezier(0.3, 0.0, 1.0, 1.0) | + +## Movement Patterns + +### Arc Motion + +- Use natural, concave arcs for diagonal movement +- Single-axis movement (horizontal/vertical only) stays straight +- Elements entering/exiting screen move on single axis + +### Choreography + +- **Stagger**: Offset timing for related elements (20-40ms between) +- **Cascade**: Sequential reveal from a focal point +- **Shared motion**: Elements that move together maintain relationship + +## Transition Patterns + +### Container Transform + +Best for: Navigation from card/list item to detail screen + +- Origin container morphs into destination +- Maintains visual continuity +- Content fades during transformation + +### Shared Axis + +Best for: Same-level navigation (tabs, stepper) + +| Axis | Direction | Usage | +|------|-----------|-------| +| X-axis | Horizontal | Tabs, horizontal paging | +| Y-axis | Vertical | Vertical lists, feeds | +| Z-axis | Depth | Parent-child relationships | + +### Fade Through + +Best for: Unrelated screen transitions + +- Outgoing content fades out +- Incoming content fades in +- Brief overlap period +- No shared elements + +### Fade + +Best for: Show/hide single elements + +- Simple opacity change +- Optionally combine with scale +- Quick duration (100-200ms) + +## Component-Specific Motion + +### FAB + +| State | Animation | +|-------|-----------| +| Appear | Scale up + fade in | +| Disappear | Scale down + fade out | +| Transform | Morph to extended FAB | +| Press | Elevation change (3dp → 8dp) | + +### Bottom Sheet + +| State | Animation | +|-------|-----------| +| Expand | Slide up with decelerate curve | +| Collapse | Slide down with accelerate curve | +| Dismiss | Swipe down with velocity-based duration | + +### Navigation + +| Pattern | Animation | +|---------|-----------| +| Push | Incoming slides from right, outgoing shifts left | +| Pop | Incoming slides from left, outgoing shifts right | +| Modal | Slide up from bottom | + +### Cards + +| State | Animation | +|-------|-----------| +| Expand | Container transform to detail | +| Press | Subtle elevation increase | +| Reorder | Follow finger with physics | + +## Loading & Progress + +### Indeterminate Indicators + +- Use for unknown duration +- Continuous, looping animation +- M3 Expressive: Customizable waveform and thickness + +### Determinate Indicators + +- Use when progress is measurable +- Smooth, linear progression +- Update frequently for responsiveness + +### Skeleton Screens + +- Show layout structure immediately +- Subtle shimmer animation +- Replace with content as it loads + +## Accessibility Considerations + +### Reduced Motion + +- Respect prefers-reduced-motion setting +- Provide alternatives: + - Instant transitions (no animation) + - Simple fade instead of complex motion + - Static loading indicators + +### Motion Duration + +- Keep essential feedback < 100ms +- Avoid motion that could trigger vestibular issues +- Test with motion sensitivity settings enabled + +## Implementation Notes + +### Android Animation APIs + +| API | Usage | +|-----|-------| +| MotionLayout | Complex, coordinated animations | +| Transition | Activity/Fragment transitions | +| Animator | Property animations | +| AnimatedContent | Compose content transitions | +| animateContentSize | Compose size changes | + +### Performance Tips + +- Use hardware layers for complex animations +- Avoid animating layout properties (use transform) +- Profile with GPU rendering tools +- Target 60 FPS (16ms per frame) diff --git a/android-native-dev/references/performance-stability.md b/android-native-dev/references/performance-stability.md new file mode 100644 index 0000000..209fbc2 --- /dev/null +++ b/android-native-dev/references/performance-stability.md @@ -0,0 +1,223 @@ +# Performance & Stability Guidelines + +Android Vitals thresholds, performance requirements, and stability best practices. + +## Android Vitals Thresholds + +### Core Metrics (Google Play) + +Exceeding these thresholds affects app visibility on Google Play: + +| Metric | Overall Threshold | Per Phone Model | Per Watch Model | +|--------|-------------------|-----------------|-----------------| +| User-perceived crash rate | **1.09%** | 8% | 4% | +| User-perceived ANR rate | **0.47%** | 8% | 5% | +| Excessive battery usage | 1% | - | 1% | +| Excessive wake locks | 5% | - | - | + +### Consequences of Exceeding Thresholds + +- Reduced app visibility in Google Play +- Warning label on store listing +- Lower ranking in search results +- Negative impact on user trust + +## Startup Performance + +### Requirements + +| Metric | Target | Maximum | +|--------|--------|---------| +| Cold start | < 1 second | 2 seconds | +| Warm start | < 500ms | 1 second | +| Hot start | < 100ms | 500ms | + +### If Startup Exceeds 2 Seconds + +Must provide visual feedback: +- Progress indicator +- Splash screen with animation +- Loading skeleton + +### Optimization Techniques + +| Technique | Impact | +|-----------|--------| +| Lazy initialization | Defer non-critical work | +| Async loading | Move I/O off main thread | +| View hierarchy optimization | Reduce layout depth | +| App Startup library | Initialize components efficiently | +| Baseline Profiles | Pre-compile hot paths | + +## Rendering Performance + +### Frame Rate Requirements + +| Target | Frame Time | Notes | +|--------|------------|-------| +| 60 FPS | ≤ 16.67ms | Standard requirement | +| 90 FPS | ≤ 11.11ms | High refresh rate displays | +| 120 FPS | ≤ 8.33ms | Premium devices | + +### Jank Detection + +| Metric | Threshold | Severity | +|--------|-----------|----------| +| Slow frames | > 16ms | Warning | +| Frozen frames | > 700ms | Critical | +| Jank rate | > 1% of frames | Poor experience | + +### Common Rendering Issues + +| Issue | Cause | Solution | +|-------|-------|----------| +| Overdraw | Multiple layers drawn | Reduce background stacking | +| Deep hierarchy | Complex view nesting | Use ConstraintLayout, Compose | +| Main thread work | Blocking operations | Move to background thread | +| Large bitmaps | Unoptimized images | Downsample, use vector | + +## ANR Prevention + +### ANR Triggers + +| Scenario | Timeout | +|----------|---------| +| Input dispatch | 5 seconds | +| Broadcast receiver | 10 seconds | +| Service start | 20 seconds | + +### Prevention Strategies + +- Never perform network calls on main thread +- Never perform database operations on main thread +- Never perform file I/O on main thread +- Use coroutines, RxJava, or other async mechanisms +- Reduce synchronized block contention + +### Common ANR Causes + +| Cause | Solution | +|-------|----------| +| Network on main thread | Use coroutines/RxJava | +| Database on main thread | Use Room with suspend | +| File I/O on main thread | Use Dispatchers.IO | +| Lock contention | Reduce synchronized blocks | +| Dead locks | Careful threading design | + +## Battery Optimization + +### Wake Lock Guidelines + +| Rule | Implementation | +|------|----------------| +| Minimize duration | Release as soon as possible | +| Use appropriate type | PARTIAL_WAKE_LOCK only when needed | +| Always release | Use try-finally or lifecycle | +| Prefer WorkManager | System-managed scheduling | + +### Background Restrictions + +| Feature | Best Practice | +|---------|---------------| +| Background services | Use WorkManager instead | +| Location | Request only when necessary | +| Network | Batch requests, respect connectivity | +| Alarms | Use inexact alarms when possible | + +### Doze and App Standby + +| Mode | Behavior | Adaptation | +|------|----------|------------| +| Doze | Limited network, alarms delayed | Use FCM for high-priority | +| App Standby | Background work restricted | Use expedited WorkManager | +| Buckets | Frequency limits by usage | Design for infrequent execution | + +## Memory Management + +### Memory Best Practices + +| Practice | Benefit | +|----------|---------| +| Avoid memory leaks | Prevent OutOfMemoryError | +| Use weak references | Allow garbage collection | +| Recycle bitmaps | Reduce memory pressure | +| Monitor heap | Profile regularly | + +### Common Memory Issues + +| Issue | Detection | Solution | +|-------|-----------|----------| +| Activity leak | LeakCanary | Fix lifecycle references | +| Bitmap leak | Memory profiler | Recycle, use Glide/Coil | +| Context leak | Static analysis | Use application context | +| Handler leak | Lint warning | Use WeakReference | + +## StrictMode + +### What StrictMode Detects + +| Category | Issues | +|----------|--------| +| Thread | Disk reads/writes, network, slow calls | +| VM | Leaked objects, unsafe intents, content URI exposure | + +Enable StrictMode in debug builds to detect violations during development. + +## SDK Requirements + +### Version Requirements + +| Property | Requirement | +|----------|-------------| +| targetSdk | Latest Android SDK (Google Play requirement) | +| compileSdk | Latest Android SDK | +| minSdk | Based on target audience | + +### Third-Party SDK Management + +| Practice | Reason | +|----------|--------| +| Keep updated | Security fixes, compatibility | +| Audit regularly | Remove unused dependencies | +| Monitor crashes | SDKs can cause issues | +| Check permissions | SDKs may request excessive permissions | + +### Non-SDK Interface Restrictions + +- Don't use reflection for hidden APIs +- Use Android Studio lint to detect +- APIs may break in future versions + +## Monitoring and Profiling + +### Tools + +| Tool | Purpose | +|------|---------| +| Android Studio Profiler | CPU, memory, network, energy | +| Android Vitals (Play Console) | Production crash/ANR data | +| Firebase Performance | Real-time performance monitoring | +| Perfetto | Advanced system tracing | +| Benchmark library | Reproducible measurements | + +### Key Metrics to Track + +| Metric | Tool | +|--------|------| +| Startup time | Macrobenchmark | +| Frame timing | JankStats | +| Memory usage | Memory Profiler | +| Network latency | Network Profiler | +| Battery drain | Energy Profiler | + +## Performance Checklist + +- [ ] Cold startup < 2 seconds +- [ ] Rendering at 60 FPS +- [ ] No StrictMode violations +- [ ] Crash rate < 1.09% +- [ ] ANR rate < 0.47% +- [ ] No memory leaks +- [ ] Background work uses WorkManager +- [ ] Wake locks properly released +- [ ] SDKs up to date diff --git a/android-native-dev/references/privacy-security.md b/android-native-dev/references/privacy-security.md new file mode 100644 index 0000000..807739e --- /dev/null +++ b/android-native-dev/references/privacy-security.md @@ -0,0 +1,244 @@ +# Privacy & Security Guidelines + +Security best practices and privacy requirements for Android applications. + +## Permissions + +### Principle of Least Privilege + +| Rule | Implementation | +|------|----------------| +| Request minimum | Only permissions essential for core features | +| Request when needed | At point of use, not app startup | +| Explain why | Show rationale before system dialog | +| Degrade gracefully | App works (limited) if denied | + +### Permission Request Flow + +1. Check if already granted +2. If not, show educational UI (rationale) +3. Request permission +4. Handle result (grant or denial) +5. If denied, offer alternative or reduced functionality + +### Sensitive Permissions + +| Permission | Consideration | +|------------|---------------| +| Location | Use coarse if fine not needed | +| Camera | Request only when capturing | +| Microphone | Request only when recording | +| Contacts | Consider contact picker intent | +| Storage | Use scoped storage | +| SMS/Call Log | Restricted, needs approval | + +### Alternative Approaches + +| Instead of... | Consider... | +|---------------|-------------| +| READ_CONTACTS | Contact picker intent | +| ACCESS_FINE_LOCATION | Coarse location | +| READ_EXTERNAL_STORAGE | Storage Access Framework | +| CAMERA | Camera intent | + +## Data Storage + +### Storage Types + +| Type | Security | Usage | +|------|----------|-------| +| Internal storage | Private to app | Sensitive data | +| External storage | World-readable | Shared files only | +| SharedPreferences | Private, unencrypted | Non-sensitive settings | +| EncryptedSharedPreferences | Private, encrypted | Sensitive settings | +| Room database | Private, optional encryption | Structured data | + +### Sensitive Data Rules + +| Rule | Implementation | +|------|----------------| +| Store internally | Use internal storage, not external | +| Encrypt at rest | Use EncryptedSharedPreferences, SQLCipher | +| Don't log | Never log PII or credentials | +| Clear on logout | Wipe user data completely | + +### Data Logging + +Never log sensitive data such as passwords, emails, tokens, or personal information. Only log non-sensitive operational information. + +## Network Security + +### HTTPS Requirements + +- All network traffic must use SSL/TLS +- Configure Network Security Config +- Don't allow cleartext traffic + +### Network Security Config + +Define a network security configuration that: +- Disables cleartext traffic +- Specifies trusted certificate authorities +- Optionally implements certificate pinning for high-security apps + +### Certificate Pinning (Optional) + +For high-security apps, pin certificates to prevent MITM attacks. Include backup pins and plan for certificate rotation. + +## User Identity + +### Credential Manager + +Integrate Credential Manager for unified sign-in supporting: +- Passkeys +- Federated identity +- Traditional passwords + +### Biometric Authentication + +Use biometric authentication for sensitive operations like: +- Financial transactions +- Accessing sensitive documents +- Confirming identity + +### Autofill Support + +Provide autofill hints on input fields: +- emailAddress, username for identity fields +- password for credential fields +- creditCardNumber, postalCode for payment fields + +## App Components Security + +### Exported Components + +| Component | Exported Rule | +|-----------|---------------| +| Launcher Activity | exported="true" with intent-filter | +| Internal Activity | exported="false" | +| Internal Service | exported="false" | +| Content Provider (shared) | exported="true" with permissions | + +Always explicitly set the exported attribute on all components. + +### Custom Permissions + +Use signature-level protection for custom permissions that control access between your own apps. + +### Intent Validation + +- Validate all intent data before use +- Check URI scheme and host +- Use explicit intents when possible +- Don't trust extras from unknown sources + +### PendingIntent Security + +Use FLAG_IMMUTABLE for PendingIntents unless mutability is required. This prevents other apps from modifying the intent. + +## WebView Security + +### Safe WebView Configuration + +| Setting | Recommendation | +|---------|----------------| +| JavaScript | Disabled unless required | +| File access | Disabled | +| Content access | Disabled | +| Universal file access | Never enable | + +### Avoid Dangerous Practices + +| Don't | Why | +|-------|-----| +| setAllowUniversalAccessFromFileURLs(true) | Security vulnerability | +| addJavascriptInterface() with untrusted content | Code injection risk | +| Load untrusted URLs | XSS, phishing | + +## Cryptography + +### Use Platform APIs + +- Use Android Keystore for key storage +- Use standard algorithms (AES-GCM, RSA) +- Never implement custom cryptography +- Use SecureRandom for random generation + +### Avoid + +- Custom encryption implementations +- Weak algorithms (MD5, SHA1 for security) +- Hardcoded keys or secrets +- Non-cryptographic random generators + +## Code Security + +### No Dynamic Code Loading + +| Don't | Do Instead | +|-------|------------| +| Load code at runtime | Android App Bundles | +| Download DEX files | Play Feature Delivery | +| Execute scripts | Predefined functionality | + +### Debug Code Removal + +- Set debuggable=false in release builds +- Enable minification (R8/ProGuard) +- Remove debug libraries from production + +## Device Identifiers + +### Don't Use Hardware IDs + +| Identifier | Status | +|------------|--------| +| IMEI | Don't use | +| MAC address | Don't use | +| Serial number | Don't use | +| Android ID | Limited use only | + +### Recommended Alternatives + +| Use Case | Solution | +|----------|----------| +| Analytics | Firebase Analytics ID | +| Advertising | Advertising ID (resettable) | +| App instance | Generate UUID on install | +| User identity | Account-based ID | + +## Google Play Policies + +### Data Safety + +- Declare all data collected +- Explain data usage +- Provide privacy policy +- Allow data deletion requests + +### User Data Policy + +| Rule | Requirement | +|------|-------------| +| Transparency | Clear disclosure of data use | +| Security | Protect user data appropriately | +| Minimization | Collect only what's needed | +| Control | Allow users to manage data | + +## Security Checklist + +- [ ] Permissions requested only when needed +- [ ] Permissions explained to user +- [ ] Sensitive data stored internally +- [ ] No sensitive data in logs +- [ ] All network traffic over HTTPS +- [ ] Network security config defined +- [ ] Components export status explicit +- [ ] Custom permissions use signature protection +- [ ] Intents validated before use +- [ ] PendingIntents use FLAG_IMMUTABLE +- [ ] WebView configured securely +- [ ] Platform crypto APIs used +- [ ] No debug code in production +- [ ] No hardware IDs used +- [ ] Privacy policy available diff --git a/android-native-dev/references/testing.md b/android-native-dev/references/testing.md new file mode 100644 index 0000000..8543f86 --- /dev/null +++ b/android-native-dev/references/testing.md @@ -0,0 +1,554 @@ +# Testing + +Detailed examples and patterns for each Android test layer. Read the section relevant to the layer you're working with. + +## Table of Contents + +1. [Local Unit Tests (JUnit + Robolectric)](#1-local-unit-tests-junit--robolectric) +2. [Instrumentation Tests (Espresso)](#2-instrumentation-tests-espresso) +3. [UI Automator (Cross-App & System UI)](#3-ui-automator-cross-app--system-ui) +4. [Compose UI Testing](#4-compose-ui-testing) +5. [Gradle Managed Devices](#5-gradle-managed-devices) + +--- + +## 1. Local Unit Tests (JUnit + Robolectric) + +Local tests live in `src/test/` and run on the JVM — no emulator needed, so they're fast (milliseconds each). Use them for ViewModels, Repositories, mappers, validators, and any pure logic. + +### Basic ViewModel Test + +```kotlin +class CounterViewModelTest { + @get:Rule + val mainDispatcherRule = MainDispatcherRule() // see below + + private lateinit var viewModel: CounterViewModel + + @Before + fun setup() { + viewModel = CounterViewModel() + } + + @Test + fun `increment updates count`() = runTest { + viewModel.increment() + assertEquals(1, viewModel.uiState.value.count) + } +} +``` + +### Testing Coroutines (Critical) + +The Main dispatcher doesn't exist on the JVM. Replace it with `TestDispatcher` or tests crash with `IllegalStateException`. + +```kotlin +// Reusable rule — put in a shared test-util module +class MainDispatcherRule( + private val dispatcher: TestDispatcher = UnconfinedTestDispatcher() +) : TestWatcher() { + override fun starting(description: Description) { + Dispatchers.setMain(dispatcher) + } + override fun finished(description: Description) { + Dispatchers.resetMain() + } +} +``` + +```kotlin +// ❌ Wrong: No Main dispatcher replacement → crash +@Test +fun `load data`() = runTest { + val vm = MyViewModel(repo) + vm.load() // launches on Dispatchers.Main → IllegalStateException +} + +// ✅ Correct: Use MainDispatcherRule +@get:Rule +val mainDispatcherRule = MainDispatcherRule() + +@Test +fun `load data`() = runTest { + val vm = MyViewModel(repo) + vm.load() + assertEquals(UiState.Success, vm.uiState.value) +} +``` + +### Testing StateFlow with Turbine + +```kotlin +@Test +fun `loading then success states`() = runTest { + val vm = MyViewModel(fakeRepo) + + vm.uiState.test { // Turbine extension + assertEquals(UiState.Idle, awaitItem()) + vm.load() + assertEquals(UiState.Loading, awaitItem()) + assertEquals(UiState.Success(data), awaitItem()) + cancelAndIgnoreRemainingEvents() + } +} +``` + +### Mocking with MockK + +```kotlin +@Test +fun `repository calls api and caches`() = runTest { + val api = mockk() + coEvery { api.getUser("42") } returns User("42", "Alice") + + val repo = UserRepository(api) + val user = repo.getUser("42") + + assertEquals("Alice", user.name) + coVerify(exactly = 1) { api.getUser("42") } +} +``` + +| MockK Function | Purpose | +|----------------|------------------------| +| `mockk()` | Create mock instance | +| `every { }` | Stub synchronous calls | +| `coEvery { }` | Stub suspend functions | +| `verify { }` | Verify call happened | +| `coVerify { }` | Verify suspend call | +| `slot()` | Capture argument value | + +### Robolectric — When You Need Android Classes + +Robolectric simulates the Android framework on the JVM, so tests stay fast while accessing `Context`, `SharedPreferences`, resources, etc. + +```kotlin +@RunWith(RobolectricTestRunner::class) +@Config(sdk = [34]) +class PreferencesManagerTest { + + private lateinit var context: Context + + @Before + fun setup() { + context = ApplicationProvider.getApplicationContext() + } + + @Test + fun `saves and reads theme preference`() { + val prefs = PreferencesManager(context) + prefs.setDarkMode(true) + assertTrue(prefs.isDarkMode()) + } +} +``` + +### Common Local Test Mistakes + +```kotlin +// ❌ Wrong: Testing implementation details (fragile) +@Test +fun `check internal cache map size`() { + repo.load() + assertEquals(1, repo.cacheMap.size) // breaks if cache strategy changes +} + +// ✅ Correct: Test observable behavior +@Test +fun `second call returns cached result without network`() = runTest { + coEvery { api.fetch() } returns data + + repo.load() + repo.load() + + coVerify(exactly = 1) { api.fetch() } // only one network call +} +``` + +--- + +## 2. Instrumentation Tests (Espresso) + +Instrumentation tests live in `src/androidTest/` and run on a real device or emulator. Slower than local tests, but they exercise the actual Android stack — use them for UI flows, database integration, and cross-component interaction. + +### Test Runner Setup + +In `app/build.gradle.kts`: + +```kotlin +android { + defaultConfig { + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } +} +``` + +### Espresso Basics + +Espresso's API follows a consistent pattern: **find → act → assert**. + +```kotlin +@RunWith(AndroidJUnit4::class) +class LoginScreenTest { + + @get:Rule + val activityRule = ActivityScenarioRule(LoginActivity::class.java) + + @Test + fun validLogin_navigatesToHome() { + // Find and act + onView(withId(R.id.email_input)) + .perform(typeText("user@example.com"), closeSoftKeyboard()) + onView(withId(R.id.password_input)) + .perform(typeText("secret123"), closeSoftKeyboard()) + onView(withId(R.id.login_button)) + .perform(click()) + + // Assert + onView(withId(R.id.home_container)) + .check(matches(isDisplayed())) + } +} +``` + +| Category | Common Matchers / Actions | +|------------|------------------------------------------------------------------------------------| +| **Find** | `withId(R.id.x)`, `withText("x")`, `withContentDescription("x")`, `withHint("x")` | +| **Act** | `click()`, `typeText("x")`, `clearText()`, `scrollTo()`, `swipeUp()` | +| **Assert** | `isDisplayed()`, `withText("x")`, `isEnabled()`, `isChecked()`, `doesNotExist()` | + +### Testing Intents + +Espresso-Intents lets you verify outgoing Intents and stub responses (e.g., camera, file picker). + +```kotlin +@get:Rule +val intentsRule = IntentsRule() + +@Test +fun shareButton_launchesShareIntent() { + onView(withId(R.id.share_button)).perform(click()) + + intended(allOf( + hasAction(Intent.ACTION_SEND), + hasType("text/plain") + )) +} + +@Test +fun cameraButton_handlesResult() { + val resultData = Intent().apply { putExtra("photo_uri", "content://mock") } + intending(hasAction(MediaStore.ACTION_IMAGE_CAPTURE)) + .respondWith(Instrumentation.ActivityResult(RESULT_OK, resultData)) + + onView(withId(R.id.camera_button)).perform(click()) + onView(withId(R.id.photo_preview)).check(matches(isDisplayed())) +} +``` + +### IdlingResource for Async Operations + +Espresso waits for the UI thread and AsyncTask by default, but not for custom async work (Retrofit, coroutines, etc.). `IdlingResource` tells Espresso when your app is busy. + +```kotlin +// In production code (thin wrapper) +object NetworkIdlingResource { + private val counter = CountingIdlingResource("Network") + fun increment() = counter.increment() + fun decrement() = counter.decrement() + fun get(): IdlingResource = counter +} + +// In test setup +@Before +fun registerIdling() { + IdlingRegistry.getInstance().register(NetworkIdlingResource.get()) +} + +@After +fun unregisterIdling() { + IdlingRegistry.getInstance().unregister(NetworkIdlingResource.get()) +} +``` + +--- + +## 3. UI Automator (Cross-App & System UI) + +UI Automator can interact with any visible UI — system dialogs, notifications, other apps. Use it when Espresso can't reach outside your app's process. + +| Use Case | Why UI Automator | +|------------------------------|----------------------------------------| +| Runtime permission dialogs | System UI, outside app process | +| Notification actions | System notification shade | +| Device settings interaction | Settings app | +| Multi-app workflows | e.g., share to another app and return | + +```kotlin +@RunWith(AndroidJUnit4::class) +class PermissionFlowTest { + + private lateinit var device: UiDevice + + @Before + fun setup() { + device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) + } + + @Test + fun grantsCameraPermission_andOpensCamera() { + // Trigger permission request from within your app + onView(withId(R.id.camera_button)).perform(click()) + + // Handle the system permission dialog via UI Automator + val allowButton = device.findObject( + By.res("com.android.permissioncontroller:id/permission_allow_foreground_only_button") + ) + allowButton?.click() + + // Back in Espresso territory — verify the camera view appeared + onView(withId(R.id.camera_preview)).check(matches(isDisplayed())) + } + + @Test + fun notificationTap_opensDetail() { + // Open notification shade + device.openNotification() + device.wait(Until.hasObject(By.textStartsWith("New message")), 5000) + + // Tap the notification + val notification = device.findObject(By.textStartsWith("New message")) + notification.click() + + // Verify deep-link target + onView(withId(R.id.message_detail)).check(matches(isDisplayed())) + } +} +``` + +--- + +## 4. Compose UI Testing + +Compose has its own testing framework that works with the semantic tree rather than the view hierarchy. Tests can run as local tests (with Robolectric) or instrumentation tests — the API is the same. + +### Basic Setup + +```kotlin +@RunWith(AndroidJUnit4::class) +class GreetingScreenTest { + + @get:Rule + val composeTestRule = createComposeRule() + + @Test + fun displaysGreeting_andRespondsToClick() { + composeTestRule.setContent { + MyAppTheme { + GreetingScreen(name = "World") + } + } + + composeTestRule.onNodeWithText("Hello, World!") + .assertIsDisplayed() + + composeTestRule.onNodeWithText("Say Hi") + .performClick() + + composeTestRule.onNodeWithText("Hi back!") + .assertIsDisplayed() + } +} +``` + +### Finders, Assertions & Actions + +| Category | API | Example | +|------------|----------------------------------------------|---------------------------------| +| **Find** | `onNodeWithText("x")` | Matches visible text | +| | `onNodeWithTag("x")` | Matches `Modifier.testTag("x")` | +| | `onNodeWithContentDescription("x")` | Matches semantics label | +| | `onAllNodesWithTag("x")` | Returns list of matches | +| **Assert** | `assertIsDisplayed()` | Node is visible | +| | `assertTextEquals("x")` | Exact text match | +| | `assertIsEnabled()` / `assertIsNotEnabled()` | Enabled state | +| | `assertDoesNotExist()` | Node not in tree | +| | `assertCountEquals(n)` | For `onAllNodes` | +| **Act** | `performClick()` | Tap | +| | `performTextInput("x")` | Type into text field | +| | `performScrollTo()` | Scroll node into view | +| | `performTouchInput { swipeUp() }` | Gestures | + +### Using testTag for Reliable Selectors + +Text-based finders break with localization or copy changes. Use `testTag` for stable selectors: + +```kotlin +// ❌ Fragile: breaks if text changes or app is localized +composeTestRule.onNodeWithText("Submit Order").performClick() + +// ✅ Stable: testTag doesn't change with locale +composeTestRule.onNodeWithTag("submit_order_button").performClick() +``` + +```kotlin +// In production Composable +Button( + onClick = { /* ... */ }, + modifier = Modifier.testTag("submit_order_button") +) { + Text(stringResource(R.string.submit_order)) +} +``` + +### Testing with Activity Context + +When your Composable needs a `ComponentActivity` (e.g., for `viewModel()` or navigation), use `createAndroidComposeRule`: + +```kotlin +@get:Rule +val composeTestRule = createAndroidComposeRule() + +@Test +fun fullScreen_endToEnd() { + // Activity is already launched — interact with the real content + composeTestRule.onNodeWithTag("login_email") + .performTextInput("user@test.com") + composeTestRule.onNodeWithTag("login_password") + .performTextInput("pass123") + composeTestRule.onNodeWithTag("login_submit") + .performClick() + + composeTestRule.waitUntil(timeoutMillis = 5000) { + composeTestRule.onAllNodesWithTag("home_screen") + .fetchSemanticsNodes().isNotEmpty() + } + + composeTestRule.onNodeWithTag("home_screen") + .assertIsDisplayed() +} +``` + +### Testing Navigation + +```kotlin +@Test +fun navigatesToDetail_onItemClick() { + val navController = TestNavHostController(ApplicationProvider.getApplicationContext()) + + composeTestRule.setContent { + navController.navigatorProvider.addNavigator(ComposeNavigator()) + MyAppNavHost(navController = navController) + } + + // Click item on list screen + composeTestRule.onNodeWithTag("item_0").performClick() + + // Verify navigation destination + assertEquals("detail/0", navController.currentBackStackEntry?.destination?.route) +} +``` + +### Common Compose Test Mistakes + +```kotlin +// ❌ Wrong: Asserting immediately after async operation +composeTestRule.onNodeWithTag("submit").performClick() +composeTestRule.onNodeWithText("Success").assertIsDisplayed() // may fail — UI hasn't updated yet + +// ✅ Correct: Wait for the UI to settle +composeTestRule.onNodeWithTag("submit").performClick() +composeTestRule.waitForIdle() +composeTestRule.onNodeWithText("Success").assertIsDisplayed() + +// ✅ Also correct: waitUntil for longer async work +composeTestRule.onNodeWithTag("submit").performClick() +composeTestRule.waitUntil(timeoutMillis = 3000) { + composeTestRule.onAllNodesWithText("Success") + .fetchSemanticsNodes().isNotEmpty() +} +``` + +--- + +## 5. Gradle Managed Devices + +Define emulator profiles in `build.gradle.kts` so anyone (including CI) can run instrumentation tests without manually creating AVDs. Gradle downloads the system image, creates the emulator, runs tests, and tears it down automatically. + +### Device Configuration + +In `app/build.gradle.kts`: + +```kotlin +android { + testOptions { + managedDevices { + localDevices { + create("pixel6Api34") { + device = "Pixel 6" + apiLevel = 34 + systemImageSource = "aosp-atd" // ATD = faster, headless + } + create("pixel4Api30") { + device = "Pixel 4" + apiLevel = 30 + systemImageSource = "aosp-atd" + } + create("smallTabletApi34") { + device = "Nexus 7" + apiLevel = 34 + systemImageSource = "google" // full Google APIs image + } + } + + // Group devices for matrix testing + groups { + create("phoneTests") { + targetDevices.add(devices["pixel6Api34"]) + targetDevices.add(devices["pixel4Api30"]) + } + create("allDevices") { + targetDevices.add(devices["pixel6Api34"]) + targetDevices.add(devices["pixel4Api30"]) + targetDevices.add(devices["smallTabletApi34"]) + } + } + } + } +} +``` + +### System Image Sources + +| Source | Description | Best For | +|----------------|---------------------------------------------------|------------------------------| +| `"aosp-atd"` | Automated Test Device — minimal, no Play Services | Fast CI, pure logic tests | +| `"google-atd"` | ATD with Google APIs | Tests needing Maps, Firebase | +| `"aosp"` | Full AOSP image | Standard emulator testing | +| `"google"` | Full image with Google Play Services | Play Services integration | + +ATD images boot faster and consume less memory because they strip out UI chrome and preinstalled apps irrelevant to testing. Prefer `aosp-atd` or `google-atd` for CI pipelines. + +### Running Tests + +```bash +# Run on a single managed device +./gradlew pixel6Api34DebugAndroidTest + +# Run on a device group (all devices in parallel if hardware allows) +./gradlew phoneTestsGroupDebugAndroidTest +./gradlew allDevicesGroupDebugAndroidTest + +# With specific flavor +./gradlew pixel6Api34DevDebugAndroidTest + +# Enable test sharding across devices (speeds up large suites) +./gradlew allDevicesGroupDebugAndroidTest \ + -Pandroid.experimental.androidTest.numManagedDeviceShards=2 + +# Generate HTML test report +./gradlew pixel6Api34DebugAndroidTest \ + --continue # don't stop on first failure +``` + +Test results are written to `app/build/reports/androidTests/managedDevice/`. diff --git a/android-native-dev/references/visual-design.md b/android-native-dev/references/visual-design.md new file mode 100644 index 0000000..b1599e1 --- /dev/null +++ b/android-native-dev/references/visual-design.md @@ -0,0 +1,246 @@ +# Visual Design Guidelines + +Detailed specifications for colors, typography, spacing, elevation, and shapes in Material Design 3. + +## Color System + +### Color Roles (Tokens) + +Material Design 3 uses a token-based color system with three accent groups: + +| Role | Usage | +|------|-------| +| **Primary** | Key components, FAB, prominent buttons | +| **Secondary** | Less prominent components, filters, chips | +| **Tertiary** | Accent, complementary elements | +| **Error** | Error states, destructive actions | +| **Surface** | Backgrounds, cards, dialogs | + +Each role includes variants: base color, onColor, container, onContainer. + +### Color Contrast Requirements + +| Element | Minimum Contrast Ratio | Notes | +|---------|----------------------|-------| +| Body text | **4.5:1** | WCAG AA compliance | +| Large text (18sp+) | **3:1** | 14sp bold also qualifies | +| UI components | **3:1** | Icons, borders, controls | +| Focus indicators | **3:1** | Must be clearly visible | + +### Recommended Color Palettes + +#### Modern Professional (Business Apps) + +| Role | Color | Name | +|------|-------|------| +| Primary | #1976D2 | Blue 700 | +| Secondary | #455A64 | Blue Grey 700 | +| Tertiary | #00897B | Teal 600 | +| Background | #FAFAFA | Grey 50 | + +#### Vibrant & Playful (Consumer Apps) + +| Role | Color | Name | +|------|-------|------| +| Primary | #6200EE | Deep Purple | +| Secondary | #03DAC6 | Teal Accent | +| Tertiary | #FF5722 | Deep Orange | +| Background | #FFFFFF | White | + +#### Dark & Elegant (Premium Apps) + +| Role | Color | Name | +|------|-------|------| +| Primary | #BB86FC | Purple 200 | +| Secondary | #03DAC6 | Teal 200 | +| Tertiary | #CF6679 | Red 200 | +| Background | #121212 | Dark surface | + +#### Nature & Wellness (Health Apps) + +| Role | Color | Name | +|------|-------|------| +| Primary | #4CAF50 | Green 500 | +| Secondary | #8BC34A | Light Green 500 | +| Tertiary | #FFEB3B | Yellow 500 | +| Background | #F1F8E9 | Light Green 50 | + +#### Finance & Trust (Banking Apps) + +| Role | Color | Name | +|------|-------|------| +| Primary | #00695C | Teal 800 | +| Secondary | #37474F | Blue Grey 800 | +| Tertiary | #FFC107 | Amber 500 | +| Background | #ECEFF1 | Blue Grey 50 | + +### Dark Theme Requirements + +- Background: #121212 or darker +- Surface colors use elevation-based tonal overlay +- Primary colors should be lighter variants (200-300 range) +- Maintain contrast ratios in dark mode +- Test all states (hover, focus, pressed) in dark mode + +## Typography System + +### Type Scale + +| Style | Size | Weight | Line Height | Usage | +|-------|------|--------|-------------|-------| +| Display Large | 57sp | 400 | 64sp | Hero text | +| Display Medium | 45sp | 400 | 52sp | Large headers | +| Display Small | 36sp | 400 | 44sp | Section headers | +| Headline Large | 32sp | 400 | 40sp | Screen titles | +| Headline Medium | 28sp | 400 | 36sp | Subsection titles | +| Headline Small | 24sp | 400 | 32sp | Card titles | +| Title Large | 22sp | 400 | 28sp | App bar titles | +| Title Medium | 16sp | 500 | 24sp | List item titles | +| Title Small | 14sp | 500 | 20sp | Tabs | +| Body Large | 16sp | 400 | 24sp | Primary body text | +| Body Medium | 14sp | 400 | 20sp | Secondary body text | +| Body Small | 12sp | 400 | 16sp | Captions | +| Label Large | 14sp | 500 | 20sp | Button text | +| Label Medium | 12sp | 500 | 16sp | Navigation labels | +| Label Small | 11sp | 500 | 16sp | Badges | + +### Recommended Fonts + +| Category | Fonts | +|----------|-------| +| Primary | Roboto (system default) | +| Display | Roboto Serif, Google Sans | +| Monospace | Roboto Mono, JetBrains Mono | + +### Text Readability + +- **Line length**: 45-75 characters per line (including spaces) +- **Paragraph spacing**: 1.5x line height between paragraphs +- **Letter spacing**: Use default unless brand requires adjustment +- **Text alignment**: Left-aligned for body text (LTR languages) + +## Spacing & Layout + +### 8dp Grid System + +All spacing values should be multiples of 8dp (with 4dp for fine adjustments). + +| Token | Value | Usage | +|-------|-------|-------| +| xs | 4dp | Icon padding, fine adjustments | +| sm | 8dp | Tight spacing, inline elements | +| md | 16dp | Default padding, card content | +| lg | 24dp | Section spacing | +| xl | 32dp | Large gaps, group separation | +| xxl | 48dp | Screen margins, major sections | + +### Component Dimensions + +| Component | Height | Min Width | Notes | +|-----------|--------|-----------|-------| +| Button | 40dp | 64dp | Touch target 48dp | +| FAB | 56dp | 56dp | Standard size | +| Mini FAB | 40dp | 40dp | Secondary actions | +| Extended FAB | 56dp | 80dp | With text label | +| Text Field | 56dp | 280dp | Including label | +| App Bar | 64dp | - | Top app bar | +| Bottom Nav | 80dp | - | With labels | +| Nav Rail | - | 80dp | Tablet/desktop | +| List Item | 56-88dp | - | Depends on content | +| Chip | 32dp | - | Filter/action chips | + +### Touch Targets + +| Type | Size | Notes | +|------|------|-------| +| Minimum | 48 × 48dp | WCAG requirement | +| Recommended | 56 × 56dp | Primary actions | +| Kids apps | 56dp+ | Larger for motor skills | +| Spacing | 8dp minimum | Between adjacent targets | + +## Elevation & Shadows + +### Elevation Levels + +| Level | Elevation | Usage | +|-------|-----------|-------| +| Level 0 | 0dp | Flat surfaces | +| Level 1 | 1dp | Cards, elevated buttons | +| Level 2 | 3dp | FAB (resting), raised elements | +| Level 3 | 6dp | Navigation drawer, bottom sheet | +| Level 4 | 8dp | FAB (pressed), menus | +| Level 5 | 12dp | Dialogs, modal surfaces | + +### Shadow Guidelines + +- Use elevation consistently for same component types +- Higher elevation = more important/prominent +- In dark theme, use surface tint instead of shadows +- Avoid excessive elevation (keeps UI grounded) + +## Shape System + +### Corner Radius + +| Size | Radius | Usage | +|------|--------|-------| +| None | 0dp | Sharp edges, dividers | +| Extra Small | 4dp | Badges, small chips | +| Small | 8dp | Buttons, chips, small cards | +| Medium | 12dp | Cards, dialogs, text fields | +| Large | 16dp | FAB, bottom sheets | +| Extra Large | 28dp | Large sheets, expanded cards | +| Full | 50% | Pills, avatars, circular buttons | + +### M3 Expressive Shapes + +Material 3 Expressive introduces 35 new decorative shapes: +- Organic curves +- Asymmetric corners +- Cut corners +- Scalloped edges + +Use sparingly for brand differentiation and visual interest. + +### Shape Consistency Rules + +- Same component type = same shape +- Related components should share shape family +- Don't mix too many shape styles on one screen +- Consider shape in dark/light theme transitions + +## Icons + +### Size Specifications + +| Size | Dimensions | Usage | +|------|------------|-------| +| Small | 20 × 20dp | Compact UI, inline | +| Standard | 24 × 24dp | Default for most uses | +| Large | 40 × 40dp | Emphasis, empty states | + +### Icon Guidelines + +- **Touch target**: Always wrap in 48dp minimum clickable area +- **Style**: Outlined (default), Filled (selected/active states) +- **Stroke width**: 2dp for outlined icons +- **Optical alignment**: May need visual adjustments +- **Color**: Use semantic colors (primary, error, etc.) + +### Recommended Icon Sets + +| Set | Usage | +|-----|-------| +| Material Symbols | Recommended, variable font support | +| Material Icons | Legacy, still widely used | + +### Adaptive Icons (App Icon) + +| Property | Value | +|----------|-------| +| Canvas size | 108 × 108dp | +| Safe zone | 66 × 66dp (centered circle) | +| Logo size | 48-66dp | +| Max display | 72 × 72dp | +| Layers | Foreground + Background (both 108dp) | +| Android 13+ | Include monochrome layer for theming |