Smithery Logo
MCPsSkillsDocsPricing
Login
Smithery Logo

Accelerating the Agent Economy

Resources

DocumentationPrivacy PolicySystem Status

Company

PricingAboutBlog

Connect

© 2026 Smithery. All rights reserved.

    travisjneuman

    android-development

    travisjneuman/android-development
    Coding
    3
    4 installs

    About

    SKILL.md

    Install

    Install via Skills CLI

    or add to your agent
    • Claude Code
      Claude Code
    • Codex
      Codex
    • OpenClaw
      OpenClaw
    • Cursor
      Cursor
    • Amp
      Amp
    • GitHub Copilot
      GitHub Copilot
    • Gemini CLI
      Gemini CLI
    • Kilo Code
      Kilo Code
    • Junie
      Junie
    • Replit
      Replit
    • Windsurf
      Windsurf
    • Cline
      Cline
    • Continue
      Continue
    • OpenCode
      OpenCode
    • OpenHands
      OpenHands
    • Roo Code
      Roo Code
    • Augment
      Augment
    • Goose
      Goose
    • Trae
      Trae
    • Zencoder
      Zencoder
    • Antigravity
      Antigravity
    ├─
    ├─
    └─

    About

    Android development with Kotlin, Jetpack Compose, and modern Android architecture. Use when building Android apps, implementing Material Design, or following Android best practices.

    SKILL.md

    Android Development

    Comprehensive guide for building modern Android applications.

    Platforms Covered

    Platform Min SDK Target SDK
    Android Phone API 24 (7.0) API 34 (14)
    Android Tablet API 24 API 34
    Android TV API 24 API 34
    Wear OS API 30 API 34
    Android Auto API 29 API 34

    Jetpack Compose (Modern UI)

    Basic Structure

    @Composable
    fun MyApp() {
        MaterialTheme {
            Surface(
                modifier = Modifier.fillMaxSize(),
                color = MaterialTheme.colorScheme.background
            ) {
                MainScreen()
            }
        }
    }
    
    @Composable
    fun MainScreen() {
        var count by remember { mutableStateOf(0) }
    
        Column(
            modifier = Modifier
                .fillMaxSize()
                .padding(16.dp),
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.Center
        ) {
            Text(
                text = "Count: $count",
                style = MaterialTheme.typography.headlineLarge
            )
    
            Spacer(modifier = Modifier.height(16.dp))
    
            Button(onClick = { count++ }) {
                Text("Increment")
            }
        }
    }
    

    State Management

    // Local state
    var text by remember { mutableStateOf("") }
    
    // State hoisting
    @Composable
    fun StatefulCounter() {
        var count by remember { mutableStateOf(0) }
        StatelessCounter(count = count, onIncrement = { count++ })
    }
    
    @Composable
    fun StatelessCounter(count: Int, onIncrement: () -> Unit) {
        Button(onClick = onIncrement) {
            Text("Count: $count")
        }
    }
    
    // ViewModel state
    @HiltViewModel
    class MainViewModel @Inject constructor(
        private val repository: ItemRepository
    ) : ViewModel() {
    
        private val _uiState = MutableStateFlow(UiState())
        val uiState: StateFlow<UiState> = _uiState.asStateFlow()
    
        fun loadItems() {
            viewModelScope.launch {
                _uiState.update { it.copy(isLoading = true) }
                try {
                    val items = repository.getItems()
                    _uiState.update { it.copy(items = items, isLoading = false) }
                } catch (e: Exception) {
                    _uiState.update { it.copy(error = e.message, isLoading = false) }
                }
            }
        }
    }
    
    // Collecting in Compose
    @Composable
    fun MainScreen(viewModel: MainViewModel = hiltViewModel()) {
        val uiState by viewModel.uiState.collectAsStateWithLifecycle()
    
        when {
            uiState.isLoading -> LoadingIndicator()
            uiState.error != null -> ErrorMessage(uiState.error!!)
            else -> ItemList(uiState.items)
        }
    }
    

    Navigation

    // Navigation setup
    @Composable
    fun AppNavigation() {
        val navController = rememberNavController()
    
        NavHost(navController = navController, startDestination = "home") {
            composable("home") {
                HomeScreen(
                    onNavigateToDetail = { id ->
                        navController.navigate("detail/$id")
                    }
                )
            }
            composable(
                route = "detail/{itemId}",
                arguments = listOf(navArgument("itemId") { type = NavType.StringType })
            ) { backStackEntry ->
                val itemId = backStackEntry.arguments?.getString("itemId")
                DetailScreen(itemId = itemId)
            }
        }
    }
    
    // Type-safe navigation (recommended)
    @Serializable
    data class DetailRoute(val itemId: String)
    
    navController.navigate(DetailRoute(itemId = "123"))
    

    Lists

    @Composable
    fun ItemList(items: List<Item>) {
        LazyColumn(
            modifier = Modifier.fillMaxSize(),
            contentPadding = PaddingValues(16.dp),
            verticalArrangement = Arrangement.spacedBy(8.dp)
        ) {
            items(
                items = items,
                key = { it.id }
            ) { item ->
                ItemCard(item = item)
            }
        }
    }
    
    // Pull to refresh
    @OptIn(ExperimentalMaterial3Api::class)
    @Composable
    fun RefreshableList(
        items: List<Item>,
        isRefreshing: Boolean,
        onRefresh: () -> Unit
    ) {
        val pullRefreshState = rememberPullToRefreshState()
    
        PullToRefreshBox(
            isRefreshing = isRefreshing,
            onRefresh = onRefresh,
            state = pullRefreshState
        ) {
            LazyColumn { /* content */ }
        }
    }
    

    Modern Android Architecture

    Clean Architecture Layers

    app/
    ├── data/
    │   ├── local/
    │   │   ├── AppDatabase.kt
    │   │   └── ItemDao.kt
    │   ├── remote/
    │   │   ├── ApiService.kt
    │   │   └── ItemDto.kt
    │   └── repository/
    │       └── ItemRepositoryImpl.kt
    ├── domain/
    │   ├── model/
    │   │   └── Item.kt
    │   ├── repository/
    │   │   └── ItemRepository.kt
    │   └── usecase/
    │       └── GetItemsUseCase.kt
    ├── presentation/
    │   ├── home/
    │   │   ├── HomeScreen.kt
    │   │   └── HomeViewModel.kt
    │   └── navigation/
    │       └── AppNavigation.kt
    └── di/
        └── AppModule.kt
    

    Dependency Injection (Hilt)

    @HiltAndroidApp
    class MyApplication : Application()
    
    @Module
    @InstallIn(SingletonComponent::class)
    object AppModule {
    
        @Provides
        @Singleton
        fun provideDatabase(@ApplicationContext context: Context): AppDatabase {
            return Room.databaseBuilder(
                context,
                AppDatabase::class.java,
                "app_database"
            ).build()
        }
    
        @Provides
        @Singleton
        fun provideApiService(): ApiService {
            return Retrofit.Builder()
                .baseUrl("https://api.example.com/")
                .addConverterFactory(GsonConverterFactory.create())
                .build()
                .create(ApiService::class.java)
        }
    
        @Provides
        @Singleton
        fun provideItemRepository(
            apiService: ApiService,
            database: AppDatabase
        ): ItemRepository {
            return ItemRepositoryImpl(apiService, database.itemDao())
        }
    }
    

    Data Layer

    Room Database

    @Entity(tableName = "items")
    data class ItemEntity(
        @PrimaryKey val id: String,
        val name: String,
        val createdAt: Long
    )
    
    @Dao
    interface ItemDao {
        @Query("SELECT * FROM items ORDER BY createdAt DESC")
        fun getItems(): Flow<List<ItemEntity>>
    
        @Insert(onConflict = OnConflictStrategy.REPLACE)
        suspend fun insertItems(items: List<ItemEntity>)
    
        @Delete
        suspend fun deleteItem(item: ItemEntity)
    }
    
    @Database(entities = [ItemEntity::class], version = 1)
    abstract class AppDatabase : RoomDatabase() {
        abstract fun itemDao(): ItemDao
    }
    

    Retrofit API

    interface ApiService {
        @GET("items")
        suspend fun getItems(): List<ItemDto>
    
        @POST("items")
        suspend fun createItem(@Body item: CreateItemRequest): ItemDto
    
        @DELETE("items/{id}")
        suspend fun deleteItem(@Path("id") id: String)
    }
    
    data class ItemDto(
        val id: String,
        val name: String,
        @SerializedName("created_at") val createdAt: String
    )
    

    Repository Pattern

    interface ItemRepository {
        fun getItems(): Flow<List<Item>>
        suspend fun refreshItems()
        suspend fun deleteItem(id: String)
    }
    
    class ItemRepositoryImpl @Inject constructor(
        private val apiService: ApiService,
        private val itemDao: ItemDao
    ) : ItemRepository {
    
        override fun getItems(): Flow<List<Item>> {
            return itemDao.getItems().map { entities ->
                entities.map { it.toDomain() }
            }
        }
    
        override suspend fun refreshItems() {
            val items = apiService.getItems()
            itemDao.insertItems(items.map { it.toEntity() })
        }
    }
    

    Material Design 3

    Theme Setup

    @Composable
    fun MyAppTheme(
        darkTheme: Boolean = isSystemInDarkTheme(),
        dynamicColor: Boolean = true,
        content: @Composable () -> Unit
    ) {
        val colorScheme = when {
            dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
                val context = LocalContext.current
                if (darkTheme) dynamicDarkColorScheme(context)
                else dynamicLightColorScheme(context)
            }
            darkTheme -> DarkColorScheme
            else -> LightColorScheme
        }
    
        MaterialTheme(
            colorScheme = colorScheme,
            typography = Typography,
            content = content
        )
    }
    

    Common Components

    // Top App Bar
    @OptIn(ExperimentalMaterial3Api::class)
    @Composable
    fun MyTopBar(
        title: String,
        onBackClick: () -> Unit
    ) {
        TopAppBar(
            title = { Text(title) },
            navigationIcon = {
                IconButton(onClick = onBackClick) {
                    Icon(Icons.Default.ArrowBack, contentDescription = "Back")
                }
            },
            actions = {
                IconButton(onClick = { /* menu */ }) {
                    Icon(Icons.Default.MoreVert, contentDescription = "Menu")
                }
            }
        )
    }
    
    // Bottom Navigation
    @Composable
    fun MyBottomBar(
        selectedTab: Int,
        onTabSelected: (Int) -> Unit
    ) {
        NavigationBar {
            NavigationBarItem(
                icon = { Icon(Icons.Default.Home, contentDescription = null) },
                label = { Text("Home") },
                selected = selectedTab == 0,
                onClick = { onTabSelected(0) }
            )
            NavigationBarItem(
                icon = { Icon(Icons.Default.Settings, contentDescription = null) },
                label = { Text("Settings") },
                selected = selectedTab == 1,
                onClick = { onTabSelected(1) }
            )
        }
    }
    

    Platform-Specific

    Android TV

    // Focus management
    @Composable
    fun TvButton(
        onClick: () -> Unit,
        content: @Composable () -> Unit
    ) {
        var isFocused by remember { mutableStateOf(false) }
    
        Box(
            modifier = Modifier
                .onFocusChanged { isFocused = it.isFocused }
                .focusable()
                .clickable(onClick = onClick)
                .background(
                    if (isFocused) MaterialTheme.colorScheme.primary
                    else MaterialTheme.colorScheme.surface
                )
        ) {
            content()
        }
    }
    

    Wear OS

    @Composable
    fun WearApp() {
        MaterialTheme {
            ScalingLazyColumn(
                modifier = Modifier.fillMaxSize(),
                anchorType = ScalingLazyListAnchorType.ItemCenter
            ) {
                item { TimeText() }
                item {
                    Chip(
                        onClick = { },
                        label = { Text("Action") }
                    )
                }
            }
        }
    }
    

    Testing

    Unit Tests

    @Test
    fun `getItems returns mapped domain objects`() = runTest {
        val repository = ItemRepositoryImpl(
            apiService = FakeApiService(),
            itemDao = FakeItemDao()
        )
    
        val items = repository.getItems().first()
    
        assertEquals(2, items.size)
        assertEquals("Item 1", items[0].name)
    }
    

    Compose UI Tests

    @HiltAndroidTest
    class MainScreenTest {
        @get:Rule
        val composeTestRule = createAndroidComposeRule<MainActivity>()
    
        @Test
        fun displayItems_whenLoaded() {
            composeTestRule.onNodeWithText("Item 1").assertIsDisplayed()
            composeTestRule.onNodeWithText("Item 2").assertIsDisplayed()
        }
    
        @Test
        fun navigateToDetail_onItemClick() {
            composeTestRule.onNodeWithText("Item 1").performClick()
            composeTestRule.onNodeWithText("Item Details").assertIsDisplayed()
        }
    }
    

    Play Store Requirements

    Required

    • Privacy policy
    • App icon (512x512)
    • Feature graphic (1024x500)
    • Screenshots (min 2)
    • Target API 34+

    App Bundle

    android {
        bundle {
            language {
                enableSplit = true
            }
            density {
                enableSplit = true
            }
            abi {
                enableSplit = true
            }
        }
    }
    

    Best Practices

    DO:

    • Use Kotlin Coroutines and Flow
    • Follow unidirectional data flow
    • Use Jetpack Compose for new UI
    • Implement proper lifecycle handling
    • Support multiple screen sizes

    DON'T:

    • Block main thread
    • Leak contexts
    • Hardcode dimensions
    • Ignore process death
    • Skip ProGuard rules

    Kotlin Multiplatform (KMP) / Compose Multiplatform (CMP)

    Shared Business Logic with KMP

    // shared/src/commonMain/kotlin/UserRepository.kt
    expect class PlatformContext
    
    class UserRepository(private val api: ApiService, private val db: Database) {
        suspend fun getUsers(): List<User> {
            return try {
                val remote = api.fetchUsers()
                db.saveUsers(remote)
                remote
            } catch (e: Exception) {
                db.getCachedUsers()
            }
        }
    }
    
    // shared/src/androidMain/kotlin/PlatformContext.kt
    actual class PlatformContext(val context: Context)
    
    // shared/src/iosMain/kotlin/PlatformContext.kt
    actual class PlatformContext
    

    Compose Multiplatform (Shared UI)

    // shared/src/commonMain/kotlin/App.kt
    @Composable
    fun App() {
        MaterialTheme {
            var users by remember { mutableStateOf<List<User>>(emptyList()) }
            val repository = remember { UserRepository() }
    
            LaunchedEffect(Unit) {
                users = repository.getUsers()
            }
    
            LazyColumn {
                items(users) { user ->
                    UserCard(user)
                }
            }
        }
    }
    
    // Runs natively on Android, iOS, Desktop, and Web
    

    KMP Project Structure

    project/
    ├── shared/
    │   └── src/
    │       ├── commonMain/     # Shared Kotlin code
    │       ├── androidMain/    # Android-specific
    │       ├── iosMain/        # iOS-specific
    │       └── desktopMain/    # Desktop-specific
    ├── androidApp/             # Android entry point
    ├── iosApp/                 # iOS entry point (Swift/SwiftUI)
    └── desktopApp/             # Desktop entry point
    

    Navigation 3 (Type-Safe Navigation)

    // Define routes as serializable data classes
    @Serializable
    data object Home
    
    @Serializable
    data class Detail(val itemId: String)
    
    @Serializable
    data class Settings(val section: String? = null)
    
    // Navigation setup
    @Composable
    fun AppNavHost(navController: NavHostController = rememberNavController()) {
        NavHost(navController = navController, startDestination = Home) {
            composable<Home> {
                HomeScreen(onItemClick = { id ->
                    navController.navigate(Detail(itemId = id))
                })
            }
            composable<Detail> { backStackEntry ->
                val detail: Detail = backStackEntry.toRoute()
                DetailScreen(itemId = detail.itemId)
            }
            composable<Settings> { backStackEntry ->
                val settings: Settings = backStackEntry.toRoute()
                SettingsScreen(section = settings.section)
            }
        }
    }
    

    API 35 (Android 15) Features

    Feature Description
    Edge-to-edge enforced Apps must handle insets properly
    Predictive back System back gesture with preview animations
    Private Space User-controlled hidden app profile
    Satellite connectivity SMS/MMS over satellite
    App archival Auto-archive unused apps, preserve data
    Health Connect Updated health data APIs
    // Edge-to-edge (required on API 35+)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
    
        setContent {
            Scaffold(
                modifier = Modifier.fillMaxSize(),
                contentWindowInsets = ScaffoldDefaults.contentWindowInsets,
            ) { innerPadding ->
                MainContent(modifier = Modifier.padding(innerPadding))
            }
        }
    }
    
    // Predictive back with Compose
    @OptIn(ExperimentalMaterial3Api::class)
    @Composable
    fun DetailScreen(onBack: () -> Unit) {
        BackHandler(onBack = onBack)
        // Content renders with predictive back animation automatically
    }
    

    Baseline Profiles for Startup Performance

    // baselineprofile/build.gradle.kts
    dependencies {
        implementation("androidx.benchmark:benchmark-macro-junit4:1.3.0")
    }
    
    // BaselineProfileGenerator.kt
    @RunWith(AndroidJUnit4::class)
    class BaselineProfileGenerator {
        @get:Rule
        val rule = BaselineProfileRule()
    
        @Test
        fun generateBaselineProfile() {
            rule.collect(packageName = "com.example.app") {
                // Critical user journey
                pressHome()
                startActivityAndWait()
                device.findObject(By.text("Login")).click()
                device.wait(Until.hasObject(By.text("Dashboard")), 5000)
            }
        }
    }
    
    // app/build.gradle.kts
    android {
        buildTypes {
            release {
                // Baseline profiles improve startup by 15-30%
                baselineProfile.automaticGenerationDuringBuild = true
            }
        }
    }
    

    Credential Manager API

    // Modern sign-in replacing legacy APIs
    val credentialManager = CredentialManager.create(context)
    
    // Sign in with passkeys, passwords, or federated identity
    suspend fun signIn() {
        val request = GetCredentialRequest(listOf(
            GetPasswordOption(),
            GetPublicKeyCredentialOption(requestJson = passkeyRequestJson),
            GetGoogleIdOption(serverClientId = WEB_CLIENT_ID),
        ))
    
        try {
            val result = credentialManager.getCredential(context, request)
            handleSignIn(result)
        } catch (e: GetCredentialException) {
            handleSignInError(e)
        }
    }
    
    // Create a passkey
    suspend fun createPasskey() {
        val request = CreatePublicKeyCredentialRequest(
            requestJson = createPasskeyRequestJson
        )
        val result = credentialManager.createCredential(context, request)
        handlePasskeyCreation(result)
    }
    
    Recommended Servers
    Svelte
    Svelte
    Vercel Grep
    Vercel Grep
    Astro Docs
    Astro Docs
    Repository
    travisjneuman/.claude
    Files