Move Compose to Material3 (Material You)

Optimize Image Processing for Jetpack Compose
Update Dependencies
This commit is contained in:
LooKeR 2022-02-25 11:56:10 +05:30
parent e1e1916126
commit 000bd16256
7 changed files with 177 additions and 117 deletions

View File

@ -135,9 +135,9 @@ dependencies {
implementation 'androidx.appcompat:appcompat-resources:1.4.1' implementation 'androidx.appcompat:appcompat-resources:1.4.1'
implementation 'androidx.fragment:fragment-ktx:1.4.1' implementation 'androidx.fragment:fragment-ktx:1.4.1'
implementation 'androidx.activity:activity-ktx:1.4.0' implementation 'androidx.activity:activity-ktx:1.4.0'
implementation "androidx.preference:preference-ktx:1.1.1" implementation "androidx.preference:preference-ktx:1.2.0"
implementation "androidx.navigation:navigation-fragment-ktx:2.5.0-alpha01" implementation "androidx.navigation:navigation-fragment-ktx:2.5.0-alpha03"
implementation "androidx.navigation:navigation-ui-ktx:2.5.0-alpha01" implementation "androidx.navigation:navigation-ui-ktx:2.5.0-alpha03"
// Material3 // Material3
implementation 'com.google.android.material:material:1.6.0-alpha02' implementation 'com.google.android.material:material:1.6.0-alpha02'
@ -171,7 +171,7 @@ dependencies {
implementation "org.jetbrains:markdown:0.2.4" implementation "org.jetbrains:markdown:0.2.4"
// Coroutines / Lifecycle // Coroutines / Lifecycle
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.0' implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.1'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0'
@ -179,20 +179,20 @@ dependencies {
implementation 'androidx.paging:paging-runtime-ktx:3.1.0' implementation 'androidx.paging:paging-runtime-ktx:3.1.0'
// Room // Room
implementation 'androidx.room:room-runtime:2.4.1' implementation 'androidx.room:room-runtime:2.4.2'
implementation 'androidx.room:room-ktx:2.4.1' implementation 'androidx.room:room-ktx:2.4.2'
implementation 'androidx.room:room-rxjava3:2.4.1' implementation 'androidx.room:room-rxjava3:2.4.2'
kapt 'androidx.room:room-compiler:2.4.1' kapt 'androidx.room:room-compiler:2.4.2'
// Compose // Compose
implementation "androidx.compose.runtime:runtime:1.2.0-alpha02" implementation "androidx.compose.runtime:runtime:1.2.0-alpha04"
implementation "androidx.compose.ui:ui:1.2.0-alpha02" implementation "androidx.compose.ui:ui:1.2.0-alpha04"
implementation "androidx.compose.ui:ui-tooling:1.2.0-alpha02" implementation "androidx.compose.ui:ui-tooling:1.2.0-alpha04"
implementation "androidx.compose.foundation:foundation:1.2.0-alpha02" implementation "androidx.compose.foundation:foundation:1.2.0-alpha04"
implementation "androidx.compose.foundation:foundation-layout:1.2.0-alpha02" implementation "androidx.compose.runtime:runtime-livedata:1.2.0-alpha04"
implementation "androidx.compose.runtime:runtime-livedata:1.2.0-alpha02" implementation "androidx.compose.material3:material3:1.0.0-alpha06"
implementation "androidx.compose.material:material:1.2.0-alpha02" implementation "androidx.compose.material:material:1.2.0-alpha04"
implementation "com.google.android.material:compose-theme-adapter:1.1.3" implementation "com.google.android.material:compose-theme-adapter:1.1.5"
} }
// using a task as a preBuild dependency instead of a function that takes some time insures that it runs // using a task as a preBuild dependency instead of a function that takes some time insures that it runs

View File

@ -145,7 +145,7 @@ object CoilDownloader {
fun createIconUri( fun createIconUri(
packageName: String, icon: String, metadataIcon: String, packageName: String, icon: String, metadataIcon: String,
address: String, auth: String address: String?, auth: String?
): Uri = Uri.Builder().scheme("https").authority(HOST_ICON) ): Uri = Uri.Builder().scheme("https").authority(HOST_ICON)
.appendQueryParameter(QUERY_ADDRESS, address) .appendQueryParameter(QUERY_ADDRESS, address)
.appendQueryParameter(QUERY_AUTHENTICATION, auth) .appendQueryParameter(QUERY_AUTHENTICATION, auth)

View File

@ -1,30 +1,25 @@
package com.looker.droidify.ui.compose package com.looker.droidify.ui.compose
import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.ContentAlpha import androidx.compose.material.ContentAlpha
import androidx.compose.material.LocalContentAlpha import androidx.compose.material.LocalContentAlpha
import androidx.compose.material.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.*
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import coil.compose.rememberImagePainter
import com.looker.droidify.R
import com.looker.droidify.database.entity.Repository import com.looker.droidify.database.entity.Repository
import com.looker.droidify.entity.ProductItem import com.looker.droidify.entity.ProductItem
import com.looker.droidify.network.CoilDownloader import com.looker.droidify.network.CoilDownloader
import com.looker.droidify.ui.compose.components.NetworkImage
import com.looker.droidify.ui.compose.theme.AppTheme import com.looker.droidify.ui.compose.theme.AppTheme
@Composable @Composable
@ -33,36 +28,31 @@ fun ProductRow(
repo: Repository? = null, repo: Repository? = null,
onUserClick: (ProductItem) -> Unit = {} onUserClick: (ProductItem) -> Unit = {}
) { ) {
Row(
modifier = Modifier val imageData by remember {
.padding(4.dp) mutableStateOf(
.fillMaxWidth()
.background(color = MaterialTheme.colors.surface, shape = RoundedCornerShape(8.dp))
.clip(shape = RoundedCornerShape(8.dp))
.clickable(onClick = { onUserClick(item) })
.padding(8.dp)
) {
// TODO: Fix the issue where coil doesn't fallback to the palceholder
val imagePainter =
if (repo != null) rememberImagePainter(
CoilDownloader.createIconUri( CoilDownloader.createIconUri(
item.packageName, item.packageName,
item.icon, item.icon,
item.metadataIcon, item.metadataIcon,
repo.address, repo?.address,
repo.authentication repo?.authentication
), builder = { )
placeholder(R.drawable.ic_application_default) )
error(R.drawable.ic_application_default)
} }
) else painterResource(id = R.drawable.ic_application_default)
Image( Row(
painter = imagePainter,
modifier = Modifier modifier = Modifier
.requiredSize(56.dp) .padding(4.dp)
.clip(shape = RoundedCornerShape(8.dp)), .fillMaxWidth()
contentScale = ContentScale.Crop, .background(color = MaterialTheme.colorScheme.surface, shape = RoundedCornerShape(8.dp))
contentDescription = null .clip(shape = RoundedCornerShape(8.dp))
.clickable(onClick = { onUserClick(item) })
.padding(8.dp)
) {
NetworkImage(
modifier = Modifier.size(56.dp),
data = imageData
) )
Column( Column(
@ -84,24 +74,23 @@ fun ProductRow(
fontWeight = FontWeight.Bold, fontWeight = FontWeight.Bold,
softWrap = true, softWrap = true,
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,
style = MaterialTheme.typography.body1 style = MaterialTheme.typography.bodyMedium
) )
Text( Text(
text = item.version, text = item.version,
modifier = Modifier modifier = Modifier
.align(Alignment.CenterEnd), .align(Alignment.CenterEnd),
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,
style = MaterialTheme.typography.body2 style = MaterialTheme.typography.bodySmall
) )
} }
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) { CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
Text( Text(
modifier = Modifier.fillMaxHeight(), modifier = Modifier.fillMaxHeight(),
text = item.summary, text = item.summary,
style = MaterialTheme.typography.body2 style = MaterialTheme.typography.bodySmall
) )
} }
} }
} }
} }
@ -112,60 +101,49 @@ fun ProductColumn(
repo: Repository? = null, repo: Repository? = null,
onUserClick: (ProductItem) -> Unit = {} onUserClick: (ProductItem) -> Unit = {}
) { ) {
Column(
modifier = Modifier val imageData by remember {
.padding(4.dp) mutableStateOf(
.requiredSize(72.dp, 96.dp)
.background(color = MaterialTheme.colors.surface, shape = RoundedCornerShape(8.dp))
.clip(shape = RoundedCornerShape(8.dp))
.clickable(onClick = { onUserClick(item) })
.padding(4.dp)
) {
// TODO: Fix the issue where coil doesn't fallback to the palceholder
val imagePainter = if (repo != null)
rememberImagePainter(
CoilDownloader.createIconUri( CoilDownloader.createIconUri(
item.packageName, item.packageName,
item.icon, item.icon,
item.metadataIcon, item.metadataIcon,
repo.address, repo?.address,
repo.authentication repo?.authentication
), builder = {
placeholder(R.drawable.ic_application_default)
error(R.drawable.ic_application_default)
}
) )
else painterResource(id = R.drawable.ic_application_default) )
Image( }
painter = imagePainter,
Column(
modifier = Modifier modifier = Modifier
.requiredSize(56.dp) .padding(4.dp)
.requiredSize(72.dp, 96.dp)
.background(color = MaterialTheme.colorScheme.surface, shape = RoundedCornerShape(8.dp))
.clip(shape = RoundedCornerShape(8.dp)) .clip(shape = RoundedCornerShape(8.dp))
.align(Alignment.CenterHorizontally), .clickable(onClick = { onUserClick(item) })
contentScale = ContentScale.Crop, .padding(4.dp),
contentDescription = null horizontalAlignment = Alignment.CenterHorizontally
) {
NetworkImage(
modifier = Modifier.size(56.dp),
data = imageData
) )
Text( Text(
text = item.name, text = item.name,
modifier = Modifier
.align(Alignment.CenterHorizontally),
fontWeight = FontWeight.Bold, fontWeight = FontWeight.Bold,
style = MaterialTheme.typography.body2, style = MaterialTheme.typography.bodyLarge,
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,
maxLines = 1 maxLines = 1
) )
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) { CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
Text( Text(
text = item.version, text = item.version,
modifier = Modifier style = MaterialTheme.typography.labelSmall,
.align(Alignment.CenterHorizontally),
style = MaterialTheme.typography.overline,
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,
maxLines = 1 maxLines = 1
) )
} }
} }
} }

