Update backups bottom sheet data handling.

This commit is contained in:
Alex Hart
2024-11-06 09:31:20 -04:00
committed by Greyson Parrelli
parent 3901c52e45
commit f14f7f7478
16 changed files with 163 additions and 114 deletions

View File

@@ -125,6 +125,11 @@ object BackupRepository {
}
}
@JvmStatic
fun skipMediaRestore() {
// TODO [backups] -- Clear the error as necessary
}
/**
* Whether the yellow dot should be displayed on the conversation list avatar.
*/

View File

@@ -6,7 +6,7 @@
package org.thoughtcrime.securesms.backup.v2.ui
import android.os.Parcelable
import androidx.annotation.StringRes
import androidx.appcompat.app.AlertDialog
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
@@ -37,6 +37,7 @@ 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 androidx.core.content.ContextCompat
import androidx.core.os.BundleCompat
import androidx.core.os.bundleOf
import com.google.android.material.dialog.MaterialAlertDialogBuilder
@@ -46,6 +47,7 @@ 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.BackupRepository
import org.thoughtcrime.securesms.billing.launchManageBackupsSubscription
import org.thoughtcrime.securesms.components.settings.app.AppSettingsActivity
import org.thoughtcrime.securesms.compose.ComposeBottomSheetDialogFragment
@@ -63,6 +65,7 @@ class BackupAlertBottomSheet : ComposeBottomSheetDialogFragment() {
companion object {
private const val ARG_ALERT = "alert"
@JvmStatic
fun create(backupAlert: BackupAlert): BackupAlertBottomSheet {
return BackupAlertBottomSheet().apply {
arguments = bundleOf(ARG_ALERT to backupAlert)
@@ -94,17 +97,17 @@ class BackupAlertBottomSheet : ComposeBottomSheetDialogFragment() {
@Stable
private fun performPrimaryAction() {
when (backupAlert) {
BackupAlert.COULD_NOT_COMPLETE_BACKUP -> {
BackupAlert.CouldNotCompleteBackup -> {
BackupMessagesJob.enqueue()
startActivity(AppSettingsActivity.remoteBackups(requireContext()))
}
BackupAlert.PAYMENT_PROCESSING -> launchManageBackupsSubscription()
BackupAlert.MEDIA_BACKUPS_ARE_OFF, BackupAlert.MEDIA_WILL_BE_DELETED_TODAY -> {
BackupAlert.FailedToRenew -> launchManageBackupsSubscription()
BackupAlert.MediaBackupsAreOff, BackupAlert.MediaWillBeDeletedToday -> {
performFullMediaDownload()
}
BackupAlert.DISK_FULL -> Unit
is BackupAlert.DiskFull -> Unit
}
dismissAllowingStateLoss()
@@ -113,20 +116,22 @@ class BackupAlertBottomSheet : ComposeBottomSheetDialogFragment() {
@Stable
private fun performSecondaryAction() {
when (backupAlert) {
BackupAlert.COULD_NOT_COMPLETE_BACKUP -> {
BackupAlert.CouldNotCompleteBackup -> {
// TODO [backups] - Dismiss and notify later
}
BackupAlert.PAYMENT_PROCESSING -> Unit
BackupAlert.MEDIA_BACKUPS_ARE_OFF -> {
BackupAlert.FailedToRenew -> Unit
BackupAlert.MediaBackupsAreOff -> {
// TODO [backups] - Silence and remind on last day
}
BackupAlert.MEDIA_WILL_BE_DELETED_TODAY -> {
BackupAlert.MediaWillBeDeletedToday -> {
displayLastChanceDialog()
}
BackupAlert.DISK_FULL -> Unit
is BackupAlert.DiskFull -> {
displaySkipRestoreDialog()
}
}
dismissAllowingStateLoss()
@@ -143,6 +148,23 @@ class BackupAlertBottomSheet : ComposeBottomSheetDialogFragment() {
.show()
}
private fun displaySkipRestoreDialog() {
MaterialAlertDialogBuilder(requireContext())
.setTitle((R.string.BackupAlertBottomSheet__skip_restore_question))
.setMessage(R.string.BackupAlertBottomSheet__if_you_skip_restore)
.setPositiveButton(R.string.BackupAlertBottomSheet__skip) { _, _ ->
BackupRepository.skipMediaRestore()
}
.setNegativeButton(android.R.string.cancel, null)
.create()
.apply {
setOnShowListener {
getButton(AlertDialog.BUTTON_POSITIVE).setTextColor(ContextCompat.getColor(requireContext(), R.color.signal_colorError))
}
}
.show()
}
private fun performFullMediaDownload() {
// TODO [backups] -- We need to force this to download everything
AppDependencies.jobManager.add(BackupRestoreMediaJob())
@@ -167,7 +189,7 @@ private fun BackupAlertSheetContent(
Spacer(modifier = Modifier.size(26.dp))
when (backupAlert) {
BackupAlert.PAYMENT_PROCESSING, BackupAlert.MEDIA_BACKUPS_ARE_OFF -> {
BackupAlert.FailedToRenew, BackupAlert.MediaBackupsAreOff -> {
Box {
Image(
painter = painterResource(id = R.drawable.image_signal_backups),
@@ -200,24 +222,21 @@ private fun BackupAlertSheetContent(
}
Text(
text = stringResource(id = rememberTitleResource(backupAlert = backupAlert)),
text = titleString(backupAlert = backupAlert),
style = MaterialTheme.typography.titleLarge,
textAlign = TextAlign.Center,
modifier = Modifier.padding(top = 16.dp, bottom = 6.dp)
)
when (backupAlert) {
BackupAlert.COULD_NOT_COMPLETE_BACKUP -> CouldNotCompleteBackup(
BackupAlert.CouldNotCompleteBackup -> CouldNotCompleteBackup(
daysSinceLastBackup = 7 // TODO [backups]
)
BackupAlert.PAYMENT_PROCESSING -> PaymentProcessingBody()
BackupAlert.MEDIA_BACKUPS_ARE_OFF -> MediaBackupsAreOffBody(30) // TODO [backups] -- Get this value from backend
BackupAlert.MEDIA_WILL_BE_DELETED_TODAY -> MediaWillBeDeletedTodayBody()
BackupAlert.DISK_FULL -> DiskFullBody(
requiredSpace = "12 GB", // TODO [backups] Where does this value come from?
daysUntilDeletion = 30 // TODO [backups] Where does this value come from?
)
BackupAlert.FailedToRenew -> PaymentProcessingBody()
BackupAlert.MediaBackupsAreOff -> MediaBackupsAreOffBody(30) // TODO [backups] -- Get this value from backend
BackupAlert.MediaWillBeDeletedToday -> MediaWillBeDeletedTodayBody()
is BackupAlert.DiskFull -> DiskFullBody(requiredSpace = backupAlert.requiredSpace)
}
val secondaryActionResource = rememberSecondaryActionResource(backupAlert = backupAlert)
@@ -299,19 +318,16 @@ private fun MediaWillBeDeletedTodayBody() {
}
@Composable
private fun DiskFullBody(
requiredSpace: String,
daysUntilDeletion: Long
) {
private fun DiskFullBody(requiredSpace: String) {
Text(
text = stringResource(id = R.string.BackupAlertBottomSheet__your_device_does_not_have_enough_free_space, requiredSpace),
text = stringResource(id = R.string.BackupAlertBottomSheet__to_finish_downloading_your_signal_backup, requiredSpace),
textAlign = TextAlign.Center,
color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(bottom = 24.dp)
)
Text(
text = pluralStringResource(id = R.plurals.BackupAlertBottomSheet__if_you_choose_skip, daysUntilDeletion.toInt(), daysUntilDeletion), // TODO [backups] Learn More link
text = stringResource(R.string.BackupAlertBottomSheet__to_free_up_space_offload),
textAlign = TextAlign.Center,
color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(bottom = 36.dp)
@@ -322,24 +338,21 @@ private fun DiskFullBody(
private fun rememberBackupsIconColors(backupAlert: BackupAlert): BackupsIconColors {
return remember(backupAlert) {
when (backupAlert) {
BackupAlert.PAYMENT_PROCESSING, BackupAlert.MEDIA_BACKUPS_ARE_OFF -> error("Not icon-based options.")
BackupAlert.COULD_NOT_COMPLETE_BACKUP, BackupAlert.DISK_FULL -> BackupsIconColors.Warning
BackupAlert.MEDIA_WILL_BE_DELETED_TODAY -> BackupsIconColors.Error
BackupAlert.FailedToRenew, BackupAlert.MediaBackupsAreOff -> error("Not icon-based options.")
BackupAlert.CouldNotCompleteBackup, is BackupAlert.DiskFull -> BackupsIconColors.Warning
BackupAlert.MediaWillBeDeletedToday -> BackupsIconColors.Error
}
}
}
@Composable
@StringRes
private fun rememberTitleResource(backupAlert: BackupAlert): Int {
return remember(backupAlert) {
when (backupAlert) {
BackupAlert.COULD_NOT_COMPLETE_BACKUP -> R.string.BackupAlertBottomSheet__couldnt_complete_backup
BackupAlert.PAYMENT_PROCESSING -> R.string.BackupAlertBottomSheet__your_backups_subscription_failed_to_renew
BackupAlert.MEDIA_BACKUPS_ARE_OFF -> R.string.BackupAlertBottomSheet__your_backups_subscription_expired
BackupAlert.MEDIA_WILL_BE_DELETED_TODAY -> R.string.BackupAlertBottomSheet__your_media_will_be_deleted_today
BackupAlert.DISK_FULL -> R.string.BackupAlertBottomSheet__cant_complete_download
}
private fun titleString(backupAlert: BackupAlert): String {
return when (backupAlert) {
BackupAlert.CouldNotCompleteBackup -> stringResource(R.string.BackupAlertBottomSheet__couldnt_complete_backup)
BackupAlert.FailedToRenew -> stringResource(R.string.BackupAlertBottomSheet__your_backups_subscription_failed_to_renew)
BackupAlert.MediaBackupsAreOff -> stringResource(R.string.BackupAlertBottomSheet__your_backups_subscription_expired)
BackupAlert.MediaWillBeDeletedToday -> stringResource(R.string.BackupAlertBottomSheet__your_media_will_be_deleted_today)
is BackupAlert.DiskFull -> stringResource(R.string.BackupAlertBottomSheet__free_up_s_on_this_device, backupAlert.requiredSpace)
}
}
@@ -349,11 +362,11 @@ private fun primaryActionString(
pricePerMonth: String
): String {
return when (backupAlert) {
BackupAlert.COULD_NOT_COMPLETE_BACKUP -> stringResource(R.string.BackupAlertBottomSheet__back_up_now)
BackupAlert.PAYMENT_PROCESSING -> stringResource(R.string.BackupAlertBottomSheet__manage_subscription)
BackupAlert.MEDIA_BACKUPS_ARE_OFF -> stringResource(R.string.BackupAlertBottomSheet__subscribe_for_s_month, pricePerMonth)
BackupAlert.MEDIA_WILL_BE_DELETED_TODAY -> stringResource(R.string.BackupAlertBottomSheet__download_media_now)
BackupAlert.DISK_FULL -> stringResource(android.R.string.ok)
BackupAlert.CouldNotCompleteBackup -> stringResource(R.string.BackupAlertBottomSheet__back_up_now)
BackupAlert.FailedToRenew -> stringResource(R.string.BackupAlertBottomSheet__manage_subscription)
BackupAlert.MediaBackupsAreOff -> stringResource(R.string.BackupAlertBottomSheet__subscribe_for_s_month, pricePerMonth)
BackupAlert.MediaWillBeDeletedToday -> stringResource(R.string.BackupAlertBottomSheet__download_media_now)
is BackupAlert.DiskFull -> stringResource(R.string.BackupAlertBottomSheet__got_it)
}
}
@@ -361,11 +374,11 @@ private fun primaryActionString(
private fun rememberSecondaryActionResource(backupAlert: BackupAlert): Int {
return remember(backupAlert) {
when (backupAlert) {
BackupAlert.COULD_NOT_COMPLETE_BACKUP -> android.R.string.cancel // TODO [backups] -- Finalized copy
BackupAlert.PAYMENT_PROCESSING -> R.string.BackupAlertBottomSheet__not_now
BackupAlert.MEDIA_BACKUPS_ARE_OFF -> R.string.BackupAlertBottomSheet__not_now
BackupAlert.MEDIA_WILL_BE_DELETED_TODAY -> R.string.BackupAlertBottomSheet__dont_download_media
BackupAlert.DISK_FULL -> R.string.BackupAlertBottomSheet__skip
BackupAlert.CouldNotCompleteBackup -> R.string.BackupAlertBottomSheet__try_later
BackupAlert.FailedToRenew -> R.string.BackupAlertBottomSheet__not_now
BackupAlert.MediaBackupsAreOff -> R.string.BackupAlertBottomSheet__not_now
BackupAlert.MediaWillBeDeletedToday -> R.string.BackupAlertBottomSheet__dont_download_media
is BackupAlert.DiskFull -> R.string.BackupAlertBottomSheet__skip_restore
}
}
}
@@ -375,7 +388,7 @@ private fun rememberSecondaryActionResource(backupAlert: BackupAlert): Int {
private fun BackupAlertSheetContentPreviewGeneric() {
Previews.BottomSheetPreview {
BackupAlertSheetContent(
backupAlert = BackupAlert.COULD_NOT_COMPLETE_BACKUP
backupAlert = BackupAlert.CouldNotCompleteBackup
)
}
}
@@ -385,7 +398,7 @@ private fun BackupAlertSheetContentPreviewGeneric() {
private fun BackupAlertSheetContentPreviewPayment() {
Previews.BottomSheetPreview {
BackupAlertSheetContent(
backupAlert = BackupAlert.PAYMENT_PROCESSING
backupAlert = BackupAlert.FailedToRenew
)
}
}
@@ -395,7 +408,7 @@ private fun BackupAlertSheetContentPreviewPayment() {
private fun BackupAlertSheetContentPreviewMedia() {
Previews.BottomSheetPreview {
BackupAlertSheetContent(
backupAlert = BackupAlert.MEDIA_BACKUPS_ARE_OFF,
backupAlert = BackupAlert.MediaBackupsAreOff,
pricePerMonth = "$2.99"
)
}
@@ -406,7 +419,7 @@ private fun BackupAlertSheetContentPreviewMedia() {
private fun BackupAlertSheetContentPreviewDelete() {
Previews.BottomSheetPreview {
BackupAlertSheetContent(
backupAlert = BackupAlert.MEDIA_WILL_BE_DELETED_TODAY
backupAlert = BackupAlert.MediaWillBeDeletedToday
)
}
}
@@ -416,16 +429,28 @@ private fun BackupAlertSheetContentPreviewDelete() {
private fun BackupAlertSheetContentPreviewDiskFull() {
Previews.BottomSheetPreview {
BackupAlertSheetContent(
backupAlert = BackupAlert.DISK_FULL
backupAlert = BackupAlert.DiskFull(requiredSpace = "12GB")
)
}
}
/**
* All necessary information to display the sheet should be handed in through the specific alert.
*/
@Parcelize
enum class BackupAlert : Parcelable {
COULD_NOT_COMPLETE_BACKUP,
PAYMENT_PROCESSING,
MEDIA_BACKUPS_ARE_OFF,
MEDIA_WILL_BE_DELETED_TODAY,
DISK_FULL
sealed class BackupAlert : Parcelable {
data object CouldNotCompleteBackup : BackupAlert()
data object FailedToRenew : BackupAlert()
data object MediaBackupsAreOff : BackupAlert()
data object MediaWillBeDeletedToday : BackupAlert()
/**
* The disk is full. Contains a value representing the amount of space that must be freed.
*
*/
data class DiskFull(val requiredSpace: String) : BackupAlert()
}

View File

@@ -10,6 +10,7 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.coroutineScope
import androidx.lifecycle.repeatOnLifecycle
import kotlinx.coroutines.launch
import org.thoughtcrime.securesms.backup.v2.BackupRepository
/**
* Delegate that controls whether and which backup alert sheet is displayed.
@@ -19,12 +20,12 @@ object BackupAlertDelegate {
fun delegate(fragmentManager: FragmentManager, lifecycle: Lifecycle) {
lifecycle.coroutineScope.launch {
lifecycle.repeatOnLifecycle(Lifecycle.State.RESUMED) {
// TODO [backups]
// 1. Get unnotified backup upload failures
// 2. Get unnotified backup download failures
// 3. Get unnotified backup payment failures
if (BackupRepository.shouldDisplayBackupFailedSheet()) {
BackupAlertBottomSheet.create(BackupAlert.CouldNotCompleteBackup).show(fragmentManager, null)
}
// Decide which do display
// TODO [backups]
// Get unnotified backup download failures & display sheet
}
}
}

View File

@@ -57,7 +57,7 @@ private const val NONE = -1
@Composable
fun BackupStatusBanner(
data: BackupStatusData,
onSkipClick: () -> Unit = {},
onActionClick: (BackupStatusData) -> Unit = {},
onDismissClick: () -> Unit = {},
contentPadding: PaddingValues = PaddingValues(horizontal = 12.dp, vertical = 8.dp)
) {
@@ -119,7 +119,7 @@ fun BackupStatusBanner(
if (data.actionRes != NONE) {
Buttons.Small(
onClick = onSkipClick,
onClick = { onActionClick(data) },
modifier = Modifier.padding(start = 8.dp)
) {
Text(text = stringResource(id = data.actionRes))
@@ -250,7 +250,7 @@ sealed interface BackupStatusData {
get() = stringResource(R.string.BackupStatus__free_up_s_of_space_to_download_your_media, requiredSpace)
override val iconColors: BackupsIconColors = BackupsIconColors.Warning
override val actionRes: Int = R.string.registration_activity__skip
override val actionRes: Int = R.string.BackupStatus__details
}
/**