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), UPDATED(1),
NEW(2) NEW(2)
} }
enum class InstallState {
INSTALL,
INSTALLING,
INSTALLED,
PENDING
}

View File

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

View File

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

View File

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

View File

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