View File

@ -0,0 +1,40 @@
package com.looker.droidify.ui.compose.components
import android.net.Uri
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.shape.CornerBasedShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.layout.ContentScale
import coil.annotation.ExperimentalCoilApi
import coil.compose.rememberImagePainter
import com.looker.droidify.R
import com.looker.droidify.ui.compose.theme.LocalShapes
@OptIn(ExperimentalCoilApi::class)
@Composable
fun NetworkImage(
modifier: Modifier = Modifier,
data: Uri?,
contentScale: ContentScale = ContentScale.Crop,
shape: CornerBasedShape = RoundedCornerShape(LocalShapes.current.medium)
) {
Box(modifier) {
val painter = rememberImagePainter(data = data) {
placeholder(R.drawable.ic_application_default)
error(R.drawable.ic_application_default)
}
Image(
painter = painter,
contentDescription = "This is Album Art",
contentScale = contentScale,
modifier = Modifier
.matchParentSize()
.clip(shape)
)
}
}

View File

@ -58,4 +58,3 @@ val BlackBackground = Color(0xFF000000)
val BlackSurface = Color(0xFF191C1A) val BlackSurface = Color(0xFF191C1A)
val Seed = Color(0xFF51DF93) val Seed = Color(0xFF51DF93)

View File

