Create AppDetail Header

Add InstallState enum { TODO(Replace with sealed class) }
Use string in image loading { Uri handling is not good in Compose }
Make Zoomable image more performant
Add few strings { Might Change }
This commit is contained in:
Iamlooker 2022-04-07 10:41:23 +05:30
parent 12ad62e948
commit c64fff0a4a
8 changed files with 223 additions and 41 deletions

View File

@ -13,3 +13,10 @@ enum class UpdateCategory(val id: Int) {
UPDATED(1),
NEW(2)
}
enum class InstallState {
INSTALL,
INSTALLING,
INSTALLED,
PENDING
}

View File

@ -36,7 +36,7 @@ fun ProductCard(
item.metadataIcon,
repo?.address,
repo?.authentication
)
).toString()
)
}

View File

@ -40,7 +40,7 @@ fun ProductsListItem(
item.metadataIcon,
repo?.address,
repo?.authentication
)
).toString()
)
}

View File

@ -36,7 +36,7 @@ fun ScreenshotList(
.wrapContentWidth()
.requiredHeight(120.dp)
.clickable { onScreenShotClick(it) },
data = it.path.toUri(),
data = it.path,
shape = RoundedCornerShape(LocalShapes.current.large)
)
}

View File

@ -0,0 +1,2 @@
package com.looker.droidify.ui.compose.pages.app_detail

View File

@ -0,0 +1,186 @@
package com.looker.droidify.ui.compose.pages.app_detail.components
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Close
import androidx.compose.material.icons.rounded.Download
import androidx.compose.material.icons.rounded.Launch
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.looker.droidify.R
import com.looker.droidify.entity.InstallState
import com.looker.droidify.ui.compose.theme.LocalShapes
import com.looker.droidify.ui.compose.utils.NetworkImage
import com.looker.droidify.utility.extension.text.formatSize
@Composable
fun Header(
modifier: Modifier = Modifier,
icon: String?,
appName: String,
packageName: String,
versionCode: String,
appSize: String,
appDev: String
) {
Surface(
modifier = modifier.fillMaxWidth(),
shape = RoundedCornerShape(LocalShapes.current.large)
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.SpaceEvenly
) {
HeaderTitle(
modifier = Modifier.fillMaxWidth(),
icon = icon,
appName = appName,
packageName = packageName
)
HeaderExtra(
versionCode = versionCode,
appSize = appSize,
appDev = appDev
)
DownloadProgress(totalSize = 69420)
InstallButton(installState = InstallState.PENDING)
}
}
}
@Composable
fun HeaderTitle(
modifier: Modifier = Modifier,
icon: String? = null,
appName: String,
packageName: String,
actions: @Composable () -> Unit = {}
) {
Row(
modifier = modifier.padding(16.dp),
horizontalArrangement = Arrangement.Start,
verticalAlignment = Alignment.CenterVertically
) {
NetworkImage(
modifier = Modifier.size(56.dp),
data = icon
)
Spacer(modifier = Modifier.width(16.dp))
Column(
horizontalAlignment = Alignment.Start,
verticalArrangement = Arrangement.SpaceAround
) {
Text(text = appName, style = MaterialTheme.typography.titleLarge)
Text(text = packageName, style = MaterialTheme.typography.bodyMedium)
}
Box { actions() }
}
}
@Composable
fun HeaderExtra(
modifier: Modifier = Modifier,
versionCode: String,
appSize: String,
appDev: String
) {
Row(
modifier = modifier
.fillMaxWidth()
.wrapContentHeight()
) {
HeaderExtrasCard(
modifier = Modifier.weight(1f),
title = stringResource(id = R.string.version),
text = versionCode
)
HeaderExtrasCard(
modifier = Modifier.weight(1f),
title = stringResource(id = R.string.size),
text = appSize
)
HeaderExtrasCard(
modifier = Modifier.weight(1f),
title = stringResource(id = R.string.source_code),
text = appDev
)
}
}
@Composable
fun HeaderExtrasCard(
modifier: Modifier = Modifier,
extra: @Composable () -> Unit = {},
title: String,
text: String,
onClick: () -> Unit = {}
) {
Surface(
modifier = modifier
.clip(RoundedCornerShape(50))
.clickable { onClick() }
.padding(16.dp)
) {
Column(
verticalArrangement = Arrangement.SpaceEvenly,
horizontalAlignment = Alignment.CenterHorizontally
) {
extra()
Text(text = title, style = MaterialTheme.typography.labelLarge)
Text(text = text, style = MaterialTheme.typography.bodyMedium)
}
}
}
@Composable
fun DownloadProgress(
modifier: Modifier = Modifier,
downloading: Float = 0f,
totalSize: Long
) {
Column(
modifier = modifier.fillMaxWidth(),
horizontalAlignment = Alignment.Start
) {
Text(text = totalSize.div(downloading).toLong().formatSize() + "/" + totalSize.formatSize())
Spacer(modifier = Modifier.height(8.dp))
LinearProgressIndicator(progress = downloading)
}
}
@Composable
fun InstallButton(
modifier: Modifier = Modifier,
installState: InstallState,
onClick: () -> Unit = {}
) {
Button(
modifier = modifier,
onClick = onClick
) {
Icon(
imageVector = when (installState) {
InstallState.INSTALLING -> Icons.Rounded.Close
InstallState.INSTALLED -> Icons.Rounded.Launch
InstallState.PENDING -> Icons.Rounded.Close
InstallState.INSTALL -> Icons.Rounded.Download
},
contentDescription = null
)
Text(
text = when (installState) {
InstallState.INSTALLING -> stringResource(id = R.string.installing)
InstallState.INSTALLED -> stringResource(id = R.string.launch)
InstallState.PENDING -> stringResource(id = R.string.pending)
InstallState.INSTALL -> stringResource(id = R.string.install)
}
)
}
}

