├── .gitignore ├── README.md ├── app ├── .gitignore ├── build.gradle.kts ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── rmyhal │ │ └── containertransform │ │ ├── AddContentScreen.kt │ │ ├── HomeScreen.kt │ │ ├── HotTake.kt │ │ ├── MainActivity.kt │ │ └── ui │ │ └── theme │ │ ├── Color.kt │ │ ├── Theme.kt │ │ └── Type.kt │ └── res │ ├── drawable │ ├── ic_launcher_background.xml │ └── ic_launcher_foreground.xml │ ├── mipmap-anydpi-v26 │ ├── ic_launcher.xml │ └── ic_launcher_round.xml │ ├── mipmap-hdpi │ ├── ic_launcher_foreground.webp │ └── ic_launcher_round.webp │ ├── mipmap-mdpi │ ├── ic_launcher_foreground.webp │ └── ic_launcher_round.webp │ ├── mipmap-xhdpi │ ├── ic_launcher_foreground.webp │ └── ic_launcher_round.webp │ ├── mipmap-xxhdpi │ ├── ic_launcher_foreground.webp │ └── ic_launcher_round.webp │ ├── mipmap-xxxhdpi │ ├── ic_launcher_foreground.webp │ └── ic_launcher_round.webp │ ├── values │ ├── ic_launcher_background.xml │ ├── strings.xml │ └── themes.xml │ └── xml │ ├── backup_rules.xml │ └── data_extraction_rules.xml ├── build.gradle.kts ├── gradle.properties ├── gradle ├── libs.versions.toml └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle.kts /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea 5 | .DS_Store 6 | /build 7 | /captures 8 | .externalNativeBuild 9 | .cxx 10 | local.properties 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Container transfrom animation 2 | 3 | 4 | 5 | [Article](https://medium.com/proandroiddev/container-transform-animation-98e5e74a15c9) 6 | 7 | ![anim](https://github.com/user-attachments/assets/94ad445c-dfa3-409f-92c2-2186a1037bbf) 8 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | alias(libs.plugins.android.application) 3 | alias(libs.plugins.jetbrains.kotlin.android) 4 | } 5 | 6 | android { 7 | namespace = "com.rmyhal.containertransform" 8 | compileSdk = 34 9 | 10 | defaultConfig { 11 | applicationId = "com.rmyhal.containertransform" 12 | minSdk = 29 13 | targetSdk = 34 14 | versionCode = 1 15 | versionName = "1.0" 16 | 17 | vectorDrawables { 18 | useSupportLibrary = true 19 | } 20 | } 21 | 22 | buildTypes { 23 | release { 24 | isMinifyEnabled = false 25 | proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") 26 | } 27 | } 28 | compileOptions { 29 | sourceCompatibility = JavaVersion.VERSION_17 30 | targetCompatibility = JavaVersion.VERSION_17 31 | } 32 | kotlinOptions { 33 | jvmTarget = "17" 34 | } 35 | buildFeatures { 36 | compose = true 37 | } 38 | composeOptions { 39 | kotlinCompilerExtensionVersion = "1.5.1" 40 | } 41 | packaging { 42 | resources { 43 | excludes += "/META-INF/{AL2.0,LGPL2.1}" 44 | } 45 | } 46 | } 47 | 48 | dependencies { 49 | implementation(libs.androidx.core.ktx) 50 | implementation(libs.androidx.lifecycle.runtime.ktx) 51 | implementation(libs.androidx.activity.compose) 52 | implementation(platform(libs.androidx.compose.bom)) 53 | implementation(libs.androidx.ui) 54 | implementation(libs.androidx.ui.graphics) 55 | implementation(libs.androidx.ui.tooling.preview) 56 | implementation(libs.androidx.material3) 57 | implementation(libs.contentment) 58 | 59 | debugImplementation(libs.androidx.ui.tooling) 60 | debugImplementation(libs.androidx.ui.test.manifest) 61 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 15 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /app/src/main/java/com/rmyhal/containertransform/AddContentScreen.kt: -------------------------------------------------------------------------------- 1 | package com.rmyhal.containertransform 2 | 3 | import androidx.activity.compose.BackHandler 4 | import androidx.compose.foundation.layout.Column 5 | import androidx.compose.foundation.layout.WindowInsets 6 | import androidx.compose.foundation.layout.fillMaxHeight 7 | import androidx.compose.foundation.layout.fillMaxWidth 8 | import androidx.compose.foundation.layout.imePadding 9 | import androidx.compose.foundation.layout.padding 10 | import androidx.compose.material.icons.Icons 11 | import androidx.compose.material.icons.filled.ArrowBack 12 | import androidx.compose.material3.CenterAlignedTopAppBar 13 | import androidx.compose.material3.ExperimentalMaterial3Api 14 | import androidx.compose.material3.FilledTonalButton 15 | import androidx.compose.material3.FilledTonalIconButton 16 | import androidx.compose.material3.Icon 17 | import androidx.compose.material3.IconButtonDefaults 18 | import androidx.compose.material3.MaterialTheme 19 | import androidx.compose.material3.OutlinedTextField 20 | import androidx.compose.material3.OutlinedTextFieldDefaults 21 | import androidx.compose.material3.Text 22 | import androidx.compose.runtime.Composable 23 | import androidx.compose.runtime.getValue 24 | import androidx.compose.runtime.mutableStateOf 25 | import androidx.compose.runtime.remember 26 | import androidx.compose.runtime.setValue 27 | import androidx.compose.ui.Modifier 28 | import androidx.compose.ui.graphics.Color 29 | import androidx.compose.ui.tooling.preview.Preview 30 | import androidx.compose.ui.unit.dp 31 | import com.rmyhal.containertransform.ui.theme.ContainerTransformTheme 32 | import com.rmyhal.containertransform.ui.theme.Gray10 33 | 34 | @Composable 35 | fun AddContentScreen( 36 | modifier: Modifier = Modifier, 37 | onBack: () -> Unit, 38 | ) { 39 | BackHandler { 40 | onBack() 41 | } 42 | Column( 43 | modifier = modifier 44 | .padding(horizontal = 8.dp) 45 | ) { 46 | Toolbar( 47 | onBack = onBack, 48 | ) 49 | TitleInputField() 50 | StoryInputField() 51 | } 52 | } 53 | 54 | @Composable 55 | @OptIn(ExperimentalMaterial3Api::class) 56 | private fun Toolbar(onBack: () -> Unit) { 57 | CenterAlignedTopAppBar( 58 | windowInsets = WindowInsets(0, 0, 0, 0), 59 | title = {}, 60 | actions = { 61 | FilledTonalButton( 62 | modifier = Modifier.padding(end = 6.dp), 63 | onClick = onBack, 64 | ) { 65 | Text(text = "Save") 66 | } 67 | }, 68 | navigationIcon = { 69 | FilledTonalIconButton( 70 | colors = IconButtonDefaults.filledTonalIconButtonColors( 71 | containerColor = MaterialTheme.colorScheme.background 72 | ), 73 | onClick = onBack, 74 | ) { 75 | Icon( 76 | imageVector = Icons.Filled.ArrowBack, 77 | contentDescription = null, 78 | ) 79 | } 80 | } 81 | ) 82 | } 83 | 84 | @Composable 85 | private fun TitleInputField() { 86 | var text by remember { mutableStateOf("") } 87 | OutlinedTextField( 88 | modifier = Modifier 89 | .fillMaxWidth(), 90 | value = text, 91 | onValueChange = { text = it }, 92 | placeholder = { 93 | Text( 94 | text = "Title", 95 | color = Gray10, 96 | ) 97 | }, 98 | colors = OutlinedTextFieldDefaults.colors( 99 | focusedBorderColor = Color.Transparent, 100 | unfocusedBorderColor = Color.Transparent, 101 | ) 102 | ) 103 | } 104 | 105 | @Composable 106 | private fun StoryInputField() { 107 | var text by remember { mutableStateOf("") } 108 | OutlinedTextField( 109 | modifier = Modifier 110 | .fillMaxWidth() 111 | .fillMaxHeight() 112 | .imePadding(), 113 | value = text, 114 | onValueChange = { text = it }, 115 | placeholder = { 116 | Text( 117 | text = "Fragmentation isn't necessarily a curse; it's a testament to Android's diversity...", 118 | color = Gray10, 119 | ) 120 | }, 121 | colors = OutlinedTextFieldDefaults.colors( 122 | focusedBorderColor = Color.Transparent, 123 | unfocusedBorderColor = Color.Transparent, 124 | ), 125 | ) 126 | } 127 | 128 | @Preview(showBackground = true) 129 | @Composable 130 | private fun Preview() { 131 | ContainerTransformTheme { 132 | AddContentScreen {} 133 | } 134 | } -------------------------------------------------------------------------------- /app/src/main/java/com/rmyhal/containertransform/HomeScreen.kt: -------------------------------------------------------------------------------- 1 | package com.rmyhal.containertransform 2 | 3 | import androidx.compose.animation.AnimatedContent 4 | import androidx.compose.animation.SizeTransform 5 | import androidx.compose.animation.animateColor 6 | import androidx.compose.animation.core.EaseInCubic 7 | import androidx.compose.animation.core.EaseOutCubic 8 | import androidx.compose.animation.core.FastOutSlowInEasing 9 | import androidx.compose.animation.core.animateDp 10 | import androidx.compose.animation.core.tween 11 | import androidx.compose.animation.core.updateTransition 12 | import androidx.compose.animation.fadeIn 13 | import androidx.compose.animation.fadeOut 14 | import androidx.compose.animation.scaleIn 15 | import androidx.compose.animation.togetherWith 16 | import androidx.compose.foundation.clickable 17 | import androidx.compose.foundation.layout.Arrangement 18 | import androidx.compose.foundation.layout.Box 19 | import androidx.compose.foundation.layout.Column 20 | import androidx.compose.foundation.layout.defaultMinSize 21 | import androidx.compose.foundation.layout.fillMaxSize 22 | import androidx.compose.foundation.layout.fillMaxWidth 23 | import androidx.compose.foundation.layout.padding 24 | import androidx.compose.foundation.lazy.LazyColumn 25 | import androidx.compose.foundation.lazy.items 26 | import androidx.compose.foundation.shape.RoundedCornerShape 27 | import androidx.compose.material.icons.Icons 28 | import androidx.compose.material.icons.filled.Add 29 | import androidx.compose.material.icons.filled.Menu 30 | import androidx.compose.material3.Card 31 | import androidx.compose.material3.CircularProgressIndicator 32 | import androidx.compose.material3.Icon 33 | import androidx.compose.material3.MaterialTheme 34 | import androidx.compose.material3.OutlinedTextField 35 | import androidx.compose.material3.OutlinedTextFieldDefaults 36 | import androidx.compose.material3.Text 37 | import androidx.compose.runtime.Composable 38 | import androidx.compose.runtime.LaunchedEffect 39 | import androidx.compose.runtime.getValue 40 | import androidx.compose.runtime.mutableStateOf 41 | import androidx.compose.runtime.remember 42 | import androidx.compose.runtime.setValue 43 | import androidx.compose.ui.Alignment 44 | import androidx.compose.ui.Modifier 45 | import androidx.compose.ui.draw.drawBehind 46 | import androidx.compose.ui.draw.shadow 47 | import androidx.compose.ui.graphics.Color 48 | import androidx.compose.ui.graphics.vector.rememberVectorPainter 49 | import androidx.compose.ui.text.style.TextOverflow 50 | import androidx.compose.ui.tooling.preview.Preview 51 | import androidx.compose.ui.unit.dp 52 | import com.rmyhal.containertransform.ui.theme.ContainerTransformTheme 53 | import kotlinx.coroutines.delay 54 | import me.rmyhal.contentment.Contentment 55 | 56 | @Composable 57 | fun HomeScreen(modifier: Modifier = Modifier) { 58 | Box( 59 | modifier = modifier 60 | .fillMaxSize(), 61 | ) { 62 | HotContent() 63 | FabContainer( 64 | modifier = Modifier 65 | .align(Alignment.BottomEnd) 66 | ) 67 | } 68 | } 69 | 70 | @Composable 71 | private fun FabContainer( 72 | modifier: Modifier = Modifier, 73 | ) { 74 | var containerState by remember { mutableStateOf(ContainerState.Fab) } 75 | val transition = updateTransition(containerState, label = "container transform") 76 | val animatedColor by transition.animateColor( 77 | label = "color", 78 | ) { state -> 79 | when (state) { 80 | ContainerState.Fab -> MaterialTheme.colorScheme.primaryContainer 81 | ContainerState.Fullscreen -> MaterialTheme.colorScheme.surface 82 | } 83 | } 84 | 85 | val cornerRadius by transition.animateDp( 86 | label = "corner radius", 87 | transitionSpec = { 88 | when (targetState) { 89 | ContainerState.Fab -> tween( 90 | durationMillis = 400, 91 | easing = EaseOutCubic, 92 | ) 93 | 94 | ContainerState.Fullscreen -> tween( 95 | durationMillis = 200, 96 | easing = EaseInCubic, 97 | ) 98 | } 99 | } 100 | ) { state -> 101 | when (state) { 102 | ContainerState.Fab -> 22.dp 103 | ContainerState.Fullscreen -> 0.dp 104 | } 105 | } 106 | val elevation by transition.animateDp( 107 | label = "elevation", 108 | transitionSpec = { 109 | when (targetState) { 110 | ContainerState.Fab -> tween( 111 | durationMillis = 400, 112 | easing = EaseOutCubic, 113 | ) 114 | 115 | ContainerState.Fullscreen -> tween( 116 | durationMillis = 200, 117 | easing = EaseOutCubic, 118 | ) 119 | } 120 | } 121 | ) { state -> 122 | when (state) { 123 | ContainerState.Fab -> 6.dp 124 | ContainerState.Fullscreen -> 0.dp 125 | } 126 | } 127 | val padding by transition.animateDp( 128 | label = "padding", 129 | ) { state -> 130 | when (state) { 131 | ContainerState.Fab -> 16.dp 132 | ContainerState.Fullscreen -> 0.dp 133 | } 134 | } 135 | 136 | transition.AnimatedContent( 137 | contentAlignment = Alignment.Center, 138 | modifier = modifier 139 | .padding(end = padding, bottom = padding) 140 | .shadow( 141 | elevation = elevation, 142 | shape = RoundedCornerShape(cornerRadius) 143 | ) 144 | .drawBehind { drawRect(animatedColor) }, 145 | transitionSpec = { 146 | ( 147 | fadeIn(animationSpec = tween(220, delayMillis = 90)) + 148 | scaleIn(initialScale = 0.92f, animationSpec = tween(220, delayMillis = 90)) 149 | ) 150 | .togetherWith(fadeOut(animationSpec = tween(90))) 151 | .using(SizeTransform(clip = false, sizeAnimationSpec = { _, _ -> 152 | tween( 153 | durationMillis = 500, 154 | easing = FastOutSlowInEasing 155 | ) 156 | })) 157 | } 158 | ) { state -> 159 | when (state) { 160 | ContainerState.Fab -> { 161 | Fab( 162 | modifier = Modifier, 163 | onClick = { containerState = ContainerState.Fullscreen } 164 | ) 165 | } 166 | 167 | ContainerState.Fullscreen -> { 168 | AddContentScreen( 169 | modifier = Modifier, 170 | onBack = { containerState = ContainerState.Fab } 171 | ) 172 | } 173 | } 174 | } 175 | } 176 | 177 | @Composable 178 | private fun HotContent() { 179 | Column { 180 | SearchBar( 181 | modifier = Modifier 182 | .fillMaxWidth() 183 | .padding(horizontal = 12.dp, vertical = 6.dp) 184 | ) 185 | 186 | var uiState by remember { mutableStateOf(UiState.Loading) } 187 | LaunchedEffect(Unit) { 188 | // fake loading 189 | delay(600) 190 | uiState = UiState.Loaded(hotTakes) 191 | } 192 | Box( 193 | modifier = Modifier.fillMaxSize(), 194 | ) { 195 | Contentment { 196 | when (uiState) { 197 | is UiState.Loading -> indicator { 198 | CircularProgressIndicator( 199 | modifier = Modifier.align(Alignment.Center) 200 | ) 201 | } 202 | 203 | is UiState.Loaded -> content { HotTakes() } 204 | } 205 | } 206 | } 207 | } 208 | } 209 | 210 | @Composable 211 | private fun SearchBar(modifier: Modifier = Modifier) { 212 | OutlinedTextField( 213 | modifier = modifier, 214 | value = "", 215 | onValueChange = {}, 216 | leadingIcon = { 217 | Icon( 218 | imageVector = Icons.Default.Menu, 219 | contentDescription = null, 220 | ) 221 | }, 222 | placeholder = { 223 | Text( 224 | text = "Search your hot takes" 225 | ) 226 | }, 227 | colors = OutlinedTextFieldDefaults.colors( 228 | focusedBorderColor = Color.Transparent, 229 | unfocusedBorderColor = Color.Transparent, 230 | unfocusedContainerColor = MaterialTheme.colorScheme.secondaryContainer, 231 | focusedContainerColor = MaterialTheme.colorScheme.secondaryContainer, 232 | ), 233 | shape = RoundedCornerShape(50) 234 | ) 235 | } 236 | 237 | @Composable 238 | private fun HotTakes() { 239 | LazyColumn( 240 | modifier = Modifier 241 | .fillMaxWidth() 242 | .padding(top = 12.dp), 243 | verticalArrangement = Arrangement.spacedBy(12.dp) 244 | ) { 245 | items(hotTakes) { HotTake(hotTake = it) } 246 | } 247 | } 248 | 249 | @Composable 250 | private fun HotTake(hotTake: HotTake) { 251 | Card( 252 | modifier = Modifier 253 | .fillMaxWidth() 254 | .padding(horizontal = 16.dp), 255 | onClick = { }, 256 | ) { 257 | Column( 258 | modifier = Modifier.padding(16.dp) 259 | ) { 260 | hotTake.title?.let { 261 | Text( 262 | text = hotTake.title, 263 | style = MaterialTheme.typography.titleMedium, 264 | ) 265 | } 266 | Text( 267 | modifier = Modifier 268 | .padding(top = 6.dp), 269 | maxLines = 3, 270 | text = hotTake.take, 271 | overflow = TextOverflow.Ellipsis 272 | ) 273 | } 274 | } 275 | } 276 | 277 | @Composable 278 | private fun Fab( 279 | modifier: Modifier = Modifier, 280 | onClick: () -> Unit 281 | ) { 282 | Box( 283 | modifier = modifier 284 | .defaultMinSize( 285 | minWidth = 76.dp, 286 | minHeight = 76.dp, 287 | ) 288 | .clickable( 289 | onClick = onClick, 290 | ), 291 | contentAlignment = Alignment.Center, 292 | ) { 293 | Icon( 294 | painter = rememberVectorPainter(Icons.Filled.Add), 295 | contentDescription = null, 296 | ) 297 | } 298 | } 299 | 300 | sealed interface UiState { 301 | data class Loaded(val list: List) : UiState 302 | data object Loading : UiState 303 | } 304 | 305 | enum class ContainerState { 306 | Fab, 307 | Fullscreen, 308 | } 309 | 310 | @Preview(showBackground = true) 311 | @Composable 312 | private fun Preview() { 313 | ContainerTransformTheme { 314 | HomeScreen() 315 | } 316 | } -------------------------------------------------------------------------------- /app/src/main/java/com/rmyhal/containertransform/HotTake.kt: -------------------------------------------------------------------------------- 1 | package com.rmyhal.containertransform 2 | 3 | import androidx.compose.runtime.Stable 4 | 5 | @Stable 6 | data class HotTake( 7 | val title: String?, 8 | val take: String, 9 | ) 10 | 11 | val hotTakes = listOf( 12 | HotTake( 13 | title = null, 14 | take = "Artificial Intelligence isn't just a threat to jobs; it's a catalyst for redefining the workforce. Instead of fearing automation, we should focus on reskilling and upskilling workers to thrive in a future augmented by AI.", 15 | ), 16 | HotTake( 17 | title = "Security?", 18 | take = "Prioritizing user security over convenience should be non-negotiable in Android app development. While features like single sign-on and biometric authentication enhance user experience, they should not compromise security standards. Developers must strike a balance between user convenience and robust security measures to safeguard user data and trust." 19 | ), 20 | HotTake( 21 | title = "Compose or Collapse", 22 | take = "Jetpack Compose isn't just a toolkit; it's the litmus test for Android's future relevance. Embrace Compose's radical departure from XML layouts or risk irrelevance in a rapidly evolving mobile ecosystem.", 23 | ) 24 | ) 25 | -------------------------------------------------------------------------------- /app/src/main/java/com/rmyhal/containertransform/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.rmyhal.containertransform 2 | 3 | import android.os.Bundle 4 | import androidx.activity.ComponentActivity 5 | import androidx.activity.compose.setContent 6 | import androidx.activity.enableEdgeToEdge 7 | import androidx.compose.foundation.layout.fillMaxSize 8 | import androidx.compose.foundation.layout.padding 9 | import androidx.compose.material3.Scaffold 10 | import androidx.compose.runtime.Composable 11 | import androidx.compose.ui.Modifier 12 | import androidx.compose.ui.tooling.preview.Preview 13 | import com.rmyhal.containertransform.ui.theme.ContainerTransformTheme 14 | 15 | class MainActivity : ComponentActivity() { 16 | override fun onCreate(savedInstanceState: Bundle?) { 17 | super.onCreate(savedInstanceState) 18 | enableEdgeToEdge() 19 | setContent { 20 | ContainerTransformTheme { 21 | Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding -> 22 | HomeScreen( 23 | modifier = Modifier 24 | .padding(innerPadding) 25 | ) 26 | } 27 | } 28 | } 29 | } 30 | } 31 | 32 | @Preview(showBackground = true) 33 | @Composable 34 | private fun Preview() { 35 | ContainerTransformTheme { 36 | HomeScreen() 37 | } 38 | } -------------------------------------------------------------------------------- /app/src/main/java/com/rmyhal/containertransform/ui/theme/Color.kt: -------------------------------------------------------------------------------- 1 | package com.rmyhal.containertransform.ui.theme 2 | 3 | import androidx.compose.ui.graphics.Color 4 | 5 | val Purple80 = Color(0xFFD0BCFF) 6 | val PurpleGrey80 = Color(0xFFCCC2DC) 7 | val Pink80 = Color(0xFFEFB8C8) 8 | 9 | val Purple40 = Color(0xFF6650a4) 10 | val PurpleGrey40 = Color(0xFF625b71) 11 | val Pink40 = Color(0xFF7D5260) 12 | 13 | val Gray10 = Color(0xFF9898A0) -------------------------------------------------------------------------------- /app/src/main/java/com/rmyhal/containertransform/ui/theme/Theme.kt: -------------------------------------------------------------------------------- 1 | package com.rmyhal.containertransform.ui.theme 2 | 3 | import android.os.Build 4 | import androidx.compose.foundation.isSystemInDarkTheme 5 | import androidx.compose.material3.MaterialTheme 6 | import androidx.compose.material3.darkColorScheme 7 | import androidx.compose.material3.dynamicDarkColorScheme 8 | import androidx.compose.material3.dynamicLightColorScheme 9 | import androidx.compose.material3.lightColorScheme 10 | import androidx.compose.runtime.Composable 11 | import androidx.compose.ui.platform.LocalContext 12 | 13 | private val DarkColorScheme = darkColorScheme( 14 | primary = Purple80, 15 | secondary = PurpleGrey80, 16 | tertiary = Pink80 17 | ) 18 | 19 | private val LightColorScheme = lightColorScheme( 20 | primary = Purple40, 21 | secondary = PurpleGrey40, 22 | tertiary = Pink40 23 | ) 24 | 25 | @Composable 26 | fun ContainerTransformTheme( 27 | darkTheme: Boolean = isSystemInDarkTheme(), 28 | // Dynamic color is available on Android 12+ 29 | dynamicColor: Boolean = true, 30 | content: @Composable () -> Unit 31 | ) { 32 | val colorScheme = when { 33 | dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { 34 | val context = LocalContext.current 35 | if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) 36 | } 37 | 38 | darkTheme -> DarkColorScheme 39 | else -> LightColorScheme 40 | } 41 | 42 | MaterialTheme( 43 | colorScheme = colorScheme, 44 | typography = Typography, 45 | content = content 46 | ) 47 | } -------------------------------------------------------------------------------- /app/src/main/java/com/rmyhal/containertransform/ui/theme/Type.kt: -------------------------------------------------------------------------------- 1 | package com.rmyhal.containertransform.ui.theme 2 | 3 | import androidx.compose.material3.Typography 4 | import androidx.compose.ui.text.TextStyle 5 | import androidx.compose.ui.text.font.FontFamily 6 | import androidx.compose.ui.text.font.FontWeight 7 | import androidx.compose.ui.unit.sp 8 | 9 | // Set of Material typography styles to start with 10 | val Typography = Typography( 11 | bodyLarge = TextStyle( 12 | fontFamily = FontFamily.Default, 13 | fontWeight = FontWeight.Normal, 14 | fontSize = 16.sp, 15 | lineHeight = 24.sp, 16 | letterSpacing = 0.5.sp 17 | ) 18 | ) -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmyhal/container-transform-compose/a2071be3aad3e3ff5cfb6b059b77a1f05d57736e/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmyhal/container-transform-compose/a2071be3aad3e3ff5cfb6b059b77a1f05d57736e/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmyhal/container-transform-compose/a2071be3aad3e3ff5cfb6b059b77a1f05d57736e/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmyhal/container-transform-compose/a2071be3aad3e3ff5cfb6b059b77a1f05d57736e/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmyhal/container-transform-compose/a2071be3aad3e3ff5cfb6b059b77a1f05d57736e/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmyhal/container-transform-compose/a2071be3aad3e3ff5cfb6b059b77a1f05d57736e/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmyhal/container-transform-compose/a2071be3aad3e3ff5cfb6b059b77a1f05d57736e/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmyhal/container-transform-compose/a2071be3aad3e3ff5cfb6b059b77a1f05d57736e/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmyhal/container-transform-compose/a2071be3aad3e3ff5cfb6b059b77a1f05d57736e/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmyhal/container-transform-compose/a2071be3aad3e3ff5cfb6b059b77a1f05d57736e/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/values/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFFFFF 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Hot 3 | -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 |