improvement(Android): Small redesign for Products to solve Play store issues

This commit is contained in:
Artem Chepurnoy 2024-09-02 11:11:02 +03:00
parent 7d1d927c4c
commit c046178df4
No known key found for this signature in database
GPG Key ID: FAC37D0CF674043E
1 changed files with 121 additions and 145 deletions

View File

@ -1,14 +1,13 @@
package com.artemchep.keyguard.feature.home.settings.component package com.artemchep.keyguard.feature.home.settings.component
import androidx.compose.animation.animateColorAsState
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.border import androidx.compose.foundation.border
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
@ -18,7 +17,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Star import androidx.compose.material.icons.outlined.Star
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
@ -26,7 +24,6 @@ import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.contentColorFor
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
@ -53,18 +50,12 @@ import com.artemchep.keyguard.feature.onboarding.onboardingItemsPremium
import com.artemchep.keyguard.platform.LocalLeContext import com.artemchep.keyguard.platform.LocalLeContext
import com.artemchep.keyguard.res.Res import com.artemchep.keyguard.res.Res
import com.artemchep.keyguard.res.* import com.artemchep.keyguard.res.*
import com.artemchep.keyguard.ui.Ah
import com.artemchep.keyguard.ui.DefaultEmphasisAlpha import com.artemchep.keyguard.ui.DefaultEmphasisAlpha
import com.artemchep.keyguard.ui.ExpandedIfNotEmpty
import com.artemchep.keyguard.ui.FlatItemLayout
import com.artemchep.keyguard.ui.FlatItemTextContent
import com.artemchep.keyguard.ui.FlatSimpleNote import com.artemchep.keyguard.ui.FlatSimpleNote
import com.artemchep.keyguard.ui.FlatTextFieldBadge import com.artemchep.keyguard.ui.FlatTextFieldBadge
import com.artemchep.keyguard.ui.GridLayout import com.artemchep.keyguard.ui.GridLayout
import com.artemchep.keyguard.ui.MediumEmphasisAlpha import com.artemchep.keyguard.ui.MediumEmphasisAlpha
import com.artemchep.keyguard.ui.SimpleNote import com.artemchep.keyguard.ui.SimpleNote
import com.artemchep.keyguard.ui.icons.ChevronIcon
import com.artemchep.keyguard.ui.shimmer.shimmer
import com.artemchep.keyguard.ui.skeleton.SkeletonText import com.artemchep.keyguard.ui.skeleton.SkeletonText
import com.artemchep.keyguard.ui.theme.Dimens import com.artemchep.keyguard.ui.theme.Dimens
import com.artemchep.keyguard.ui.theme.combineAlpha import com.artemchep.keyguard.ui.theme.combineAlpha
@ -164,13 +155,7 @@ private fun SettingSubscriptions(
Section(text = stringResource(Res.string.pref_item_premium_membership_section_subscriptions_title)) Section(text = stringResource(Res.string.pref_item_premium_membership_section_subscriptions_title))
loadableSubscriptions.fold( loadableSubscriptions.fold(
ifLoading = { ifLoading = {
GridLayout( SettingGroupLayout {
modifier = Modifier
.padding(horizontal = 8.dp),
columns = 2,
mainAxisSpacing = 8.dp,
crossAxisSpacing = 8.dp,
) {
repeat(SubscriptionsCountDefault) { repeat(SubscriptionsCountDefault) {
SettingSubscriptionSkeletonItem( SettingSubscriptionSkeletonItem(
modifier = Modifier, modifier = Modifier,
@ -186,13 +171,7 @@ private fun SettingSubscriptions(
) )
return@fold return@fold
} }
GridLayout( SettingGroupLayout {
modifier = Modifier
.padding(horizontal = 8.dp),
columns = 2,
mainAxisSpacing = 8.dp,
crossAxisSpacing = 8.dp,
) {
subscriptions.forEach { subscription -> subscriptions.forEach { subscription ->
SettingSubscriptionItem( SettingSubscriptionItem(
modifier = Modifier, modifier = Modifier,
@ -215,8 +194,10 @@ private fun SettingSubscriptions(
Section(text = stringResource(Res.string.pref_item_premium_membership_section_products_title)) Section(text = stringResource(Res.string.pref_item_premium_membership_section_products_title))
loadableProducts.fold( loadableProducts.fold(
ifLoading = { ifLoading = {
repeat(ProductsCountDefault) { SettingGroupLayout {
SettingSubscriptionSkeletonItem() repeat(ProductsCountDefault) {
SettingSubscriptionSkeletonItem()
}
} }
}, },
ifOk = { products -> ifOk = { products ->
@ -227,10 +208,12 @@ private fun SettingSubscriptions(
) )
return@fold return@fold
} }
products.forEach { product -> SettingGroupLayout {
SettingProductItem( products.forEach { product ->
product = product, SettingProductItem(
) product = product,
)
}
} }
}, },
) )
@ -241,28 +224,27 @@ private fun SettingSubscriptions(
private fun SettingSubscriptionSkeletonItem( private fun SettingSubscriptionSkeletonItem(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
SettingSubscriptionContentItem( SettingItemLayout(
modifier = modifier modifier = modifier,
.shimmer(),
isActive = false, isActive = false,
) { onClick = {
Column( // Do nothing
modifier = Modifier },
.padding(8.dp), title = {
) {
SkeletonText( SkeletonText(
modifier = Modifier modifier = Modifier
.fillMaxWidth(0.45f), .fillMaxWidth(0.45f),
style = MaterialTheme.typography.titleSmall, style = MaterialTheme.typography.titleSmall,
) )
Spacer(modifier = Modifier.height(8.dp)) },
price = {
SkeletonText( SkeletonText(
modifier = Modifier modifier = Modifier
.fillMaxWidth(0.45f), .fillMaxWidth(0.45f),
style = MaterialTheme.typography.titleMedium, style = MaterialTheme.typography.titleMedium,
) )
} },
} )
} }
@Composable @Composable
@ -271,44 +253,23 @@ private fun SettingSubscriptionItem(
subscription: Subscription, subscription: Subscription,
) { ) {
val context by rememberUpdatedState(LocalLeContext) val context by rememberUpdatedState(LocalLeContext)
SettingSubscriptionContentItem( val status = subscription.status
SettingItemLayout(
modifier = modifier, modifier = modifier,
isActive = subscription.status is Subscription.Status.Active, isActive = status is Subscription.Status.Active,
) { onClick = {
Column( subscription.purchase(context)
modifier = Modifier },
.clickable(role = Role.Button) { title = {
subscription.purchase(context) Text(subscription.title)
} },
.padding(8.dp), price = {
) { val text = "${subscription.price} / ${subscription.periodFormatted}"
val localEmphasis = DefaultEmphasisAlpha Text(text)
val localTextStyle = TextStyle( },
color = LocalContentColor.current content = if (status is Subscription.Status.Inactive && status.trialPeriodFormatted != null) {
.combineAlpha(localEmphasis), // composable
) {
CompositionLocalProvider(
LocalTextStyle provides MaterialTheme.typography.titleSmall
.merge(localTextStyle),
) {
Text(
subscription.title,
color = LocalContentColor.current
.combineAlpha(MediumEmphasisAlpha),
)
}
Spacer(modifier = Modifier.height(8.dp))
CompositionLocalProvider(
LocalTextStyle provides MaterialTheme.typography.titleMedium
.merge(localTextStyle),
) {
val text = "${subscription.price} / ${subscription.periodFormatted}"
Text(text)
}
Spacer(modifier = Modifier.height(8.dp))
val status = subscription.status
if (status is Subscription.Status.Inactive && status.trialPeriodFormatted != null) {
FlatTextFieldBadge( FlatTextFieldBadge(
type = TextFieldModel2.Vl.Type.INFO, type = TextFieldModel2.Vl.Type.INFO,
text = stringResource( text = stringResource(
@ -317,15 +278,58 @@ private fun SettingSubscriptionItem(
), ),
) )
} }
} } else {
null
},
)
}
@Composable
private fun SettingProductItem(
modifier: Modifier = Modifier,
product: Product,
) {
val context by rememberUpdatedState(LocalLeContext)
val status = product.status
SettingItemLayout(
modifier = modifier,
isActive = status is Product.Status.Active,
onClick = {
product.purchase(context)
},
title = {
Text(product.title)
},
price = {
Text(product.price)
},
)
}
@Composable
private fun SettingGroupLayout(
modifier: Modifier = Modifier,
content: @Composable () -> Unit,
) {
GridLayout(
modifier = modifier
.padding(horizontal = 8.dp),
columns = 2,
mainAxisSpacing = 8.dp,
crossAxisSpacing = 8.dp,
) {
content()
} }
} }
@Composable @Composable
private fun SettingSubscriptionContentItem( private fun SettingItemLayout(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
isActive: Boolean, isActive: Boolean,
content: @Composable BoxScope.() -> Unit, onClick: () -> Unit,
title: @Composable ColumnScope.() -> Unit,
price: @Composable ColumnScope.() -> Unit,
content: (@Composable ColumnScope.() -> Unit)? = null,
) { ) {
val backgroundModifier = run { val backgroundModifier = run {
val tintColor = MaterialTheme.colorScheme val tintColor = MaterialTheme.colorScheme
@ -357,69 +361,41 @@ private fun SettingSubscriptionContentItem(
contentDescription = null, contentDescription = null,
) )
} }
content() val updatedOnClick by rememberUpdatedState(onClick)
Column(
modifier = Modifier
.clickable(role = Role.Button) {
updatedOnClick()
}
.padding(8.dp),
) {
val localEmphasis = DefaultEmphasisAlpha
val localTitleTextStyle = TextStyle(
color = LocalContentColor.current
.combineAlpha(localEmphasis)
.combineAlpha(MediumEmphasisAlpha),
)
val localPriceTextStyle = TextStyle(
color = LocalContentColor.current
.combineAlpha(localEmphasis),
)
CompositionLocalProvider(
LocalTextStyle provides MaterialTheme.typography.titleSmall
.merge(localTitleTextStyle),
) {
title()
}
Spacer(modifier = Modifier.height(8.dp))
CompositionLocalProvider(
LocalTextStyle provides MaterialTheme.typography.titleMedium
.merge(localPriceTextStyle),
) {
price()
}
if (content != null) {
Spacer(modifier = Modifier.height(8.dp))
content()
}
}
} }
} }
@Composable
private fun SettingProductItem(
product: Product,
) {
val context by rememberUpdatedState(LocalLeContext)
FlatItemLayout(
leading = {
val backgroundColor = MaterialTheme.colorScheme.secondaryContainer
Box(
modifier = Modifier
.size(24.dp)
.background(backgroundColor, CircleShape)
.padding(4.dp),
) {
val targetContentColor = kotlin.run {
val active = product.status is Product.Status.Active
if (active) {
MaterialTheme.colorScheme.primary
} else {
contentColorFor(backgroundColor)
}
}
val contentColor by animateColorAsState(targetValue = targetContentColor)
Icon(
Icons.Outlined.Star,
contentDescription = null,
tint = contentColor,
)
}
},
elevation = 2.dp,
trailing = {
ChevronIcon()
},
content = {
FlatItemTextContent(
title = {
Text(product.price)
},
text = {
Text(product.title)
},
)
val statusOrNull = product.status as? Product.Status.Active
ExpandedIfNotEmpty(statusOrNull) {
Row(
modifier = Modifier
.padding(top = 4.dp),
) {
Ah(
score = 1f,
text = stringResource(Res.string.pref_item_premium_status_active),
)
}
}
},
onClick = {
product.purchase(context)
},
)
}