diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/CreateBackupBottomSheet.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/CreateBackupBottomSheet.kt index b802df6bf0..6531d04a50 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/CreateBackupBottomSheet.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/CreateBackupBottomSheet.kt @@ -5,6 +5,7 @@ package org.thoughtcrime.securesms.backup.v2.ui +import android.content.DialogInterface import androidx.compose.foundation.background import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -24,26 +25,38 @@ 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 androidx.core.os.bundleOf +import androidx.fragment.app.setFragmentResult import org.signal.core.ui.BottomSheets import org.signal.core.ui.Buttons import org.signal.core.ui.Icons import org.signal.core.ui.Previews import org.signal.core.ui.SignalPreview import org.thoughtcrime.securesms.R -import org.thoughtcrime.securesms.components.settings.app.AppSettingsActivity import org.thoughtcrime.securesms.compose.ComposeBottomSheetDialogFragment import org.thoughtcrime.securesms.jobs.BackupMessagesJob /** * Bottom sheet allowing the user to immediately start a backup or delay. + * + * If the result key is true, then the user has enqueued a backup and should be directed to the + * remote backup settings screen. */ class CreateBackupBottomSheet : ComposeBottomSheetDialogFragment() { + + companion object { + const val REQUEST_KEY = "CreateBackupBottomSheet" + } + + private var isResultSet = false + @Composable override fun SheetContent() { CreateBackupBottomSheetContent( onBackupNowClick = { BackupMessagesJob.enqueue() - startActivity(AppSettingsActivity.remoteBackups(requireContext())) + setFragmentResult(REQUEST_KEY, bundleOf(REQUEST_KEY to Result.BACKUP_STARTED)) + isResultSet = true dismissAllowingStateLoss() }, onBackupLaterClick = { @@ -51,6 +64,19 @@ class CreateBackupBottomSheet : ComposeBottomSheetDialogFragment() { } ) } + + enum class Result { + BACKUP_STARTED, + BACKUP_DELAYED + } + + override fun onDismiss(dialog: DialogInterface) { + if (!isResultSet) { + setFragmentResult(REQUEST_KEY, bundleOf(REQUEST_KEY to Result.BACKUP_DELAYED)) + } + + super.onDismiss(dialog) + } } @Composable diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsFlowFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsFlowFragment.kt index f6e71eb806..62b0e0bc45 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsFlowFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsFlowFragment.kt @@ -187,6 +187,12 @@ class MessageBackupsFlowFragment : ComposeFragment(), InAppPaymentCheckoutDelega return@LaunchedEffect } + if (state.screen == MessageBackupsScreen.PROCESS_FREE) { + checkoutDelegate.setActivityResult(InAppPaymentProcessorAction.UPDATE_SUBSCRIPTION, InAppPaymentType.RECURRING_BACKUP) + viewModel.goToNextScreen() + return@LaunchedEffect + } + val routeScreen = MessageBackupsScreen.valueOf(route) if (routeScreen.isAfter(state.screen)) { navController.popBackStack() diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsFlowViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsFlowViewModel.kt index ef1ed84d13..3e5ef0e4a2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsFlowViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsFlowViewModel.kt @@ -76,6 +76,7 @@ class MessageBackupsFlowViewModel : ViewModel() { MessageBackupsScreen.CANCELLATION_DIALOG -> it.copy(screen = MessageBackupsScreen.PROCESS_CANCELLATION) MessageBackupsScreen.PROCESS_PAYMENT -> it.copy(screen = MessageBackupsScreen.COMPLETED) MessageBackupsScreen.PROCESS_CANCELLATION -> it.copy(screen = MessageBackupsScreen.COMPLETED) + MessageBackupsScreen.PROCESS_FREE -> it.copy(screen = MessageBackupsScreen.COMPLETED) MessageBackupsScreen.COMPLETED -> error("Unsupported state transition from terminal state COMPLETED") } } @@ -95,6 +96,7 @@ class MessageBackupsFlowViewModel : ViewModel() { MessageBackupsScreen.CREATING_IN_APP_PAYMENT -> MessageBackupsScreen.TYPE_SELECTION MessageBackupsScreen.PROCESS_PAYMENT -> MessageBackupsScreen.TYPE_SELECTION MessageBackupsScreen.PROCESS_CANCELLATION -> MessageBackupsScreen.TYPE_SELECTION + MessageBackupsScreen.PROCESS_FREE -> MessageBackupsScreen.TYPE_SELECTION MessageBackupsScreen.CANCELLATION_DIALOG -> MessageBackupsScreen.TYPE_SELECTION MessageBackupsScreen.COMPLETED -> error("Unsupported state transition from terminal state COMPLETED") } @@ -170,8 +172,7 @@ class MessageBackupsFlowViewModel : ViewModel() { SignalStore.backup.areBackupsEnabled = true SignalStore.backup.backupTier = MessageBackupTier.FREE - // TODO [message-backups] -- Trigger backup now? - state.copy(screen = MessageBackupsScreen.COMPLETED) + state.copy(screen = MessageBackupsScreen.PROCESS_FREE) } } MessageBackupTier.PAID -> state.copy(screen = MessageBackupsScreen.CHECKOUT_SHEET) diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsScreen.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsScreen.kt index eb9fb847c8..0a8ec4cb84 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsScreen.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ui/subscription/MessageBackupsScreen.kt @@ -15,6 +15,7 @@ enum class MessageBackupsScreen { CREATING_IN_APP_PAYMENT, PROCESS_PAYMENT, PROCESS_CANCELLATION, + PROCESS_FREE, COMPLETED; fun isAfter(other: MessageBackupsScreen): Boolean = ordinal > other.ordinal diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/AppSettingsActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/AppSettingsActivity.kt index 2a292ecbfe..bc624d3fe2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/AppSettingsActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/AppSettingsActivity.kt @@ -55,8 +55,8 @@ class AppSettingsActivity : DSLSettingsActivity(), InAppPaymentComponent { StartLocation.PROXY -> AppSettingsFragmentDirections.actionDirectToEditProxyFragment() StartLocation.NOTIFICATIONS -> AppSettingsFragmentDirections.actionDirectToNotificationsSettingsFragment() StartLocation.CHANGE_NUMBER -> AppSettingsFragmentDirections.actionDirectToChangeNumberFragment() - StartLocation.SUBSCRIPTIONS -> AppSettingsFragmentDirections.actionDirectToCheckout(InAppPaymentType.RECURRING_DONATION) - StartLocation.BOOST -> AppSettingsFragmentDirections.actionDirectToCheckout(InAppPaymentType.ONE_TIME_DONATION) + StartLocation.SUBSCRIPTIONS -> AppSettingsFragmentDirections.actionDirectToManageDonations().setDirectToCheckoutType(InAppPaymentType.RECURRING_DONATION) + StartLocation.BOOST -> AppSettingsFragmentDirections.actionDirectToManageDonations().setDirectToCheckoutType(InAppPaymentType.ONE_TIME_DONATION) StartLocation.MANAGE_SUBSCRIPTIONS -> AppSettingsFragmentDirections.actionDirectToManageDonations() StartLocation.NOTIFICATION_PROFILES -> AppSettingsFragmentDirections.actionDirectToNotificationProfiles() StartLocation.CREATE_NOTIFICATION_PROFILE -> AppSettingsFragmentDirections.actionDirectToCreateNotificationProfiles() diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/ChatsSettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/ChatsSettingsFragment.kt index df9d70f8b6..80094ad07c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/ChatsSettingsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/ChatsSettingsFragment.kt @@ -1,13 +1,15 @@ package org.thoughtcrime.securesms.components.settings.app.chats +import androidx.activity.result.ActivityResultLauncher import androidx.lifecycle.ViewModelProvider import androidx.navigation.Navigation +import androidx.navigation.fragment.findNavController import org.signal.donations.InAppPaymentType import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.components.settings.DSLConfiguration import org.thoughtcrime.securesms.components.settings.DSLSettingsFragment import org.thoughtcrime.securesms.components.settings.DSLSettingsText -import org.thoughtcrime.securesms.components.settings.app.subscription.donate.CheckoutFlowActivity +import org.thoughtcrime.securesms.components.settings.app.subscription.InAppPaymentCheckoutLauncher.createBackupsCheckoutLauncher import org.thoughtcrime.securesms.components.settings.configure import org.thoughtcrime.securesms.util.RemoteConfig import org.thoughtcrime.securesms.util.adapter.mapping.MappingAdapter @@ -16,6 +18,7 @@ import org.thoughtcrime.securesms.util.navigation.safeNavigate class ChatsSettingsFragment : DSLSettingsFragment(R.string.preferences_chats__chats) { private lateinit var viewModel: ChatsSettingsViewModel + private lateinit var checkoutLauncher: ActivityResultLauncher override fun onResume() { super.onResume() @@ -24,6 +27,10 @@ class ChatsSettingsFragment : DSLSettingsFragment(R.string.preferences_chats__ch @Suppress("ReplaceGetOrSet") override fun bindAdapter(adapter: MappingAdapter) { + checkoutLauncher = createBackupsCheckoutLauncher { + findNavController().safeNavigate(ChatsSettingsFragmentDirections.actionChatsSettingsFragmentToRemoteBackupsSettingsFragment().setBackupLaterSelected(it)) + } + viewModel = ViewModelProvider(this).get(ChatsSettingsViewModel::class.java) viewModel.state.observe(viewLifecycleOwner) { @@ -92,7 +99,7 @@ class ChatsSettingsFragment : DSLSettingsFragment(R.string.preferences_chats__ch if (state.canAccessRemoteBackupsSettings) { Navigation.findNavController(requireView()).safeNavigate(R.id.action_chatsSettingsFragment_to_remoteBackupsSettingsFragment) } else { - startActivity(CheckoutFlowActivity.createIntent(requireContext(), InAppPaymentType.RECURRING_BACKUP)) + checkoutLauncher.launch(InAppPaymentType.RECURRING_BACKUP) } } ) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/RemoteBackupsSettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/RemoteBackupsSettingsFragment.kt index 08e7347845..9cb0716712 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/RemoteBackupsSettingsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/RemoteBackupsSettingsFragment.kt @@ -7,6 +7,7 @@ package org.thoughtcrime.securesms.components.settings.app.chats.backups import android.os.Bundle import android.view.View +import androidx.activity.result.ActivityResultLauncher import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box @@ -44,7 +45,9 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.compose.ui.window.DialogProperties +import androidx.fragment.app.setFragmentResultListener import androidx.navigation.fragment.findNavController +import androidx.navigation.fragment.navArgs import kotlinx.collections.immutable.persistentListOf import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe @@ -65,7 +68,8 @@ import org.thoughtcrime.securesms.backup.v2.BackupFrequency import org.thoughtcrime.securesms.backup.v2.BackupV2Event import org.thoughtcrime.securesms.backup.v2.MessageBackupTier import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsType -import org.thoughtcrime.securesms.components.settings.app.subscription.donate.CheckoutFlowActivity +import org.thoughtcrime.securesms.components.settings.app.chats.backups.type.BackupsTypeSettingsFragment +import org.thoughtcrime.securesms.components.settings.app.subscription.InAppPaymentCheckoutLauncher.createBackupsCheckoutLauncher import org.thoughtcrime.securesms.compose.ComposeFragment import org.thoughtcrime.securesms.conversation.v2.registerForLifecycle import org.thoughtcrime.securesms.payments.FiatMoneyUtil @@ -86,6 +90,10 @@ class RemoteBackupsSettingsFragment : ComposeFragment() { RemoteBackupsSettingsViewModel() } + private val args: RemoteBackupsSettingsFragmentArgs by navArgs() + + private lateinit var checkoutLauncher: ActivityResultLauncher + @Composable override fun FragmentContent() { val state by viewModel.state.collectAsState() @@ -111,7 +119,7 @@ class RemoteBackupsSettingsFragment : ComposeFragment() { } override fun onEnableBackupsClick() { - startActivity(CheckoutFlowActivity.createIntent(requireContext(), InAppPaymentType.RECURRING_BACKUP)) + checkoutLauncher.launch(InAppPaymentType.RECURRING_BACKUP) } override fun onBackUpUsingCellularClick(canUseCellular: Boolean) { @@ -163,6 +171,22 @@ class RemoteBackupsSettingsFragment : ComposeFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) EventBus.getDefault().registerForLifecycle(subscriber = this, lifecycleOwner = viewLifecycleOwner) + checkoutLauncher = createBackupsCheckoutLauncher { backUpLater -> + if (backUpLater) { + viewModel.requestSnackbar(RemoteBackupsSettingsState.Snackbar.BACKUP_WILL_BE_CREATED_OVERNIGHT) + } + } + + setFragmentResultListener(BackupsTypeSettingsFragment.REQUEST_KEY) { _, bundle -> + val backUpLater = bundle.getBoolean(BackupsTypeSettingsFragment.REQUEST_KEY) + if (backUpLater) { + viewModel.requestSnackbar(RemoteBackupsSettingsState.Snackbar.BACKUP_WILL_BE_CREATED_OVERNIGHT) + } + } + + if (savedInstanceState == null && args.backupLaterSelected) { + viewModel.requestSnackbar(RemoteBackupsSettingsState.Snackbar.BACKUP_WILL_BE_CREATED_OVERNIGHT) + } } override fun onResume() { @@ -346,6 +370,7 @@ private fun RemoteBackupsSettingsContent( RemoteBackupsSettingsState.Snackbar.BACKUP_TYPE_CHANGED_AND_SUBSCRIPTION_CANCELLED -> R.string.RemoteBackupsSettingsFragment__backup_type_changed_and_subcription_deleted RemoteBackupsSettingsState.Snackbar.SUBSCRIPTION_CANCELLED -> R.string.RemoteBackupsSettingsFragment__subscription_cancelled RemoteBackupsSettingsState.Snackbar.DOWNLOAD_COMPLETE -> R.string.RemoteBackupsSettingsFragment__download_complete + RemoteBackupsSettingsState.Snackbar.BACKUP_WILL_BE_CREATED_OVERNIGHT -> R.string.RemoteBackupsSettingsFragment__backup_will_be_created_overnight } } @@ -359,9 +384,9 @@ private fun RemoteBackupsSettingsContent( else -> { snackbarHostState.showSnackbar(snackbarText) + contentCallbacks.onSnackbarDismissed() } } - contentCallbacks.onSnackbarDismissed() } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/RemoteBackupsSettingsState.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/RemoteBackupsSettingsState.kt index f037d8a08d..2165265049 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/RemoteBackupsSettingsState.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/RemoteBackupsSettingsState.kt @@ -32,6 +32,7 @@ data class RemoteBackupsSettingsState( BACKUP_DELETED_AND_TURNED_OFF, BACKUP_TYPE_CHANGED_AND_SUBSCRIPTION_CANCELLED, SUBSCRIPTION_CANCELLED, - DOWNLOAD_COMPLETE + DOWNLOAD_COMPLETE, + BACKUP_WILL_BE_CREATED_OVERNIGHT } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/type/BackupsTypeSettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/type/BackupsTypeSettingsFragment.kt index 86b2ad7884..8b0ef8a528 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/type/BackupsTypeSettingsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/chats/backups/type/BackupsTypeSettingsFragment.kt @@ -5,6 +5,9 @@ package org.thoughtcrime.securesms.components.settings.app.chats.backups.type +import android.os.Bundle +import android.view.View +import androidx.activity.result.ActivityResultLauncher import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn @@ -18,6 +21,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.core.os.bundleOf +import androidx.fragment.app.setFragmentResult import androidx.navigation.fragment.findNavController import kotlinx.collections.immutable.persistentListOf import org.signal.core.ui.Previews @@ -30,7 +35,7 @@ import org.signal.donations.PaymentSourceType import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.backup.v2.MessageBackupTier import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsType -import org.thoughtcrime.securesms.components.settings.app.subscription.donate.CheckoutFlowActivity +import org.thoughtcrime.securesms.components.settings.app.subscription.InAppPaymentCheckoutLauncher.createBackupsCheckoutLauncher import org.thoughtcrime.securesms.compose.ComposeFragment import org.thoughtcrime.securesms.payments.FiatMoneyUtil import org.thoughtcrime.securesms.util.DateUtils @@ -45,10 +50,24 @@ import java.util.Locale */ class BackupsTypeSettingsFragment : ComposeFragment() { + companion object { + const val REQUEST_KEY = "BackupsTypeSettingsFragment__result" + } + private val viewModel: BackupsTypeSettingsViewModel by viewModel { BackupsTypeSettingsViewModel() } + private lateinit var checkoutLauncher: ActivityResultLauncher + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + checkoutLauncher = createBackupsCheckoutLauncher { backUpLater -> + findNavController().popBackStack() + setFragmentResult(REQUEST_KEY, bundleOf(REQUEST_KEY to backUpLater)) + } + } + @Composable override fun FragmentContent() { val contentCallbacks = remember { @@ -73,7 +92,7 @@ class BackupsTypeSettingsFragment : ComposeFragment() { } override fun onChangeOrCancelSubscriptionClick() { - startActivity(CheckoutFlowActivity.createIntent(requireContext(), InAppPaymentType.RECURRING_BACKUP)) + checkoutLauncher.launch(InAppPaymentType.RECURRING_BACKUP) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/storage/UpgradeToEnableOptimizedStorageSheet.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/storage/UpgradeToEnableOptimizedStorageSheet.kt index f4db241721..6497fca202 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/storage/UpgradeToEnableOptimizedStorageSheet.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/storage/UpgradeToEnableOptimizedStorageSheet.kt @@ -5,6 +5,9 @@ package org.thoughtcrime.securesms.components.settings.app.storage +import android.os.Bundle +import android.view.View +import androidx.activity.result.ActivityResultLauncher import androidx.compose.foundation.background import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth @@ -35,7 +38,7 @@ import org.thoughtcrime.securesms.backup.v2.ui.BackupsIconColors 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.testBackupTypes -import org.thoughtcrime.securesms.components.settings.app.subscription.donate.CheckoutFlowActivity +import org.thoughtcrime.securesms.components.settings.app.subscription.InAppPaymentCheckoutLauncher.createBackupsCheckoutLauncher import org.thoughtcrime.securesms.compose.ComposeBottomSheetDialogFragment /** @@ -47,13 +50,20 @@ class UpgradeToEnableOptimizedStorageSheet : ComposeBottomSheetDialogFragment() private val viewModel: UpgradeToEnableOptimizedStorageViewModel by viewModels() + private lateinit var checkoutLauncher: ActivityResultLauncher + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + checkoutLauncher = createBackupsCheckoutLauncher() + } + @Composable override fun SheetContent() { val type by viewModel.messageBackupsType UpgradeToEnableOptimizedStorageSheetContent( messageBackupsType = type, onUpgradeNowClick = { - startActivity(CheckoutFlowActivity.createIntent(requireContext(), InAppPaymentType.RECURRING_BACKUP)) + checkoutLauncher.launch(InAppPaymentType.RECURRING_BACKUP) dismissAllowingStateLoss() }, onCancelClick = { diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/InAppPaymentCheckoutLauncher.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/InAppPaymentCheckoutLauncher.kt new file mode 100644 index 0000000000..a0c3b3bdef --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/InAppPaymentCheckoutLauncher.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.components.settings.app.subscription + +import androidx.activity.result.ActivityResultLauncher +import androidx.fragment.app.Fragment +import org.signal.core.util.getSerializableCompat +import org.signal.donations.InAppPaymentType +import org.thoughtcrime.securesms.backup.v2.ui.CreateBackupBottomSheet +import org.thoughtcrime.securesms.components.settings.app.subscription.donate.CheckoutFlowActivity +import org.thoughtcrime.securesms.components.settings.app.subscription.donate.InAppPaymentProcessorAction +import org.thoughtcrime.securesms.util.BottomSheetUtil + +object InAppPaymentCheckoutLauncher { + + fun Fragment.createBackupsCheckoutLauncher( + onCreateBackupBottomSheetResultListener: OnCreateBackupBottomSheetResultListener = {} as OnCreateBackupBottomSheetResultListener + ): ActivityResultLauncher { + childFragmentManager.setFragmentResultListener(CreateBackupBottomSheet.REQUEST_KEY, viewLifecycleOwner) { requestKey, bundle -> + if (requestKey == CreateBackupBottomSheet.REQUEST_KEY) { + val result = bundle.getSerializableCompat(CreateBackupBottomSheet.REQUEST_KEY, CreateBackupBottomSheet.Result::class.java) + onCreateBackupBottomSheetResultListener.onCreateBackupBottomSheetResult(result != CreateBackupBottomSheet.Result.BACKUP_STARTED) + } + } + + return registerForActivityResult(CheckoutFlowActivity.Contract()) { result -> + if (result?.action == InAppPaymentProcessorAction.PROCESS_NEW_IN_APP_PAYMENT || result?.action == InAppPaymentProcessorAction.UPDATE_SUBSCRIPTION) { + CreateBackupBottomSheet().show(childFragmentManager, BottomSheetUtil.STANDARD_BOTTOM_SHEET_FRAGMENT_TAG) + } + } + } + + fun interface OnCreateBackupBottomSheetResultListener { + fun onCreateBackupBottomSheetResult(backUpLater: Boolean) + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/RecurringInAppPaymentRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/RecurringInAppPaymentRepository.kt index 42f7d88310..4714f24207 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/RecurringInAppPaymentRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/RecurringInAppPaymentRepository.kt @@ -238,7 +238,7 @@ object RecurringInAppPaymentRepository { it.state == InAppPaymentTable.State.END }.take(1).map { if (it.data.error != null) { - Log.d(TAG, "Failure during redemption chain.", true) + Log.d(TAG, "Failure during redemption chain: ${it.data.error}", true) throw DonationError.genericBadgeRedemptionFailure(errorSource) } it diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/CheckoutFlowActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/CheckoutFlowActivity.kt index 91fa3b6857..f532da7c34 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/CheckoutFlowActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/CheckoutFlowActivity.kt @@ -7,10 +7,14 @@ package org.thoughtcrime.securesms.components.settings.app.subscription.donate import android.content.Context import android.content.Intent +import android.os.Parcelable +import androidx.activity.result.contract.ActivityResultContract import androidx.fragment.app.Fragment -import androidx.navigation.navArgs import io.reactivex.rxjava3.subjects.PublishSubject import io.reactivex.rxjava3.subjects.Subject +import kotlinx.parcelize.Parcelize +import org.signal.core.util.getParcelableExtraCompat +import org.signal.core.util.getSerializableCompat import org.signal.donations.InAppPaymentType import org.thoughtcrime.securesms.components.FragmentWrapperActivity import org.thoughtcrime.securesms.components.settings.app.subscription.InAppPaymentComponent @@ -22,20 +26,23 @@ import org.thoughtcrime.securesms.components.settings.app.subscription.StripeRep class CheckoutFlowActivity : FragmentWrapperActivity(), InAppPaymentComponent { companion object { + private const val ARG_IN_APP_PAYMENT_TYPE = "in_app_payment_type" + const val RESULT_DATA = "result_data" + fun createIntent(context: Context, inAppPaymentType: InAppPaymentType): Intent { - return Intent(context, CheckoutFlowActivity::class.java).putExtras( - CheckoutFlowActivityArgs.Builder(inAppPaymentType).build().toBundle() - ) + return Contract().createIntent(context, inAppPaymentType) } } override val stripeRepository: StripeRepository by lazy { StripeRepository(this) } override val googlePayResultPublisher: Subject = PublishSubject.create() - private val args by navArgs() + private val inAppPaymentType: InAppPaymentType by lazy { + intent.extras!!.getSerializableCompat(ARG_IN_APP_PAYMENT_TYPE, InAppPaymentType::class.java)!! + } override fun getFragment(): Fragment { - return CheckoutNavHostFragment.create(args.inAppPaymentType) + return CheckoutNavHostFragment.create(inAppPaymentType) } @Suppress("DEPRECATION") @@ -43,4 +50,21 @@ class CheckoutFlowActivity : FragmentWrapperActivity(), InAppPaymentComponent { super.onActivityResult(requestCode, resultCode, data) googlePayResultPublisher.onNext(InAppPaymentComponent.GooglePayResult(requestCode, resultCode, data)) } + + class Contract : ActivityResultContract() { + + override fun createIntent(context: Context, input: InAppPaymentType): Intent { + return Intent(context, CheckoutFlowActivity::class.java).putExtra(ARG_IN_APP_PAYMENT_TYPE, input) + } + + override fun parseResult(resultCode: Int, intent: Intent?): Result? { + return intent?.getParcelableExtraCompat(RESULT_DATA, Result::class.java) + } + } + + @Parcelize + data class Result( + val action: InAppPaymentProcessorAction, + val inAppPaymentType: InAppPaymentType + ) : Parcelable } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/InAppPaymentCheckoutDelegate.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/InAppPaymentCheckoutDelegate.kt index 69031921ac..65dc89f253 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/InAppPaymentCheckoutDelegate.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/donate/InAppPaymentCheckoutDelegate.kt @@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.components.settings.app.subscription.donate import android.app.Activity import android.content.Context import android.content.DialogInterface +import android.content.Intent import androidx.fragment.app.Fragment import androidx.fragment.app.setFragmentResultListener import androidx.fragment.app.viewModels @@ -114,6 +115,10 @@ class InAppPaymentCheckoutDelegate( } } + fun setActivityResult(action: InAppPaymentProcessorAction, inAppPaymentType: InAppPaymentType) { + fragment.requireActivity().setResult(Activity.RESULT_OK, Intent().putExtra(CheckoutFlowActivity.RESULT_DATA, CheckoutFlowActivity.Result(action, inAppPaymentType))) + } + private fun handleDonationProcessorActionResult(result: InAppPaymentProcessorActionResult) { when (result.status) { InAppPaymentProcessorActionResult.Status.SUCCESS -> handleSuccessfulDonationProcessorActionResult(result) @@ -124,10 +129,11 @@ class InAppPaymentCheckoutDelegate( } private fun handleSuccessfulDonationProcessorActionResult(result: InAppPaymentProcessorActionResult) { + setActivityResult(result.action, result.inAppPaymentType) + if (result.action == InAppPaymentProcessorAction.CANCEL_SUBSCRIPTION) { callback.onSubscriptionCancelled(result.inAppPaymentType) } else { - fragment.requireActivity().setResult(Activity.RESULT_OK) callback.onPaymentComplete(result.inAppPayment!!) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/manage/ManageDonationsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/manage/ManageDonationsFragment.kt index d610065a08..93d3415488 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/manage/ManageDonationsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/manage/ManageDonationsFragment.kt @@ -3,10 +3,12 @@ package org.thoughtcrime.securesms.components.settings.app.subscription.manage import android.os.Bundle import android.text.SpannableStringBuilder import android.view.View +import androidx.activity.result.ActivityResultLauncher import androidx.appcompat.widget.Toolbar import androidx.core.content.ContextCompat import androidx.fragment.app.viewModels import androidx.navigation.fragment.findNavController +import androidx.navigation.fragment.navArgs import com.google.android.material.dialog.MaterialAlertDialogBuilder import org.signal.core.util.dp import org.signal.core.util.money.FiatMoney @@ -21,6 +23,7 @@ import org.thoughtcrime.securesms.components.settings.DSLSettingsText import org.thoughtcrime.securesms.components.settings.app.AppSettingsActivity import org.thoughtcrime.securesms.components.settings.app.subscription.DonationSerializationHelper.toFiatMoney import org.thoughtcrime.securesms.components.settings.app.subscription.completed.InAppPaymentsBottomSheetDelegate +import org.thoughtcrime.securesms.components.settings.app.subscription.donate.CheckoutFlowActivity import org.thoughtcrime.securesms.components.settings.app.subscription.models.NetworkFailure import org.thoughtcrime.securesms.components.settings.configure import org.thoughtcrime.securesms.components.settings.models.IndeterminateLoadingCircle @@ -54,6 +57,9 @@ class ManageDonationsFragment : const val DONATE_TROUBLESHOOTING_URL = "https://support.signal.org/hc/articles/360031949872#fix" } + private val args: ManageDonationsFragmentArgs by navArgs() + private lateinit var launcher: ActivityResultLauncher + private val supportTechSummary: CharSequence by lazy { SpannableStringBuilder(SpanUtil.color(ContextCompat.getColor(requireContext(), R.color.signal_colorOnSurfaceVariant), requireContext().getString(R.string.DonateToSignalFragment__private_messaging))) .append(" ") @@ -69,6 +75,13 @@ class ManageDonationsFragment : override fun onViewCreated(view: View, savedInstanceState: Bundle?) { viewLifecycleOwner.lifecycle.addObserver(InAppPaymentsBottomSheetDelegate(childFragmentManager, viewLifecycleOwner)) super.onViewCreated(view, savedInstanceState) + + val contract = CheckoutFlowActivity.Contract() + launcher = registerForActivityResult(contract) { } + + if (savedInstanceState == null && args.directToCheckoutType != InAppPaymentType.UNKNOWN) { + launcher.launch(args.directToCheckoutType) + } } override fun onResume() { @@ -159,7 +172,7 @@ class ManageDonationsFragment : primaryWrappedButton( text = DSLSettingsText.from(R.string.ManageDonationsFragment__donate_to_signal), onClick = { - findNavController().safeNavigate(ManageDonationsFragmentDirections.actionManageDonationsFragmentToDonateToSignalFragment(InAppPaymentType.ONE_TIME_DONATION)) + launcher.launch(InAppPaymentType.ONE_TIME_DONATION) } ) @@ -269,7 +282,7 @@ class ManageDonationsFragment : subscriberRequiresCancel = state.subscriberRequiresCancel, onRowClick = { if (it != ManageDonationsState.RedemptionState.IN_PROGRESS) { - findNavController().safeNavigate(ManageDonationsFragmentDirections.actionManageDonationsFragmentToDonateToSignalFragment(InAppPaymentType.RECURRING_DONATION)) + launcher.launch(InAppPaymentType.RECURRING_DONATION) } }, onPendingClick = { @@ -337,7 +350,7 @@ class ManageDonationsFragment : title = DSLSettingsText.from(R.string.ManageDonationsFragment__donate_for_a_friend), icon = DSLSettingsIcon.from(R.drawable.symbol_gift_24), onClick = { - findNavController().safeNavigate(ManageDonationsFragmentDirections.actionManageDonationsFragmentToDonateToSignalFragment(InAppPaymentType.ONE_TIME_GIFT)) + launcher.launch(InAppPaymentType.ONE_TIME_GIFT) } ) } @@ -437,6 +450,6 @@ class ManageDonationsFragment : } override fun onMakeAMonthlyDonation() { - findNavController().safeNavigate(ManageDonationsFragmentDirections.actionManageDonationsFragmentToDonateToSignalFragment(InAppPaymentType.RECURRING_DONATION)) + launcher.launch(InAppPaymentType.ONE_TIME_DONATION) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt index d0f698907f..f4384c7e33 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt @@ -382,6 +382,10 @@ class ConversationFragment : MessageRequestRepository(requireContext()) } + private val checkoutLauncher by lazy { + registerForActivityResult(CheckoutFlowActivity.Contract()) {} + } + private val disposables = LifecycleDisposable() private val binding by ViewBinderDelegate(V2ConversationFragmentBinding::bind) { _binding -> _binding.conversationInputPanel.embeddedTextEditor.apply { @@ -2959,7 +2963,7 @@ class ConversationFragment : override fun onCallToAction(action: String) { if ("gift_badge" == action) { - startActivity(CheckoutFlowActivity.createIntent(requireContext(), InAppPaymentType.ONE_TIME_GIFT)) + checkoutLauncher.launch(InAppPaymentType.ONE_TIME_GIFT) } else if ("username_edit" == action) { startActivity(EditProfileActivity.getIntentForUsernameEdit(requireContext())) } diff --git a/app/src/main/res/navigation/app_settings.xml b/app/src/main/res/navigation/app_settings.xml index 78d3ec0d81..b8d3c3f853 100644 --- a/app/src/main/res/navigation/app_settings.xml +++ b/app/src/main/res/navigation/app_settings.xml @@ -532,17 +532,13 @@ app:popEnterAnim="@anim/fragment_close_enter" app:popExitAnim="@anim/fragment_close_exit" app:popUpTo="@id/app_settings" - app:popUpToInclusive="true" /> + app:popUpToInclusive="true"> - + + - + + + + android:name="org.thoughtcrime.securesms.components.settings.app.chats.backups.history.RemoteBackupsPaymentHistoryFragment" /> - - - - - \ No newline at end of file diff --git a/app/src/main/res/navigation/app_settings_with_change_number.xml b/app/src/main/res/navigation/app_settings_with_change_number.xml index ed2e7e39a6..9669c2a659 100644 --- a/app/src/main/res/navigation/app_settings_with_change_number.xml +++ b/app/src/main/res/navigation/app_settings_with_change_number.xml @@ -532,17 +532,13 @@ app:popEnterAnim="@anim/fragment_close_enter" app:popExitAnim="@anim/fragment_close_exit" app:popUpTo="@id/app_settings" - app:popUpToInclusive="true" /> + app:popUpToInclusive="true"> - + + - + + + - + android:name="org.thoughtcrime.securesms.components.settings.app.chats.backups.history.RemoteBackupsPaymentHistoryFragment"> - - - - - \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7e6f503a67..2ad0b5817b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -7311,6 +7311,8 @@ Subscription cancelled Download complete + + Backup will be created overnight. Backup type