@ -1,11 +1,15 @@
package com.looker.droidify.ui.compose.theme package com.looker.droidify.ui.compose.theme
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.runtime.Immutable
import androidx.compose.material.Shapes import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
val AppShapes = Shapes( @Immutable
small = RoundedCornerShape(4.dp), data class ShapeSize(
medium = RoundedCornerShape(8.dp), val small: Dp = 4.dp,
large = RoundedCornerShape(16.dp) val medium: Dp = 8.dp,
val large: Dp = 16.dp,
) )
val LocalShapes = staticCompositionLocalOf { ShapeSize() }

View File

@ -1,10 +1,11 @@
package com.looker.droidify.ui.compose.theme package com.looker.droidify.ui.compose.theme
import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material.darkColors import androidx.compose.material3.darkColorScheme
import androidx.compose.material.lightColors import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import com.looker.droidify.utility.isBlackTheme import com.looker.droidify.utility.isBlackTheme
@Composable @Composable
@ -13,9 +14,10 @@ fun AppTheme(
blackTheme: Boolean = isBlackTheme, blackTheme: Boolean = isBlackTheme,
content: @Composable () -> Unit content: @Composable () -> Unit
) { ) {
CompositionLocalProvider(LocalShapes provides ShapeSize()) {
MaterialTheme( MaterialTheme(
shapes = AppShapes, colorScheme = when {
colors = when {
darkTheme && blackTheme -> BlackColors darkTheme && blackTheme -> BlackColors
darkTheme -> DarkColors darkTheme -> DarkColors
else -> LightColors else -> LightColors
@ -23,42 +25,79 @@ fun AppTheme(
content = content content = content
) )
} }
}
private val LightColors = lightColors( private val LightColors = lightColorScheme(
primary = LightPrimary, primary = LightPrimary,
primaryVariant = LightPrimaryContainer,
onPrimary = LightOnPrimary, onPrimary = LightOnPrimary,
primaryContainer = LightPrimaryContainer,
onPrimaryContainer = LightOnPrimaryContainer,
secondary = LightSecondary, secondary = LightSecondary,
secondaryVariant = LightSecondaryContainer,
onSecondary = LightOnSecondary, onSecondary = LightOnSecondary,
secondaryContainer = LightSecondaryContainer,
onSecondaryContainer = LightOnSecondaryContainer,
surface = LightSurface, surface = LightSurface,
onSurface = LightOnSurface,
surfaceVariant = LightSurfaceVariant,
onSurfaceVariant = LightOnSurfaceVariant,
outline = LightOutline,
background = LightBackground, background = LightBackground,
onBackground = LightOnBackground, onBackground = LightOnBackground,
error = LightError inversePrimary = LightInversePrimary,
inverseSurface = LightInverseSurface,
inverseOnSurface = LightInverseOnSurface,
error = LightError,
onError = LightOnError,
errorContainer = LightErrorContainer,
onErrorContainer = LightOnErrorContainer
) )
private val DarkColors = darkColors( private val DarkColors = darkColorScheme(
primary = DarkPrimary, primary = DarkPrimary,
primaryVariant = DarkPrimaryContainer,
onPrimary = DarkOnPrimary, onPrimary = DarkOnPrimary,
primaryContainer = DarkPrimaryContainer,
onPrimaryContainer = DarkOnPrimaryContainer,
secondary = DarkSecondary, secondary = DarkSecondary,
secondaryVariant = DarkSecondaryContainer,
onSecondary = DarkOnSecondary, onSecondary = DarkOnSecondary,
secondaryContainer = DarkSecondaryContainer,
onSecondaryContainer = DarkOnSecondaryContainer,
surface = DarkSurface, surface = DarkSurface,
onSurface = DarkOnSurface,
surfaceVariant = DarkSurfaceVariant,
onSurfaceVariant = DarkOnSurfaceVariant,
outline = DarkOutline,
background = DarkBackground, background = DarkBackground,
onBackground = DarkOnBackground, onBackground = DarkOnBackground,
error = DarkError inversePrimary = DarkInversePrimary,
inverseSurface = DarkInverseSurface,
inverseOnSurface = DarkInverseOnSurface,
error = DarkError,
onError = DarkOnError,
errorContainer = DarkErrorContainer,
onErrorContainer = DarkOnErrorContainer
) )
private val BlackColors = darkColors( private val BlackColors = darkColorScheme(
primary = DarkPrimary, primary = DarkPrimary,
primaryVariant = DarkPrimaryContainer,
onPrimary = DarkOnPrimary, onPrimary = DarkOnPrimary,
primaryContainer = DarkPrimaryContainer,
onPrimaryContainer = DarkOnPrimaryContainer,
secondary = DarkSecondary, secondary = DarkSecondary,
secondaryVariant = DarkSecondaryContainer,
onSecondary = DarkOnSecondary, onSecondary = DarkOnSecondary,
secondaryContainer = DarkSecondaryContainer,
onSecondaryContainer = DarkOnSecondaryContainer,
surface = BlackSurface, surface = BlackSurface,
onSurface = DarkOnSurface,
surfaceVariant = DarkSurfaceVariant,
onSurfaceVariant = DarkOnSurfaceVariant,
outline = DarkOutline,
background = BlackBackground, background = BlackBackground,
onBackground = DarkOnBackground, onBackground = DarkOnBackground,
error = DarkError inversePrimary = DarkInversePrimary,
inverseSurface = DarkInverseSurface,
inverseOnSurface = DarkInverseOnSurface,
error = DarkError,
onError = DarkOnError,
errorContainer = DarkErrorContainer,
onErrorContainer = DarkOnErrorContainer
) )