diff --git a/build.gradle b/build.gradle index 7d12d126..6058e1ee 100644 --- a/build.gradle +++ b/build.gradle @@ -135,9 +135,9 @@ dependencies { implementation 'androidx.appcompat:appcompat-resources:1.4.1' implementation 'androidx.fragment:fragment-ktx:1.4.1' implementation 'androidx.activity:activity-ktx:1.4.0' - implementation "androidx.preference:preference-ktx:1.1.1" - implementation "androidx.navigation:navigation-fragment-ktx:2.5.0-alpha01" - implementation "androidx.navigation:navigation-ui-ktx:2.5.0-alpha01" + implementation "androidx.preference:preference-ktx:1.2.0" + implementation "androidx.navigation:navigation-fragment-ktx:2.5.0-alpha03" + implementation "androidx.navigation:navigation-ui-ktx:2.5.0-alpha03" // Material3 implementation 'com.google.android.material:material:1.6.0-alpha02' @@ -171,7 +171,7 @@ dependencies { implementation "org.jetbrains:markdown:0.2.4" // 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-android:1.6.0' @@ -179,20 +179,20 @@ dependencies { implementation 'androidx.paging:paging-runtime-ktx:3.1.0' // Room - implementation 'androidx.room:room-runtime:2.4.1' - implementation 'androidx.room:room-ktx:2.4.1' - implementation 'androidx.room:room-rxjava3:2.4.1' - kapt 'androidx.room:room-compiler:2.4.1' + implementation 'androidx.room:room-runtime:2.4.2' + implementation 'androidx.room:room-ktx:2.4.2' + implementation 'androidx.room:room-rxjava3:2.4.2' + kapt 'androidx.room:room-compiler:2.4.2' // Compose - implementation "androidx.compose.runtime:runtime:1.2.0-alpha02" - implementation "androidx.compose.ui:ui:1.2.0-alpha02" - implementation "androidx.compose.ui:ui-tooling:1.2.0-alpha02" - implementation "androidx.compose.foundation:foundation:1.2.0-alpha02" - implementation "androidx.compose.foundation:foundation-layout:1.2.0-alpha02" - implementation "androidx.compose.runtime:runtime-livedata:1.2.0-alpha02" - implementation "androidx.compose.material:material:1.2.0-alpha02" - implementation "com.google.android.material:compose-theme-adapter:1.1.3" + implementation "androidx.compose.runtime:runtime:1.2.0-alpha04" + implementation "androidx.compose.ui:ui:1.2.0-alpha04" + implementation "androidx.compose.ui:ui-tooling:1.2.0-alpha04" + implementation "androidx.compose.foundation:foundation:1.2.0-alpha04" + implementation "androidx.compose.runtime:runtime-livedata:1.2.0-alpha04" + implementation "androidx.compose.material3:material3:1.0.0-alpha06" + implementation "androidx.compose.material:material:1.2.0-alpha04" + 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 diff --git a/src/main/kotlin/com/looker/droidify/network/CoilDownloader.kt b/src/main/kotlin/com/looker/droidify/network/CoilDownloader.kt index 7f936736..6158a062 100644 --- a/src/main/kotlin/com/looker/droidify/network/CoilDownloader.kt +++ b/src/main/kotlin/com/looker/droidify/network/CoilDownloader.kt @@ -145,7 +145,7 @@ object CoilDownloader { fun createIconUri( packageName: String, icon: String, metadataIcon: String, - address: String, auth: String + address: String?, auth: String? ): Uri = Uri.Builder().scheme("https").authority(HOST_ICON) .appendQueryParameter(QUERY_ADDRESS, address) .appendQueryParameter(QUERY_AUTHENTICATION, auth) diff --git a/src/main/kotlin/com/looker/droidify/ui/compose/Items.kt b/src/main/kotlin/com/looker/droidify/ui/compose/Items.kt index 22af69e0..9463a1dc 100644 --- a/src/main/kotlin/com/looker/droidify/ui/compose/Items.kt +++ b/src/main/kotlin/com/looker/droidify/ui/compose/Items.kt @@ -1,30 +1,25 @@ package com.looker.droidify.ui.compose -import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.ContentAlpha import androidx.compose.material.LocalContentAlpha -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier 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.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview 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.entity.ProductItem import com.looker.droidify.network.CoilDownloader +import com.looker.droidify.ui.compose.components.NetworkImage import com.looker.droidify.ui.compose.theme.AppTheme @Composable @@ -33,36 +28,31 @@ fun ProductRow( repo: Repository? = null, onUserClick: (ProductItem) -> Unit = {} ) { + + val imageData by remember { + mutableStateOf( + CoilDownloader.createIconUri( + item.packageName, + item.icon, + item.metadataIcon, + repo?.address, + repo?.authentication + ) + ) + } + Row( modifier = Modifier .padding(4.dp) .fillMaxWidth() - .background(color = MaterialTheme.colors.surface, shape = RoundedCornerShape(8.dp)) + .background(color = MaterialTheme.colorScheme.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( - item.packageName, - item.icon, - item.metadataIcon, - repo.address, - 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, - modifier = Modifier - .requiredSize(56.dp) - .clip(shape = RoundedCornerShape(8.dp)), - contentScale = ContentScale.Crop, - contentDescription = null + NetworkImage( + modifier = Modifier.size(56.dp), + data = imageData ) Column( @@ -84,24 +74,23 @@ fun ProductRow( fontWeight = FontWeight.Bold, softWrap = true, overflow = TextOverflow.Ellipsis, - style = MaterialTheme.typography.body1 + style = MaterialTheme.typography.bodyMedium ) Text( text = item.version, modifier = Modifier .align(Alignment.CenterEnd), overflow = TextOverflow.Ellipsis, - style = MaterialTheme.typography.body2 + style = MaterialTheme.typography.bodySmall ) } CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) { Text( modifier = Modifier.fillMaxHeight(), text = item.summary, - style = MaterialTheme.typography.body2 + style = MaterialTheme.typography.bodySmall ) } - } } } @@ -112,60 +101,49 @@ fun ProductColumn( repo: Repository? = null, onUserClick: (ProductItem) -> Unit = {} ) { + + val imageData by remember { + mutableStateOf( + CoilDownloader.createIconUri( + item.packageName, + item.icon, + item.metadataIcon, + repo?.address, + repo?.authentication + ) + ) + } + Column( modifier = Modifier .padding(4.dp) .requiredSize(72.dp, 96.dp) - .background(color = MaterialTheme.colors.surface, shape = RoundedCornerShape(8.dp)) + .background(color = MaterialTheme.colorScheme.surface, shape = RoundedCornerShape(8.dp)) .clip(shape = RoundedCornerShape(8.dp)) .clickable(onClick = { onUserClick(item) }) - .padding(4.dp) + .padding(4.dp), + horizontalAlignment = Alignment.CenterHorizontally ) { - // TODO: Fix the issue where coil doesn't fallback to the palceholder - val imagePainter = if (repo != null) - rememberImagePainter( - CoilDownloader.createIconUri( - item.packageName, - item.icon, - item.metadataIcon, - repo.address, - 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, - modifier = Modifier - .requiredSize(56.dp) - .clip(shape = RoundedCornerShape(8.dp)) - .align(Alignment.CenterHorizontally), - contentScale = ContentScale.Crop, - contentDescription = null + NetworkImage( + modifier = Modifier.size(56.dp), + data = imageData ) Text( text = item.name, - modifier = Modifier - .align(Alignment.CenterHorizontally), fontWeight = FontWeight.Bold, - style = MaterialTheme.typography.body2, + style = MaterialTheme.typography.bodyLarge, overflow = TextOverflow.Ellipsis, maxLines = 1 ) CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) { Text( text = item.version, - modifier = Modifier - .align(Alignment.CenterHorizontally), - style = MaterialTheme.typography.overline, + style = MaterialTheme.typography.labelSmall, overflow = TextOverflow.Ellipsis, maxLines = 1 ) } - } } diff --git a/src/main/kotlin/com/looker/droidify/ui/compose/components/Image.kt b/src/main/kotlin/com/looker/droidify/ui/compose/components/Image.kt new file mode 100644 index 00000000..ec7eb638 --- /dev/null +++ b/src/main/kotlin/com/looker/droidify/ui/compose/components/Image.kt @@ -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) + ) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/looker/droidify/ui/compose/theme/Color.kt b/src/main/kotlin/com/looker/droidify/ui/compose/theme/Color.kt index 23f4aaa1..b7e1a59a 100644 --- a/src/main/kotlin/com/looker/droidify/ui/compose/theme/Color.kt +++ b/src/main/kotlin/com/looker/droidify/ui/compose/theme/Color.kt @@ -57,5 +57,4 @@ val DarkInverseOnSurface = Color(0xFF006D3E) val BlackBackground = Color(0xFF000000) val BlackSurface = Color(0xFF191C1A) -val Seed = Color(0xFF51DF93) - +val Seed = Color(0xFF51DF93) \ No newline at end of file diff --git a/src/main/kotlin/com/looker/droidify/ui/compose/theme/Shape.kt b/src/main/kotlin/com/looker/droidify/ui/compose/theme/Shape.kt index 454bb388..f7619bc2 100644 --- a/src/main/kotlin/com/looker/droidify/ui/compose/theme/Shape.kt +++ b/src/main/kotlin/com/looker/droidify/ui/compose/theme/Shape.kt @@ -1,11 +1,15 @@ package com.looker.droidify.ui.compose.theme -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.Shapes +import androidx.compose.runtime.Immutable +import androidx.compose.runtime.staticCompositionLocalOf +import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp -val AppShapes = Shapes( - small = RoundedCornerShape(4.dp), - medium = RoundedCornerShape(8.dp), - large = RoundedCornerShape(16.dp) -) \ No newline at end of file +@Immutable +data class ShapeSize( + val small: Dp = 4.dp, + val medium: Dp = 8.dp, + val large: Dp = 16.dp, +) + +val LocalShapes = staticCompositionLocalOf { ShapeSize() } \ No newline at end of file diff --git a/src/main/kotlin/com/looker/droidify/ui/compose/theme/Theme.kt b/src/main/kotlin/com/looker/droidify/ui/compose/theme/Theme.kt index ece6f3f2..914ad82b 100644 --- a/src/main/kotlin/com/looker/droidify/ui/compose/theme/Theme.kt +++ b/src/main/kotlin/com/looker/droidify/ui/compose/theme/Theme.kt @@ -1,10 +1,11 @@ package com.looker.droidify.ui.compose.theme import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.material.MaterialTheme -import androidx.compose.material.darkColors -import androidx.compose.material.lightColors +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.darkColorScheme +import androidx.compose.material3.lightColorScheme import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider import com.looker.droidify.utility.isBlackTheme @Composable @@ -13,52 +14,90 @@ fun AppTheme( blackTheme: Boolean = isBlackTheme, content: @Composable () -> Unit ) { - MaterialTheme( - shapes = AppShapes, - colors = when { - darkTheme && blackTheme -> BlackColors - darkTheme -> DarkColors - else -> LightColors - }, - content = content - ) + + CompositionLocalProvider(LocalShapes provides ShapeSize()) { + MaterialTheme( + colorScheme = when { + darkTheme && blackTheme -> BlackColors + darkTheme -> DarkColors + else -> LightColors + }, + content = content + ) + } } -private val LightColors = lightColors( +private val LightColors = lightColorScheme( primary = LightPrimary, - primaryVariant = LightPrimaryContainer, onPrimary = LightOnPrimary, + primaryContainer = LightPrimaryContainer, + onPrimaryContainer = LightOnPrimaryContainer, secondary = LightSecondary, - secondaryVariant = LightSecondaryContainer, onSecondary = LightOnSecondary, + secondaryContainer = LightSecondaryContainer, + onSecondaryContainer = LightOnSecondaryContainer, surface = LightSurface, + onSurface = LightOnSurface, + surfaceVariant = LightSurfaceVariant, + onSurfaceVariant = LightOnSurfaceVariant, + outline = LightOutline, background = LightBackground, 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, - primaryVariant = DarkPrimaryContainer, onPrimary = DarkOnPrimary, + primaryContainer = DarkPrimaryContainer, + onPrimaryContainer = DarkOnPrimaryContainer, secondary = DarkSecondary, - secondaryVariant = DarkSecondaryContainer, onSecondary = DarkOnSecondary, + secondaryContainer = DarkSecondaryContainer, + onSecondaryContainer = DarkOnSecondaryContainer, surface = DarkSurface, + onSurface = DarkOnSurface, + surfaceVariant = DarkSurfaceVariant, + onSurfaceVariant = DarkOnSurfaceVariant, + outline = DarkOutline, background = DarkBackground, 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, - primaryVariant = DarkPrimaryContainer, onPrimary = DarkOnPrimary, + primaryContainer = DarkPrimaryContainer, + onPrimaryContainer = DarkOnPrimaryContainer, secondary = DarkSecondary, - secondaryVariant = DarkSecondaryContainer, onSecondary = DarkOnSecondary, + secondaryContainer = DarkSecondaryContainer, + onSecondaryContainer = DarkOnSecondaryContainer, surface = BlackSurface, + onSurface = DarkOnSurface, + surfaceVariant = DarkSurfaceVariant, + onSurfaceVariant = DarkOnSurfaceVariant, + outline = DarkOutline, background = BlackBackground, onBackground = DarkOnBackground, - error = DarkError + inversePrimary = DarkInversePrimary, + inverseSurface = DarkInverseSurface, + inverseOnSurface = DarkInverseOnSurface, + error = DarkError, + onError = DarkOnError, + errorContainer = DarkErrorContainer, + onErrorContainer = DarkOnErrorContainer ) \ No newline at end of file