mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-21 17:29:32 +01:00
Add base and subclassed upgrade sheets.
This commit is contained in:
committed by
Greyson Parrelli
parent
7abe76f76a
commit
7cc425fa7b
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright 2024 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.billing.upgrade
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.defaultMinSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.dimensionResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import org.signal.core.ui.BottomSheets
|
||||
import org.signal.core.ui.Buttons
|
||||
import org.signal.core.ui.Previews
|
||||
import org.signal.core.ui.SignalPreview
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsType
|
||||
import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsTypeBlock
|
||||
import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsTypeIconColors
|
||||
import org.thoughtcrime.securesms.backup.v2.ui.subscription.testBackupTypes
|
||||
import org.thoughtcrime.securesms.payments.FiatMoneyUtil
|
||||
|
||||
/**
|
||||
* Sheet describing how users must upgrade to enable optimized storage.
|
||||
*/
|
||||
class UpgradeToEnableOptimizedStorageSheet : UpgradeToPaidTierBottomSheet() {
|
||||
|
||||
@Composable
|
||||
override fun UpgradeSheetContent(
|
||||
paidBackupType: MessageBackupsType.Paid,
|
||||
freeBackupType: MessageBackupsType.Free,
|
||||
isSubscribeEnabled: Boolean,
|
||||
onSubscribeClick: () -> Unit
|
||||
) {
|
||||
UpgradeToEnableOptimizedStorageSheetContent(
|
||||
messageBackupsType = paidBackupType,
|
||||
isSubscribeEnabled = isSubscribeEnabled,
|
||||
onSubscribeClick = onSubscribeClick,
|
||||
onCancelClick = {
|
||||
dismissAllowingStateLoss()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun UpgradeToEnableOptimizedStorageSheetContent(
|
||||
messageBackupsType: MessageBackupsType.Paid,
|
||||
isSubscribeEnabled: Boolean,
|
||||
onSubscribeClick: () -> Unit = {},
|
||||
onCancelClick: () -> Unit = {}
|
||||
) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
BottomSheets.Handle()
|
||||
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.image_signal_backups),
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.padding(top = 8.dp, bottom = 16.dp)
|
||||
.size(80.dp)
|
||||
)
|
||||
|
||||
Text(
|
||||
text = stringResource(id = R.string.UpgradeToEnableOptimizedStorageSheet__upgrade_to_enable_this_feature),
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = dimensionResource(id = R.dimen.core_ui__gutter))
|
||||
)
|
||||
|
||||
Text(
|
||||
text = stringResource(id = R.string.UpgradeToEnableOptimizedStorageSheet__storage_optimization_can_only_be_used),
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier
|
||||
.padding(top = 10.dp, bottom = 28.dp)
|
||||
.padding(horizontal = dimensionResource(id = R.dimen.core_ui__gutter))
|
||||
)
|
||||
|
||||
MessageBackupsTypeBlock(
|
||||
messageBackupsType = messageBackupsType,
|
||||
isCurrent = false,
|
||||
isSelected = false,
|
||||
onSelected = {},
|
||||
enabled = false,
|
||||
iconColors = MessageBackupsTypeIconColors.default().let {
|
||||
it.copy(iconColorNormal = it.iconColorSelected)
|
||||
},
|
||||
modifier = Modifier
|
||||
.padding(horizontal = dimensionResource(id = R.dimen.core_ui__gutter))
|
||||
.padding(bottom = 50.dp)
|
||||
)
|
||||
|
||||
Buttons.LargePrimary(
|
||||
enabled = isSubscribeEnabled,
|
||||
onClick = onSubscribeClick,
|
||||
modifier = Modifier
|
||||
.defaultMinSize(minWidth = 256.dp)
|
||||
.padding(horizontal = dimensionResource(id = R.dimen.core_ui__gutter))
|
||||
.padding(bottom = 8.dp)
|
||||
) {
|
||||
val resources = LocalContext.current.resources
|
||||
val formattedPrice = remember(messageBackupsType.pricePerMonth) {
|
||||
FiatMoneyUtil.format(resources, messageBackupsType.pricePerMonth, FiatMoneyUtil.formatOptions().trimZerosAfterDecimal())
|
||||
}
|
||||
|
||||
Text(
|
||||
text = stringResource(id = R.string.UpgradeToEnableOptimizedStorageSheet__subscribe_for_s_month, formattedPrice)
|
||||
)
|
||||
}
|
||||
|
||||
TextButton(
|
||||
enabled = isSubscribeEnabled,
|
||||
onClick = onCancelClick,
|
||||
modifier = Modifier
|
||||
.defaultMinSize(minWidth = 256.dp)
|
||||
.padding(horizontal = dimensionResource(id = R.dimen.core_ui__gutter))
|
||||
.padding(bottom = 16.dp)
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = android.R.string.cancel)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SignalPreview
|
||||
@Composable
|
||||
private fun UpgradeToEnableOptimizedStorageSheetContentPreview() {
|
||||
Previews.BottomSheetPreview {
|
||||
UpgradeToEnableOptimizedStorageSheetContent(
|
||||
messageBackupsType = testBackupTypes()[1] as MessageBackupsType.Paid,
|
||||
isSubscribeEnabled = true
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright 2024 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.billing.upgrade
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.fragment.app.setFragmentResult
|
||||
import kotlinx.coroutines.rx3.asFlowable
|
||||
import org.signal.core.ui.Dialogs
|
||||
import org.thoughtcrime.securesms.backup.v2.MessageBackupTier
|
||||
import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsFlowViewModel
|
||||
import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsStage
|
||||
import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsType
|
||||
import org.thoughtcrime.securesms.components.settings.app.subscription.donate.InAppPaymentCheckoutDelegate
|
||||
import org.thoughtcrime.securesms.compose.ComposeBottomSheetDialogFragment
|
||||
import org.thoughtcrime.securesms.database.InAppPaymentTable
|
||||
import org.thoughtcrime.securesms.dependencies.AppDependencies
|
||||
import org.thoughtcrime.securesms.util.viewModel
|
||||
|
||||
/**
|
||||
* BottomSheet that encapsulates the common logic for updating someone to paid tier.
|
||||
*/
|
||||
abstract class UpgradeToPaidTierBottomSheet : ComposeBottomSheetDialogFragment(), InAppPaymentCheckoutDelegate.ErrorHandlerCallback {
|
||||
|
||||
companion object {
|
||||
const val RESULT_KEY = "UpgradeToPaidTierBottomSheet.RESULT_KEY"
|
||||
}
|
||||
|
||||
private val viewModel: MessageBackupsFlowViewModel by viewModel {
|
||||
MessageBackupsFlowViewModel(
|
||||
initialTierSelection = MessageBackupTier.PAID,
|
||||
startScreen = MessageBackupsStage.TYPE_SELECTION
|
||||
)
|
||||
}
|
||||
|
||||
private val errorHandler = InAppPaymentCheckoutDelegate.ErrorHandler()
|
||||
|
||||
override val peekHeightPercentage: Float = 1f
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
errorHandler.attach(
|
||||
fragment = this,
|
||||
errorHandlerCallback = this,
|
||||
inAppPaymentIdSource = viewModel.stateFlow.asFlowable()
|
||||
.filter { it.inAppPayment != null }
|
||||
.map { it.inAppPayment!!.id }
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun SheetContent() {
|
||||
val state by viewModel.stateFlow.collectAsState()
|
||||
|
||||
val paidBackupType = state.availableBackupTypes.firstOrNull { it.tier == MessageBackupTier.PAID } as? MessageBackupsType.Paid
|
||||
val freeBackupType = state.availableBackupTypes.firstOrNull { it.tier == MessageBackupTier.FREE } as? MessageBackupsType.Free
|
||||
|
||||
if (paidBackupType != null && freeBackupType != null) {
|
||||
UpgradeSheetContent(
|
||||
paidBackupType = paidBackupType,
|
||||
freeBackupType = freeBackupType,
|
||||
isSubscribeEnabled = state.stage == MessageBackupsStage.TYPE_SELECTION,
|
||||
onSubscribeClick = viewModel::goToNextStage
|
||||
)
|
||||
}
|
||||
|
||||
when (state.stage) {
|
||||
MessageBackupsStage.CREATING_IN_APP_PAYMENT -> Dialogs.IndeterminateProgressDialog()
|
||||
MessageBackupsStage.PROCESS_PAYMENT -> Dialogs.IndeterminateProgressDialog()
|
||||
MessageBackupsStage.PROCESS_FREE -> Dialogs.IndeterminateProgressDialog()
|
||||
else -> Unit
|
||||
}
|
||||
|
||||
LaunchedEffect(state.stage) {
|
||||
if (state.stage == MessageBackupsStage.CHECKOUT_SHEET) {
|
||||
AppDependencies.billingApi.launchBillingFlow(requireActivity())
|
||||
}
|
||||
|
||||
if (state.stage == MessageBackupsStage.COMPLETED) {
|
||||
dismissAllowingStateLoss()
|
||||
setFragmentResult(RESULT_KEY, bundleOf(RESULT_KEY to true))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is responsible for displaying the normal upgrade sheet content.
|
||||
*/
|
||||
@Composable
|
||||
abstract fun UpgradeSheetContent(
|
||||
paidBackupType: MessageBackupsType.Paid,
|
||||
freeBackupType: MessageBackupsType.Free,
|
||||
isSubscribeEnabled: Boolean,
|
||||
onSubscribeClick: () -> Unit
|
||||
)
|
||||
|
||||
override fun onUserLaunchedAnExternalApplication() = error("Unsupported.")
|
||||
|
||||
override fun navigateToDonationPending(inAppPayment: InAppPaymentTable.InAppPayment) = error("Unsupported.")
|
||||
|
||||
override fun exitCheckoutFlow() {
|
||||
dismissAllowingStateLoss()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Copyright 2024 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.billing.upgrade
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.defaultMinSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.dimensionResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.pluralStringResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import org.signal.core.ui.BottomSheets
|
||||
import org.signal.core.ui.Buttons
|
||||
import org.signal.core.ui.Previews
|
||||
import org.signal.core.ui.SignalPreview
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsType
|
||||
import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsTypeBlock
|
||||
import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsTypeIconColors
|
||||
import org.thoughtcrime.securesms.backup.v2.ui.subscription.testBackupTypes
|
||||
import org.thoughtcrime.securesms.payments.FiatMoneyUtil
|
||||
|
||||
/**
|
||||
* Bottom sheet notifying user that the media they selected is no longer available. This
|
||||
* can occur when a user had a paid tier in the past and had storage optimization enabled,
|
||||
* but did not download their media within 30 days of canceling their subscription.
|
||||
*/
|
||||
class UpgradeToStartMediaBackupSheet : UpgradeToPaidTierBottomSheet() {
|
||||
@Composable
|
||||
override fun UpgradeSheetContent(
|
||||
paidBackupType: MessageBackupsType.Paid,
|
||||
freeBackupType: MessageBackupsType.Free,
|
||||
isSubscribeEnabled: Boolean,
|
||||
onSubscribeClick: () -> Unit
|
||||
) {
|
||||
UpgradeToStartMediaBackupSheetContent(
|
||||
paidBackupType = paidBackupType,
|
||||
freeBackupType = freeBackupType,
|
||||
isSubscribeEnabled = isSubscribeEnabled,
|
||||
onSubscribeClick = onSubscribeClick,
|
||||
onCancelClick = {
|
||||
dismissAllowingStateLoss()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun UpgradeToStartMediaBackupSheetContent(
|
||||
paidBackupType: MessageBackupsType.Paid,
|
||||
freeBackupType: MessageBackupsType.Free,
|
||||
isSubscribeEnabled: Boolean,
|
||||
onSubscribeClick: () -> Unit = {},
|
||||
onCancelClick: () -> Unit = {}
|
||||
) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = dimensionResource(R.dimen.core_ui__gutter))
|
||||
) {
|
||||
BottomSheets.Handle()
|
||||
|
||||
Image(
|
||||
painter = painterResource(R.drawable.image_signal_backups_media),
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.size(80.dp)
|
||||
)
|
||||
|
||||
Text(
|
||||
text = stringResource(R.string.UpgradeToStartMediaBackupSheet__this_media_is_no_longer_available),
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.padding(vertical = 16.dp)
|
||||
)
|
||||
|
||||
Text(
|
||||
text = pluralStringResource(R.plurals.UpgradeToStartMediaBackupSheet__your_current_signal_backup_plan_includes, freeBackupType.mediaRetentionDays, freeBackupType.mediaRetentionDays),
|
||||
textAlign = TextAlign.Center,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
|
||||
MessageBackupsTypeBlock(
|
||||
messageBackupsType = paidBackupType,
|
||||
isCurrent = false,
|
||||
isSelected = false,
|
||||
onSelected = {},
|
||||
enabled = false,
|
||||
modifier = Modifier.padding(top = 24.dp, bottom = 32.dp),
|
||||
iconColors = MessageBackupsTypeIconColors.default().let {
|
||||
it.copy(iconColorNormal = it.iconColorSelected)
|
||||
}
|
||||
)
|
||||
|
||||
Buttons.LargePrimary(
|
||||
enabled = isSubscribeEnabled,
|
||||
onClick = onSubscribeClick,
|
||||
modifier = Modifier
|
||||
.defaultMinSize(minWidth = 256.dp)
|
||||
.padding(horizontal = dimensionResource(id = R.dimen.core_ui__gutter))
|
||||
.padding(bottom = 8.dp)
|
||||
) {
|
||||
val resources = LocalContext.current.resources
|
||||
val formattedPrice = remember(paidBackupType.pricePerMonth) {
|
||||
FiatMoneyUtil.format(resources, paidBackupType.pricePerMonth, FiatMoneyUtil.formatOptions().trimZerosAfterDecimal())
|
||||
}
|
||||
|
||||
Text(
|
||||
text = stringResource(id = R.string.UpgradeToStartMediaBackupSheet__subscribe_for_s_month, formattedPrice)
|
||||
)
|
||||
}
|
||||
|
||||
TextButton(
|
||||
enabled = isSubscribeEnabled,
|
||||
onClick = onCancelClick,
|
||||
modifier = Modifier
|
||||
.defaultMinSize(minWidth = 256.dp)
|
||||
.padding(horizontal = dimensionResource(id = R.dimen.core_ui__gutter))
|
||||
.padding(bottom = 16.dp)
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = android.R.string.cancel)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SignalPreview
|
||||
@Composable
|
||||
private fun UpgradeToStartMediaBackupSheetContentPreview() {
|
||||
Previews.Preview {
|
||||
UpgradeToStartMediaBackupSheetContent(
|
||||
paidBackupType = testBackupTypes()[1] as MessageBackupsType.Paid,
|
||||
freeBackupType = testBackupTypes()[0] as MessageBackupsType.Free,
|
||||
isSubscribeEnabled = true
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user