# Android Developer Markdown Brain > Used by AI pipeline to plan, generate, and validate Android apps (Jetpack Compose, XML, SDK APIs). --- ## Project Structure (Compose) ``` app/ ├── src/main/ │ ├── AndroidManifest.xml │ ├── java/com/dolor3v/app/ │ │ ├── MainActivity.kt │ │ ├── ui/ │ │ │ ├── theme/ │ │ │ │ ├── Color.kt │ │ │ │ ├── Theme.kt │ │ │ │ └── Type.kt │ │ │ ├── screens/ │ │ │ │ ├── HomeScreen.kt │ │ │ │ └── DetailScreen.kt │ │ │ └── components/ │ │ ├── data/ │ │ │ ├── repository/ │ │ │ ├── model/ │ │ │ └── api/ │ │ ├── domain/ │ │ │ └── usecase/ │ │ └── di/ │ └── res/ │ ├── drawable/ │ ├── values/ │ │ ├── strings.xml │ │ ├── colors.xml │ │ └── themes.xml │ └── mipmap/ ├── build.gradle.kts └── proguard-rules.pro build.gradle.kts (project level) settings.gradle.kts gradle/libs.versions.toml ``` --- ## build.gradle.kts (App Level) ```kotlin plugins { alias(libs.plugins.android.application) alias(libs.plugins.kotlin.android) alias(libs.plugins.kotlin.compose) alias(libs.plugins.hilt) id("kotlin-kapt") } android { namespace = "com.dolor3v.app" compileSdk = 35 defaultConfig { applicationId = "com.dolor3v.app" minSdk = 26 targetSdk = 35 versionCode = 1 versionName = "1.0.0" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } buildTypes { release { isMinifyEnabled = true isShrinkResources = true proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") signingConfig = signingConfigs.getByName("release") } debug { applicationIdSuffix = ".debug" isDebuggable = true } } compileOptions { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 } kotlinOptions { jvmTarget = "17" } buildFeatures { compose = true buildConfig = true } } dependencies { // Core implementation(libs.androidx.core.ktx) implementation(libs.androidx.lifecycle.runtime.ktx) implementation(libs.androidx.activity.compose) // Compose BOM implementation(platform(libs.androidx.compose.bom)) implementation(libs.androidx.ui) implementation(libs.androidx.ui.graphics) implementation(libs.androidx.ui.tooling.preview) implementation(libs.androidx.material3) // Navigation implementation(libs.androidx.navigation.compose) // ViewModel implementation(libs.androidx.lifecycle.viewmodel.compose) // Hilt DI implementation(libs.hilt.android) kapt(libs.hilt.compiler) implementation(libs.androidx.hilt.navigation.compose) // Retrofit + OkHttp implementation(libs.retrofit) implementation(libs.retrofit.converter.gson) implementation(libs.okhttp.logging.interceptor) // Room implementation(libs.room.runtime) implementation(libs.room.ktx) kapt(libs.room.compiler) // Coil (images) implementation(libs.coil.compose) // DataStore implementation(libs.datastore.preferences) // Coroutines implementation(libs.kotlinx.coroutines.android) // Testing testImplementation(libs.junit) androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.espresso.core) androidTestImplementation(platform(libs.androidx.compose.bom)) androidTestImplementation(libs.androidx.ui.test.junit4) debugImplementation(libs.androidx.ui.tooling) } ``` --- ## Jetpack Compose Patterns ### MainActivity ```kotlin @AndroidEntryPoint class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() setContent { MyAppTheme { Surface( modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background ) { AppNavHost() } } } } } ``` ### Navigation ```kotlin @Composable fun AppNavHost( navController: NavHostController = rememberNavController(), startDestination: String = Screen.Home.route ) { NavHost(navController = navController, startDestination = startDestination) { composable(Screen.Home.route) { HomeScreen(navController = navController) } composable( route = Screen.Detail.route, arguments = listOf(navArgument("id") { type = NavType.IntType }) ) { backStack -> val id = backStack.arguments?.getInt("id") ?: 0 DetailScreen(id = id, navController = navController) } } } sealed class Screen(val route: String) { object Home : Screen("home") object Detail : Screen("detail/{id}") { fun createRoute(id: Int) = "detail/$id" } } ``` ### ViewModel + StateFlow ```kotlin @HiltViewModel class HomeViewModel @Inject constructor( private val repository: PostRepository ) : ViewModel() { private val _uiState = MutableStateFlow>>(UiState.Loading) val uiState: StateFlow>> = _uiState.asStateFlow() init { loadPosts() } fun loadPosts() { viewModelScope.launch { _uiState.value = UiState.Loading repository.getPosts() .onSuccess { _uiState.value = UiState.Success(it) } .onFailure { _uiState.value = UiState.Error(it.message ?: "Unknown error") } } } } sealed class UiState { object Loading : UiState() data class Success(val data: T) : UiState() data class Error(val message: String) : UiState() } ``` ### Screen with State Handling ```kotlin @Composable fun HomeScreen( viewModel: HomeViewModel = hiltViewModel(), navController: NavController ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() Scaffold( topBar = { TopAppBar(title = { Text("Home") }) } ) { padding -> Box(modifier = Modifier.padding(padding).fillMaxSize()) { when (val state = uiState) { is UiState.Loading -> CircularProgressIndicator(Modifier.align(Alignment.Center)) is UiState.Error -> Text(state.message, color = MaterialTheme.colorScheme.error) is UiState.Success -> PostList(posts = state.data, onPostClick = { id -> navController.navigate(Screen.Detail.createRoute(id)) }) } } } } ``` ### LazyColumn (RecyclerView equivalent) ```kotlin @Composable fun PostList(posts: List, onPostClick: (Int) -> Unit) { LazyColumn( contentPadding = PaddingValues(16.dp), verticalArrangement = Arrangement.spacedBy(8.dp) ) { items(posts, key = { it.id }) { post -> PostCard(post = post, onClick = { onPostClick(post.id) }) } } } @Composable fun PostCard(post: Post, onClick: () -> Unit) { Card( onClick = onClick, modifier = Modifier.fillMaxWidth(), elevation = CardDefaults.cardElevation(defaultElevation = 2.dp) ) { Column(modifier = Modifier.padding(16.dp)) { Text(post.title, style = MaterialTheme.typography.titleMedium) Spacer(Modifier.height(4.dp)) Text(post.excerpt, style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.onSurfaceVariant, maxLines = 2, overflow = TextOverflow.Ellipsis) } } } ``` --- ## Room Database ### Entity ```kotlin @Entity(tableName = "posts") data class PostEntity( @PrimaryKey val id: Int, @ColumnInfo(name = "title") val title: String, @ColumnInfo(name = "content") val content: String, @ColumnInfo(name = "created_at") val createdAt: Long = System.currentTimeMillis() ) ``` ### DAO ```kotlin @Dao interface PostDao { @Query("SELECT * FROM posts ORDER BY created_at DESC") fun getAllPosts(): Flow> @Query("SELECT * FROM posts WHERE id = :id") suspend fun getPostById(id: Int): PostEntity? @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertPosts(posts: List) @Delete suspend fun deletePost(post: PostEntity) @Query("DELETE FROM posts") suspend fun clearAll() } ``` ### Database ```kotlin @Database(entities = [PostEntity::class], version = 1, exportSchema = true) @TypeConverters(Converters::class) abstract class AppDatabase : RoomDatabase() { abstract fun postDao(): PostDao companion object { @Volatile private var INSTANCE: AppDatabase? = null fun getInstance(context: Context): AppDatabase = INSTANCE ?: synchronized(this) { Room.databaseBuilder(context, AppDatabase::class.java, "app.db") .fallbackToDestructiveMigration() .build().also { INSTANCE = it } } } } ``` --- ## Retrofit API Service ```kotlin interface ApiService { @GET("posts") suspend fun getPosts( @Query("page") page: Int = 1, @Query("per_page") perPage: Int = 20 ): Response> @GET("posts/{id}") suspend fun getPost(@Path("id") id: Int): Response @POST("posts") suspend fun createPost(@Body post: CreatePostDto): Response @Multipart @POST("media") suspend fun uploadMedia( @Part file: MultipartBody.Part, @Part("title") title: RequestBody ): Response } // Retrofit instance @Provides @Singleton fun provideRetrofit(): Retrofit = Retrofit.Builder() .baseUrl(BuildConfig.API_BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .client( OkHttpClient.Builder() .addInterceptor(HttpLoggingInterceptor().apply { level = HttpLoggingInterceptor.Level.BODY }) .addInterceptor { chain -> chain.proceed( chain.request().newBuilder() .addHeader("Authorization", "Bearer ${BuildConfig.API_TOKEN}") .build() ) } .connectTimeout(30, TimeUnit.SECONDS) .readTimeout(30, TimeUnit.SECONDS) .build() ) .build() ``` --- ## AndroidManifest.xml ```xml ``` --- ## Material 3 Theme ```kotlin // Color.kt val BlueElectric = Color(0xFF3B82F6) val AmberSpark = Color(0xFFF59E0B) val DeepSpace = Color(0xFF090E1A) val SpaceSurface = Color(0xFF0F1629) private val DarkColorScheme = darkColorScheme( primary = BlueElectric, secondary = AmberSpark, background = DeepSpace, surface = SpaceSurface, onPrimary = Color.White, onBackground = Color(0xFFE2E8F0), onSurface = Color(0xFF94A3B8), ) @Composable fun MyAppTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit) { val colorScheme = if (darkTheme) DarkColorScheme else LightColorScheme MaterialTheme(colorScheme = colorScheme, typography = Typography, content = content) } ```