deps(Android): Update to Android Billing v6.0
This commit is contained in:
parent
0ca1a1c15e
commit
be9807c5af
|
@ -4,9 +4,10 @@ import android.app.Activity
|
||||||
import com.android.billingclient.api.AcknowledgePurchaseParams
|
import com.android.billingclient.api.AcknowledgePurchaseParams
|
||||||
import com.android.billingclient.api.BillingClient
|
import com.android.billingclient.api.BillingClient
|
||||||
import com.android.billingclient.api.BillingFlowParams
|
import com.android.billingclient.api.BillingFlowParams
|
||||||
|
import com.android.billingclient.api.ProductDetails
|
||||||
import com.android.billingclient.api.Purchase
|
import com.android.billingclient.api.Purchase
|
||||||
import com.android.billingclient.api.SkuDetails
|
import com.android.billingclient.api.QueryProductDetailsParams
|
||||||
import com.android.billingclient.api.SkuDetailsParams
|
import com.android.billingclient.api.QueryPurchasesParams
|
||||||
import com.artemchep.keyguard.common.model.RichResult
|
import com.artemchep.keyguard.common.model.RichResult
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
@ -21,7 +22,7 @@ interface BillingConnection {
|
||||||
* Perform a network query to get SKU details and
|
* Perform a network query to get SKU details and
|
||||||
* return the result asynchronously.
|
* return the result asynchronously.
|
||||||
*/
|
*/
|
||||||
fun skuDetailsFlow(skuDetailsParams: SkuDetailsParams): Flow<RichResult<List<SkuDetails>>>
|
fun productDetailsFlow(params: QueryProductDetailsParams): Flow<RichResult<List<ProductDetails>>>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get purchases details for all the items bought within your app.
|
* Get purchases details for all the items bought within your app.
|
||||||
|
@ -32,7 +33,7 @@ interface BillingConnection {
|
||||||
* https://developers.google.com/android-publisher/api-ref/purchases/products/get
|
* https://developers.google.com/android-publisher/api-ref/purchases/products/get
|
||||||
* https://developers.google.com/android-publisher/api-ref/purchases/subscriptions/get
|
* https://developers.google.com/android-publisher/api-ref/purchases/subscriptions/get
|
||||||
*/
|
*/
|
||||||
fun purchasesFlow(skuType: String): Flow<RichResult<List<Purchase>>>
|
fun purchasesFlow(params: QueryPurchasesParams): Flow<RichResult<List<Purchase>>>
|
||||||
|
|
||||||
fun launchBillingFlow(activity: Activity, billingFlowParams: BillingFlowParams)
|
fun launchBillingFlow(activity: Activity, billingFlowParams: BillingFlowParams)
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,10 @@ import com.android.billingclient.api.AcknowledgePurchaseParams
|
||||||
import com.android.billingclient.api.BillingClient
|
import com.android.billingclient.api.BillingClient
|
||||||
import com.android.billingclient.api.BillingFlowParams
|
import com.android.billingclient.api.BillingFlowParams
|
||||||
import com.android.billingclient.api.BillingResult
|
import com.android.billingclient.api.BillingResult
|
||||||
|
import com.android.billingclient.api.ProductDetails
|
||||||
import com.android.billingclient.api.Purchase
|
import com.android.billingclient.api.Purchase
|
||||||
|
import com.android.billingclient.api.QueryProductDetailsParams
|
||||||
|
import com.android.billingclient.api.QueryPurchasesParams
|
||||||
import com.android.billingclient.api.SkuDetails
|
import com.android.billingclient.api.SkuDetails
|
||||||
import com.android.billingclient.api.SkuDetailsParams
|
import com.android.billingclient.api.SkuDetailsParams
|
||||||
import com.artemchep.keyguard.common.io.IO
|
import com.artemchep.keyguard.common.io.IO
|
||||||
|
@ -59,14 +62,14 @@ class BillingConnectionImpl(
|
||||||
private const val TIMEOUT_LAUNCH_BILLING_FLOW = 1000L
|
private const val TIMEOUT_LAUNCH_BILLING_FLOW = 1000L
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun skuDetailsFlow(skuDetailsParams: SkuDetailsParams): Flow<RichResult<List<SkuDetails>>> =
|
override fun productDetailsFlow(params: QueryProductDetailsParams): Flow<RichResult<List<ProductDetails>>> =
|
||||||
mapClient { client ->
|
mapClient { client ->
|
||||||
client.mapToSkuDetails(skuDetailsParams)
|
client.mapToSkuDetails(params)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun BillingClient.mapToSkuDetails(skuDetailsParams: SkuDetailsParams) =
|
private suspend fun BillingClient.mapToSkuDetails(params: QueryProductDetailsParams) =
|
||||||
ioEffect(Dispatchers.IO) {
|
ioEffect(Dispatchers.IO) {
|
||||||
querySkuDetailsSuspending(skuDetailsParams)
|
querySkuDetailsSuspending(params)
|
||||||
}
|
}
|
||||||
.flattenMap()
|
.flattenMap()
|
||||||
.flatMap { skuDetailsList ->
|
.flatMap { skuDetailsList ->
|
||||||
|
@ -76,16 +79,16 @@ class BillingConnectionImpl(
|
||||||
?: kotlin.run {
|
?: kotlin.run {
|
||||||
val exception =
|
val exception =
|
||||||
IllegalArgumentException("Sku details list must not be null!")
|
IllegalArgumentException("Sku details list must not be null!")
|
||||||
ioRaise<List<SkuDetails>>(exception)
|
ioRaise<List<ProductDetails>>(exception)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.retryIfNetworkIssue()
|
.retryIfNetworkIssue()
|
||||||
.attempt().bind().let { RichResult.invoke(it) }
|
.attempt().bind().let { RichResult.invoke(it) }
|
||||||
|
|
||||||
override fun purchasesFlow(skuType: String): Flow<RichResult<List<Purchase>>> =
|
override fun purchasesFlow(params: QueryPurchasesParams): Flow<RichResult<List<Purchase>>> =
|
||||||
mapClient { client ->
|
mapClient { client ->
|
||||||
ioEffect(Dispatchers.IO) {
|
ioEffect(Dispatchers.IO) {
|
||||||
client.queryPurchasesSuspending(skuType)
|
client.queryPurchasesSuspending(params)
|
||||||
}
|
}
|
||||||
.flattenMap()
|
.flattenMap()
|
||||||
.flatMap { purchasesList ->
|
.flatMap { purchasesList ->
|
||||||
|
@ -169,9 +172,9 @@ class BillingConnectionImpl(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun BillingClient.querySkuDetailsSuspending(skuDetailsParams: SkuDetailsParams) =
|
private suspend fun BillingClient.querySkuDetailsSuspending(params: QueryProductDetailsParams) =
|
||||||
suspendCancellableCoroutine<Either<BillingResponseException, List<SkuDetails>?>> { continuation ->
|
suspendCancellableCoroutine<Either<BillingResponseException, List<ProductDetails>?>> { continuation ->
|
||||||
querySkuDetailsAsync(skuDetailsParams) { billingResult, skuDetailsList ->
|
queryProductDetailsAsync(params) { billingResult, skuDetailsList ->
|
||||||
kotlin.runCatching {
|
kotlin.runCatching {
|
||||||
val r = getBillingResultOrException(billingResult, skuDetailsList)
|
val r = getBillingResultOrException(billingResult, skuDetailsList)
|
||||||
continuation.resume(r)
|
continuation.resume(r)
|
||||||
|
@ -179,9 +182,9 @@ private suspend fun BillingClient.querySkuDetailsSuspending(skuDetailsParams: Sk
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun BillingClient.queryPurchasesSuspending(skuType: String) =
|
private suspend fun BillingClient.queryPurchasesSuspending(params: QueryPurchasesParams) =
|
||||||
suspendCancellableCoroutine<Either<BillingResponseException, List<Purchase>?>> { continuation ->
|
suspendCancellableCoroutine<Either<BillingResponseException, List<Purchase>?>> { continuation ->
|
||||||
queryPurchasesAsync(skuType) { billingResult, purchases ->
|
queryPurchasesAsync(params) { billingResult, purchases ->
|
||||||
kotlin.runCatching {
|
kotlin.runCatching {
|
||||||
val r = getBillingResultOrException(billingResult, purchases)
|
val r = getBillingResultOrException(billingResult, purchases)
|
||||||
continuation.resume(r)
|
continuation.resume(r)
|
||||||
|
|
|
@ -2,10 +2,11 @@ package com.artemchep.keyguard.copy
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import com.android.billingclient.api.AcknowledgePurchaseParams
|
import com.android.billingclient.api.AcknowledgePurchaseParams
|
||||||
import com.android.billingclient.api.BillingClient.SkuType
|
import com.android.billingclient.api.BillingClient.ProductType
|
||||||
import com.android.billingclient.api.BillingFlowParams
|
import com.android.billingclient.api.BillingFlowParams
|
||||||
import com.android.billingclient.api.Purchase
|
import com.android.billingclient.api.Purchase
|
||||||
import com.android.billingclient.api.SkuDetailsParams
|
import com.android.billingclient.api.QueryProductDetailsParams
|
||||||
|
import com.android.billingclient.api.QueryPurchasesParams
|
||||||
import com.artemchep.keyguard.android.closestActivityOrNull
|
import com.artemchep.keyguard.android.closestActivityOrNull
|
||||||
import com.artemchep.keyguard.billing.BillingConnection
|
import com.artemchep.keyguard.billing.BillingConnection
|
||||||
import com.artemchep.keyguard.billing.BillingManager
|
import com.artemchep.keyguard.billing.BillingManager
|
||||||
|
@ -59,8 +60,8 @@ class SubscriptionServiceAndroid(
|
||||||
.map { receiptsResult ->
|
.map { receiptsResult ->
|
||||||
receiptsResult.map { receipts ->
|
receiptsResult.map { receipts ->
|
||||||
receipts.any {
|
receipts.any {
|
||||||
val isSubscription = it.skus.intersect(SkuListSubscription).isNotEmpty()
|
val isSubscription = it.products.intersect(SkuListSubscription).isNotEmpty()
|
||||||
val isProduct = it.skus.intersect(SkuListProduct).isNotEmpty()
|
val isProduct = it.products.intersect(SkuListProduct).isNotEmpty()
|
||||||
isSubscription || isProduct
|
isSubscription || isProduct
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -69,47 +70,80 @@ class SubscriptionServiceAndroid(
|
||||||
override fun subscriptions(): Flow<List<Subscription>?> = combine(
|
override fun subscriptions(): Flow<List<Subscription>?> = combine(
|
||||||
getReceiptFlow()
|
getReceiptFlow()
|
||||||
.filter { it !is RichResult.Loading },
|
.filter { it !is RichResult.Loading },
|
||||||
getSkuDetailsFlow(SkuType.SUBS)
|
getProductDetailsFlow(ProductType.SUBS)
|
||||||
.filter { it !is RichResult.Loading },
|
.filter { it !is RichResult.Loading },
|
||||||
) { receiptsResult, skuDetailsResult ->
|
) { receiptsResult, skuDetailsResult ->
|
||||||
val receipts = receiptsResult.orNull()
|
val receipts = receiptsResult.orNull()
|
||||||
val skuDetails = skuDetailsResult.orNull()
|
val skuDetails = skuDetailsResult.orNull()
|
||||||
|
|
||||||
skuDetails?.map {
|
skuDetails?.mapNotNull {
|
||||||
|
val bestOffer = it.subscriptionOfferDetails
|
||||||
|
?.filter { it.offerId != null }
|
||||||
|
?.minByOrNull {
|
||||||
|
val firstPrice = it.pricingPhases
|
||||||
|
.pricingPhaseList
|
||||||
|
.first()
|
||||||
|
firstPrice.priceAmountMicros
|
||||||
|
}
|
||||||
|
val baseOffer = it.subscriptionOfferDetails
|
||||||
|
?.firstOrNull { it.offerId == null }
|
||||||
|
?: return@mapNotNull null
|
||||||
|
val finalPrice = baseOffer.pricingPhases
|
||||||
|
.pricingPhaseList
|
||||||
|
.last()
|
||||||
|
|
||||||
val status = kotlin.run {
|
val status = kotlin.run {
|
||||||
val skuReceipt = receipts?.firstOrNull { purchase -> it.sku in purchase.skus }
|
val skuReceipt =
|
||||||
?: return@run Subscription.Status.Inactive
|
receipts?.firstOrNull { purchase -> it.productId in purchase.products }
|
||||||
|
?: return@run kotlin.run {
|
||||||
|
val hasTrialAvailable = bestOffer?.pricingPhases
|
||||||
|
?.pricingPhaseList
|
||||||
|
?.firstOrNull()
|
||||||
|
?.priceAmountMicros == 0L
|
||||||
|
Subscription.Status.Inactive(
|
||||||
|
hasTrialAvailable = hasTrialAvailable,
|
||||||
|
)
|
||||||
|
}
|
||||||
Subscription.Status.Active(
|
Subscription.Status.Active(
|
||||||
willRenew = skuReceipt.isAutoRenewing,
|
willRenew = skuReceipt.isAutoRenewing,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Subscription(
|
Subscription(
|
||||||
id = it.sku,
|
id = it.productId,
|
||||||
title = it.title,
|
title = it.name,
|
||||||
description = it.description,
|
description = it.description,
|
||||||
price = it.price,
|
price = finalPrice.formattedPrice,
|
||||||
status = status,
|
status = status,
|
||||||
purchase = { context ->
|
purchase = { context ->
|
||||||
val activity = context.context.closestActivityOrNull
|
val activity = context.context.closestActivityOrNull
|
||||||
?: return@Subscription
|
?: return@Subscription
|
||||||
|
|
||||||
|
val offerToken = bestOffer?.offerToken
|
||||||
|
?: baseOffer.offerToken
|
||||||
|
val list = listOf(
|
||||||
|
BillingFlowParams.ProductDetailsParams.newBuilder()
|
||||||
|
.setProductDetails(it)
|
||||||
|
.setOfferToken(offerToken)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
val params = BillingFlowParams.newBuilder()
|
val params = BillingFlowParams.newBuilder()
|
||||||
.run {
|
.run {
|
||||||
val existingPurchase = receipts
|
val existingPurchase = receipts
|
||||||
?.firstOrNull {
|
?.firstOrNull {
|
||||||
SkuListSubscription.intersect(it.skus)
|
SkuListSubscription.intersect(it.products)
|
||||||
.isNotEmpty()
|
.isNotEmpty()
|
||||||
}
|
}
|
||||||
if (existingPurchase != null && it.sku !in existingPurchase.skus) {
|
if (existingPurchase != null && it.productId !in existingPurchase.products) {
|
||||||
val params = BillingFlowParams.SubscriptionUpdateParams
|
val params = BillingFlowParams.SubscriptionUpdateParams
|
||||||
.newBuilder()
|
.newBuilder()
|
||||||
.setOldSkuPurchaseToken(existingPurchase.purchaseToken)
|
.setOldPurchaseToken(existingPurchase.purchaseToken)
|
||||||
.build()
|
.build()
|
||||||
setSubscriptionUpdateParams(params)
|
setSubscriptionUpdateParams(params)
|
||||||
} else {
|
} else {
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.setSkuDetails(it)
|
.setProductDetailsParamsList(list)
|
||||||
.build()
|
.build()
|
||||||
purchase(activity, params)
|
purchase(activity, params)
|
||||||
.crashlyticsTap()
|
.crashlyticsTap()
|
||||||
|
@ -123,29 +157,37 @@ class SubscriptionServiceAndroid(
|
||||||
override fun products(): Flow<List<Product>?> = combine(
|
override fun products(): Flow<List<Product>?> = combine(
|
||||||
getReceiptFlow()
|
getReceiptFlow()
|
||||||
.filter { it !is RichResult.Loading },
|
.filter { it !is RichResult.Loading },
|
||||||
getSkuDetailsFlow(SkuType.INAPP)
|
getProductDetailsFlow(ProductType.INAPP)
|
||||||
.filter { it !is RichResult.Loading },
|
.filter { it !is RichResult.Loading },
|
||||||
) { receiptsResult, skuDetailsResult ->
|
) { receiptsResult, skuDetailsResult ->
|
||||||
val receipts = receiptsResult.orNull()
|
val receipts = receiptsResult.orNull()
|
||||||
val skuDetails = skuDetailsResult.orNull()
|
val skuDetails = skuDetailsResult.orNull()
|
||||||
|
|
||||||
skuDetails?.map {
|
skuDetails?.mapNotNull {
|
||||||
val status = kotlin.run {
|
val status = kotlin.run {
|
||||||
receipts?.firstOrNull { purchase -> it.sku in purchase.skus }
|
receipts?.firstOrNull { purchase -> it.productId in purchase.products }
|
||||||
?: return@run Product.Status.Inactive
|
?: return@run Product.Status.Inactive
|
||||||
Product.Status.Active
|
Product.Status.Active
|
||||||
}
|
}
|
||||||
|
val finalPrice = it.oneTimePurchaseOfferDetails
|
||||||
|
?: return@mapNotNull null
|
||||||
Product(
|
Product(
|
||||||
id = it.sku,
|
id = it.productId,
|
||||||
title = it.title,
|
title = it.name,
|
||||||
description = it.description,
|
description = it.description,
|
||||||
price = it.price,
|
price = finalPrice.formattedPrice,
|
||||||
status = status,
|
status = status,
|
||||||
purchase = { context ->
|
purchase = { context ->
|
||||||
val activity = context.context.closestActivityOrNull
|
val activity = context.context.closestActivityOrNull
|
||||||
?: return@Product
|
?: return@Product
|
||||||
|
|
||||||
|
val list = listOf(
|
||||||
|
BillingFlowParams.ProductDetailsParams.newBuilder()
|
||||||
|
.setProductDetails(it)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
val params = BillingFlowParams.newBuilder()
|
val params = BillingFlowParams.newBuilder()
|
||||||
.setSkuDetails(it)
|
.setProductDetailsParamsList(list)
|
||||||
.build()
|
.build()
|
||||||
purchase(activity, params)
|
purchase(activity, params)
|
||||||
.crashlyticsTap()
|
.crashlyticsTap()
|
||||||
|
@ -180,10 +222,18 @@ class SubscriptionServiceAndroid(
|
||||||
.billingConnectionFlow
|
.billingConnectionFlow
|
||||||
.flatMapLatest { connection ->
|
.flatMapLatest { connection ->
|
||||||
val subscriptionsFlow = connection
|
val subscriptionsFlow = connection
|
||||||
.purchasesFlow(SkuType.SUBS)
|
.purchasesFlow(
|
||||||
|
QueryPurchasesParams.newBuilder()
|
||||||
|
.setProductType(ProductType.SUBS)
|
||||||
|
.build(),
|
||||||
|
)
|
||||||
.onEachAcknowledge(connection)
|
.onEachAcknowledge(connection)
|
||||||
val productsFlow = connection
|
val productsFlow = connection
|
||||||
.purchasesFlow(SkuType.INAPP)
|
.purchasesFlow(
|
||||||
|
QueryPurchasesParams.newBuilder()
|
||||||
|
.setProductType(ProductType.INAPP)
|
||||||
|
.build(),
|
||||||
|
)
|
||||||
.onEachAcknowledge(connection)
|
.onEachAcknowledge(connection)
|
||||||
combine(
|
combine(
|
||||||
subscriptionsFlow,
|
subscriptionsFlow,
|
||||||
|
@ -213,19 +263,24 @@ class SubscriptionServiceAndroid(
|
||||||
connection.acknowledgePurchase(acknowledgePurchaseParams)
|
connection.acknowledgePurchase(acknowledgePurchaseParams)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getSkuDetailsFlow(skuType: String) = billingManager
|
private fun getProductDetailsFlow(@ProductType productType: String) = billingManager
|
||||||
.billingConnectionFlow
|
.billingConnectionFlow
|
||||||
.flatMapLatest { connection ->
|
.flatMapLatest { connection ->
|
||||||
val skusList = when (skuType) {
|
val productIdList = when (productType) {
|
||||||
SkuType.SUBS -> SkuListSubscription
|
ProductType.SUBS -> SkuListSubscription
|
||||||
SkuType.INAPP -> SkuListProduct
|
ProductType.INAPP -> SkuListProduct
|
||||||
else -> error("Unknown SKU type!")
|
else -> error("Unknown SKU type!")
|
||||||
}
|
}
|
||||||
|
val productList = productIdList
|
||||||
val skuDetailsParams = SkuDetailsParams.newBuilder()
|
.map {
|
||||||
.setType(skuType)
|
QueryProductDetailsParams.Product.newBuilder()
|
||||||
.setSkusList(skusList)
|
.setProductId(it)
|
||||||
|
.setProductType(productType)
|
||||||
.build()
|
.build()
|
||||||
connection.skuDetailsFlow(skuDetailsParams)
|
}
|
||||||
|
val skuDetailsParams = QueryProductDetailsParams.newBuilder()
|
||||||
|
.setProductList(productList)
|
||||||
|
.build()
|
||||||
|
connection.productDetailsFlow(skuDetailsParams)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1077,6 +1077,7 @@
|
||||||
<string name="pref_item_premium_membership_failed_to_load_subscriptions">Failed to load a list of subscriptions</string>
|
<string name="pref_item_premium_membership_failed_to_load_subscriptions">Failed to load a list of subscriptions</string>
|
||||||
<string name="pref_item_premium_membership_failed_to_load_products">Failed to load a list of products</string>
|
<string name="pref_item_premium_membership_failed_to_load_products">Failed to load a list of products</string>
|
||||||
<string name="pref_item_premium_status_active">Active</string>
|
<string name="pref_item_premium_status_active">Active</string>
|
||||||
|
<string name="pref_item_premium_status_free_trial">Free trial</string>
|
||||||
<string name="pref_item_premium_status_will_not_renew">Will not renew</string>
|
<string name="pref_item_premium_status_will_not_renew">Will not renew</string>
|
||||||
<string name="pref_item_premium_manage_subscription_on_play_store_title">Manage on Play Store</string>
|
<string name="pref_item_premium_manage_subscription_on_play_store_title">Manage on Play Store</string>
|
||||||
<string name="pref_item_permission_post_notifications_title">Post notifications</string>
|
<string name="pref_item_permission_post_notifications_title">Post notifications</string>
|
||||||
|
|
|
@ -11,7 +11,9 @@ data class Subscription(
|
||||||
val purchase: (LeContext) -> Unit,
|
val purchase: (LeContext) -> Unit,
|
||||||
) {
|
) {
|
||||||
sealed interface Status {
|
sealed interface Status {
|
||||||
data object Inactive : Status
|
data class Inactive(
|
||||||
|
val hasTrialAvailable: Boolean,
|
||||||
|
) : Status
|
||||||
|
|
||||||
data class Active(
|
data class Active(
|
||||||
val willRenew: Boolean,
|
val willRenew: Boolean,
|
||||||
|
|
|
@ -13,6 +13,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
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.foundation.shape.CircleShape
|
||||||
|
@ -35,6 +36,7 @@ import com.artemchep.keyguard.common.model.Subscription
|
||||||
import com.artemchep.keyguard.common.model.fold
|
import com.artemchep.keyguard.common.model.fold
|
||||||
import com.artemchep.keyguard.common.usecase.GetProducts
|
import com.artemchep.keyguard.common.usecase.GetProducts
|
||||||
import com.artemchep.keyguard.common.usecase.GetSubscriptions
|
import com.artemchep.keyguard.common.usecase.GetSubscriptions
|
||||||
|
import com.artemchep.keyguard.feature.auth.common.TextFieldModel2
|
||||||
import com.artemchep.keyguard.feature.home.vault.component.Section
|
import com.artemchep.keyguard.feature.home.vault.component.Section
|
||||||
import com.artemchep.keyguard.feature.onboarding.OnboardingCard
|
import com.artemchep.keyguard.feature.onboarding.OnboardingCard
|
||||||
import com.artemchep.keyguard.feature.onboarding.onboardingItemsPremium
|
import com.artemchep.keyguard.feature.onboarding.onboardingItemsPremium
|
||||||
|
@ -47,6 +49,7 @@ import com.artemchep.keyguard.ui.ExpandedIfNotEmpty
|
||||||
import com.artemchep.keyguard.ui.FlatItemLayout
|
import com.artemchep.keyguard.ui.FlatItemLayout
|
||||||
import com.artemchep.keyguard.ui.FlatItemTextContent
|
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.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.icons.ChevronIcon
|
||||||
|
@ -268,6 +271,18 @@ private fun SettingSubscriptionItem(
|
||||||
},
|
},
|
||||||
elevation = 2.dp,
|
elevation = 2.dp,
|
||||||
trailing = {
|
trailing = {
|
||||||
|
val status = subscription.status
|
||||||
|
if (status is Subscription.Status.Inactive && status.hasTrialAvailable) {
|
||||||
|
FlatTextFieldBadge(
|
||||||
|
type = TextFieldModel2.Vl.Type.INFO,
|
||||||
|
text = stringResource(Res.string.pref_item_premium_status_free_trial),
|
||||||
|
)
|
||||||
|
Spacer(
|
||||||
|
modifier = Modifier
|
||||||
|
.width(16.dp),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
ChevronIcon()
|
ChevronIcon()
|
||||||
},
|
},
|
||||||
content = {
|
content = {
|
||||||
|
|
|
@ -13,7 +13,7 @@ appVersionName = "1.3.1"
|
||||||
appVersionCode = "6"
|
appVersionCode = "6"
|
||||||
# https://github.com/google/accompanist
|
# https://github.com/google/accompanist
|
||||||
accompanist = "0.34.0"
|
accompanist = "0.34.0"
|
||||||
androidBillingClient = "6.2.1"
|
androidBillingClient = "7.0.0"
|
||||||
# https://mvnrepository.com/artifact/com.android.tools/desugar_jdk_libs
|
# https://mvnrepository.com/artifact/com.android.tools/desugar_jdk_libs
|
||||||
androidDesugar = "2.0.4"
|
androidDesugar = "2.0.4"
|
||||||
# https://mvnrepository.com/artifact/com.android.tools.build/gradle?repo=google
|
# https://mvnrepository.com/artifact/com.android.tools.build/gradle?repo=google
|
||||||
|
|
Loading…
Reference in New Issue