View File

@ -1,37 +1,30 @@
package com.looker.droidify.ui.compose.utils
import android.net.Uri
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.detectTransformGestures
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.gestures.rememberTransformableState
import androidx.compose.foundation.gestures.transformable
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import coil.annotation.ExperimentalCoilApi
import coil.compose.SubcomposeAsyncImage
import coil.compose.SubcomposeAsyncImageContent
import com.looker.droidify.R
import com.looker.droidify.ui.compose.theme.LocalShapes
@OptIn(ExperimentalCoilApi::class)
@Composable
fun NetworkImage(
modifier: Modifier = Modifier,
data: Uri?,
data: String?,
contentScale: ContentScale = ContentScale.Crop,
backgroundColor: Color = MaterialTheme.colorScheme.surface,
shape: Shape = RoundedCornerShape(LocalShapes.current.medium)
@ -57,32 +50,24 @@ fun NetworkImage(
@Composable
fun ZoomableImage(
modifier: Modifier = Modifier,
data: Uri?
data: String?,
) {
val scale = remember { mutableStateOf(1f) }
val rotationState = remember { mutableStateOf(1f) }
Box(
modifier = modifier
.fillMaxSize() // Give the size you want...
.background(Color.Gray)
.pointerInput(Unit) {
detectTransformGestures { _, _, zoom, rotation ->
scale.value *= zoom
rotationState.value += rotation
var scale by remember { mutableStateOf(1f) }
var offset by remember { mutableStateOf(Offset.Zero) }
val state = rememberTransformableState { zoomChange, offsetChange, _ ->
scale *= zoomChange
offset += offsetChange * scale
}
}
) {
NetworkImage(
modifier = Modifier
.align(Alignment.Center) // keep the image centralized into the Box
modifier = modifier
.graphicsLayer(
// adding some zoom limits (min 50%, max 200%)
scaleX = maxOf(.5f, minOf(3f, scale.value)),
scaleY = maxOf(.5f, minOf(3f, scale.value)),
rotationZ = rotationState.value
),
scaleX = scale,
scaleY = scale,
translationX = offset.x,
translationY = offset.y
)
.transformable(state = state),
data = data,
shape = RectangleShape
)
}
}

View File

@ -187,4 +187,6 @@
<string name="prefs_new_apps">Number of new apps to show</string>
<string name="new_repository">New repository</string>
<string name="default_tab">Default Tab</string>
<string name="pending">Pending</string>
<string name="installing">Installing</string>
</resources>