Skip to main content
skill Claude Code CX Codex Cursor GitHub Copilot

android-native-dev — Claude Code skill: what it does, how to install

Guides AI agents through native Android development: project scaffolding, Gradle setup, Jetpack Compose dependencies, and product flavors.

Install into Claude Code · Skills CLI (Vercel) All methods and platforms ↓
npx skills add MiniMax-AI/skills --skill android-native-dev -a claude-code
Open on GitHub

What android-native-dev does

The skill provides step-by-step guidance for native Android development using Kotlin and Jetpack Compose. Before writing application logic, it assesses the workspace to confirm whether a Gradle Wrapper and proper module files exist. The primary rule is straightforward: ./gradlew assembleDebug must compile cleanly before adding features.

Inside, you get configuration baselines for gradle.properties, JVM memory allocation guidelines to prevent OutOfMemoryError, and BOM-based Compose dependency management. It also details multi-environment setups via productFlavors (such as dev, staging, and prod), handling custom application ID suffixes and flavor-specific source sets.

A critical detail: starting with AGP 8.0, BuildConfig is disabled by default, so the guide directs you to explicitly enable buildFeatures.buildConfig inside build.gradle.kts.

What it helps with

  • Set up a clean Android project structure with Gradle Wrapper and AndroidX
  • Configure build variants and product flavors for multiple environments
  • Resolve JVM memory limits during heavy Gradle compilation tasks
  • Manage Jetpack Compose dependencies using Compose BOM

Example prompts for the agent

  • Configure dev, staging, and prod product flavors with distinct API base URLs in build.gradle.kts.
  • Check this Android project directory for missing configuration files and fix gradle.properties.
  • Run assembleDevDebug using gradlew and prepare the install command for adb.

How to install android-native-dev

1Skills CLI (Vercel)

npx skills add MiniMax-AI/skills --skill android-native-dev -a claude-code

2Manually: put the files in place

~/.claude/skills/android-native-dev/SKILL.md

Copy the whole skill folder; for one project use .claude/skills/android-native-dev/

From the same set17

The whole set →
pptx-generator MiniMax-AI A skill to read, edit, and build PowerPoint decks from scratch using PptxGenJS and XML workflows. Useful when generating presentations from code or extracting slide text. Claude Code CX Codex Cursor +1 6,000installs minimax-docx MiniMaxAI A skill for generating, filling and formatting Word documents with OpenXML SDK. Use it when the agent must deliver a .docx, not plain text. Claude Code CX Codex Cursor +1 4,500installs minimax-xlsx MiniMax-AI Skill to create, read, edit, and validate Excel files (.xlsx, .xlsm, .csv, .tsv) directly in code. Use it when you need to build a financial model, add calculated columns, or fix broken formulas while preserving formatting and macros. Claude Code CX Codex Cursor +1 3,800installs minimax-pdf MiniMax-AI Generates professional PDFs with visual identity: reports, resumes, portfolios. Design is derived from document type, with consistent color and typography throughout. Claude Code CX Codex Cursor +1 3,800installs fullstack-dev MiniMax-AI Guides full-stack backend architecture and frontend integration. It enforces a structured checklist from requirement gathering to verification smoke tests. Claude Code CX Codex Cursor +1 3,400installs frontend-dev MiniMax-AI Orchestrates frontend development, motion design, AI-generated media, and conversion copy. Built for landing pages, marketing sites, and dashboards. Claude Code CX Codex Cursor +1 3,200installs vision-analysis MiniMax-AI Describes images, extracts text, and reviews UI mockups using the MiniMax vision MCP tool. Handy when an agent needs to understand a screenshot or design file. Claude Code CX Codex Cursor +1 2,700installs shader-dev MiniMax-AI GLSL reference skill covering 36 real-time shader techniques for ShaderToy and WebGL2. Helpful for ray marching, SDF scenes, fluids, and visual effects. Claude Code CX Codex Cursor +1 2,500installs

Similar

argent-ios-device-setup software-mansion Guides coding agents through setting up a cabled physical iPhone for argent automation. Essential when testing on real hardware instead of an iOS simulator. Claude Code CX Codex Cursor +1 10,900installs argent-ios-device-interact software-mansion Drives a physical iPhone through argent for app checks, gestures, screenshots, and UI waits. Use it when the target is a real device, not a simulator. Claude Code CX Codex Cursor +1 10,900installs appllama-app-design-skill Appllama (appllama.io) Skill for building native iOS and Android screens with Expo and React Native. Designs with Apple HIG fidelity, semantic colors, Reanimated motion, proper navigation semantics, and simulator-verified iteration. Claude Code CX Codex Cursor +1 4,700installs eas-update expo Skill for configuring and publishing updates via EAS Update. Delivers JavaScript and asset changes to installed apps without rebuilding native code. Claude Code CX Codex Cursor +1 10,600installs kotlin-specialist https://github.com/Jeffallan This skill guides Kotlin work with coroutines, Flow, KMP, Compose, and Ktor. Use it when an implementation needs structured concurrency, shared code, or Kotlin UI patterns. Claude Code CX Codex Cursor +1 5,200installs react-native-expert https://github.com/Jeffallan A skill for AI coding agents that helps build React Native and Expo apps: navigation, native modules, list performance, platform-specific code for iOS and Android. Claude Code CX Codex Cursor +1 4,900installs vercel-react-native-skills vercel A rules pack for React Native and Expo work: lists, animations, navigation, native modules. Load it so your agent makes mobile changes the same way every time. Claude Code CX Codex Cursor +1 253,900installs animate-expo emilkowalski Builds React Native and Expo animations using Reanimated and Gesture Handler. Helps keep motion on the UI thread and avoid dropped frames on physical devices. Claude Code CX Codex Cursor +1 135,900installs

Questions about android-native-dev

How to install android-native-dev in Claude Code?

Run npx skills add MiniMax-AI/skills --skill android-native-dev -a claude-code. Once finished, the skill is immediately active in your agent sessions.

What does android-native-dev do?

It guides agents through Android native development rules, Gradle builds, Compose UI setups, and multi-flavor variant management.

Why is BuildConfig missing in newer Android Gradle Plugin versions?

AGP 8.0 stops generating BuildConfig automatically. You must add buildFeatures { buildConfig = true } inside build.gradle.kts to access flavor constants.

Source text

SKILL.md · MIT
---
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<Data> {
    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<String, User>()  // 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<Data?>(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> = _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
<!-- res/mipmap-anydpi-v26/ic_launcher.xml -->
<adaptive-icon>
    <background android:drawable="@color/ic_launcher_background"/>
    <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>
```

### 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
<!-- ❌ Wrong: Using reserved names -->
<color name="background">#FFFFFF</color>
<color name="icon">#000000</color>

<!-- ✅ Correct: Add prefix or specific naming -->
<color name="app_background">#FFFFFF</color>
<color name="icon_primary">#000000</color>
```

```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
<!-- ❌ Wrong: Drawable name conflicts -->
<ImageView android:src="@drawable/icon" />

<!-- ✅ Correct: Add prefix -->
<ImageView android:src="@drawable/ic_home" />
```

---

## 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:<version>")                          // 4.13.2+
    testImplementation("org.robolectric:robolectric:<version>")          // 4.16.1+
    testImplementation("io.mockk:mockk:<version>")                      // match Kotlin version
    testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:<version>")  // match coroutines-core
    testImplementation("androidx.arch.core:core-testing:<version>")      // InstantTaskExecutorRule for LiveData
    testImplementation("app.cash.turbine:turbine:<version>")             // Flow/StateFlow testing

    // --- Instrumentation tests (src/androidTest/) ---
    androidTestImplementation("androidx.test.ext:junit:<version>")
    androidTestImplementation("androidx.test:runner:<version>")
    androidTestImplementation("androidx.test:rules:<version>")
    androidTestImplementation("androidx.test.espresso:espresso-core:<version>")
    androidTestImplementation("androidx.test.espresso:espresso-contrib:<version>")   // RecyclerView, Drawer
    androidTestImplementation("androidx.test.espresso:espresso-intents:<version>")   // Intent verification
    androidTestImplementation("androidx.test.espresso:espresso-idling-resource:<version>")
    androidTestImplementation("androidx.test.uiautomator:uiautomator:<version>")

    // --- 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
```

© MiniMax-AI · MIT · MiniMax-AI/skills

See also

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor