Implement several donor badge fixes and rotate flags.

* Add white Google Pay buttons for use in dark mode.
* Always display badges for self.
* Disallow toggling / feature selection if no network is present.
* Only display bottom sheet overscroll if content scrolls.
* Flatten settings xml for better animations.
* Add a bit of space to the bottom of subscribe fragment.
* Treat GooglePay errors as setup failures.
* Add quieter log for 404.
* Ensure we check case before initial currency code comparison.
* Fix timeout dialog copy.
* Fix double settings activity on top issue.
* Rotate FF.
This commit is contained in:
Alex Hart
2021-11-18 13:25:37 -04:00
committed by GitHub
parent 473747ee03
commit e17c49505c
95 changed files with 838 additions and 228 deletions

View File

@@ -34,6 +34,7 @@ abstract class DSLSettingsBottomSheetFragment(
recyclerView.layoutManager = layoutManagerProducer(requireContext())
recyclerView.adapter = adapter
recyclerView.overScrollMode = RecyclerView.OVER_SCROLL_IF_CONTENT_SCROLLS
bindAdapter(adapter)
}

View File

@@ -47,8 +47,8 @@ class AppSettingsActivity : DSLSettingsActivity(), DonationPaymentComponent {
StartLocation.PROXY -> AppSettingsFragmentDirections.actionDirectToEditProxyFragment()
StartLocation.NOTIFICATIONS -> AppSettingsFragmentDirections.actionDirectToNotificationsSettingsFragment()
StartLocation.CHANGE_NUMBER -> AppSettingsFragmentDirections.actionDirectToChangeNumberFragment()
StartLocation.SUBSCRIPTIONS -> AppSettingsFragmentDirections.actionDirectToSubscriptions().setSkipToSubscribe(true)
StartLocation.MANAGE_SUBSCRIPTIONS -> AppSettingsFragmentDirections.actionDirectToSubscriptions()
StartLocation.SUBSCRIPTIONS -> AppSettingsFragmentDirections.actionDirectToSubscriptions()
StartLocation.MANAGE_SUBSCRIPTIONS -> AppSettingsFragmentDirections.actionDirectToManageDonations()
}
}
@@ -75,6 +75,12 @@ class AppSettingsActivity : DSLSettingsActivity(), DonationPaymentComponent {
}
}
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
finish()
startActivity(intent)
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putBoolean(STATE_WAS_CONFIGURATION_UPDATED, wasConfigurationUpdated)

View File

@@ -157,11 +157,11 @@ class AppSettingsFragment : DSLSettingsFragment(R.string.text_secure_normal__men
icon = DSLSettingsIcon.from(R.drawable.ic_heart_24),
isActive = state.hasActiveSubscription,
onClick = { isActive ->
findNavController()
.navigate(
AppSettingsFragmentDirections.actionAppSettingsFragmentToSubscriptions()
.setSkipToSubscribe(!isActive)
)
if (isActive) {
findNavController().navigate(AppSettingsFragmentDirections.actionAppSettingsFragmentToManageDonationsFragment())
} else {
findNavController().navigate(AppSettingsFragmentDirections.actionAppSettingsFragmentToSubscribeFragment())
}
}
)
)

View File

@@ -11,6 +11,8 @@ import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.util.FeatureFlags
import org.thoughtcrime.securesms.util.livedata.Store
import org.whispersystems.signalservice.api.push.exceptions.NotFoundException
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException
import java.util.concurrent.TimeUnit
class AppSettingsViewModel(private val subscriptionsRepository: SubscriptionsRepository) : ViewModel() {
@@ -39,6 +41,10 @@ class AppSettingsViewModel(private val subscriptionsRepository: SubscriptionsRep
subscriptionsRepository.getActiveSubscription().subscribeBy(
onSuccess = { subscription -> store.update { it.copy(hasActiveSubscription = subscription.isActive) } },
onError = { throwable ->
if (throwable.isNotFoundException()) {
Log.w(TAG, "Could not load active subscription due to unset SubscriberId (404).")
}
Log.w(TAG, "Could not load active subscription", throwable)
}
)
@@ -53,4 +59,8 @@ class AppSettingsViewModel(private val subscriptionsRepository: SubscriptionsRep
companion object {
private val TAG = Log.tag(AppSettingsViewModel::class.java)
}
private fun Throwable.isNotFoundException(): Boolean {
return this is PushNetworkException && this.cause is NotFoundException || this is NotFoundException
}
}

View File

@@ -8,7 +8,7 @@ import org.thoughtcrime.securesms.badges.models.Badge
sealed class DonationEvent {
class GooglePayUnavailableError(val throwable: Throwable) : DonationEvent()
object RequestTokenSuccess : DonationEvent()
object RequestTokenError : DonationEvent()
class RequestTokenError(val throwable: Throwable) : DonationEvent()
class PaymentConfirmationError(val throwable: Throwable) : DonationEvent()
class PaymentConfirmationSuccess(val badge: Badge) : DonationEvent()
class SubscriptionCancellationFailed(val throwable: Throwable) : DonationEvent()

View File

@@ -1,14 +1,9 @@
package org.thoughtcrime.securesms.components.settings.app.subscription
import android.app.Activity
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.net.ConnectivityManager
import com.google.android.gms.wallet.PaymentData
import io.reactivex.rxjava3.core.Completable
import io.reactivex.rxjava3.core.Observable
import io.reactivex.rxjava3.core.Single
import io.reactivex.rxjava3.schedulers.Schedulers
import org.signal.core.util.concurrent.SignalExecutors
@@ -21,7 +16,6 @@ import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.jobmanager.JobTracker
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint
import org.thoughtcrime.securesms.jobs.BoostReceiptRequestResponseJob
import org.thoughtcrime.securesms.jobs.SubscriptionReceiptRequestResponseJob
import org.thoughtcrime.securesms.keyvalue.SignalStore
@@ -75,19 +69,6 @@ class DonationPaymentRepository(activity: Activity) : StripeApi.PaymentIntentFet
StorageSyncHelper.scheduleSyncForDataChange()
}
fun internetConnectionObserver(): Observable<Boolean> = Observable.create {
val observer = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
if (!it.isDisposed) {
it.onNext(NetworkConstraint.isMet(application))
}
}
}
it.setCancellable { application.unregisterReceiver(observer) }
application.registerReceiver(observer, IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION))
}
fun requestTokenFromGooglePay(price: FiatMoney, label: String, requestCode: Int) {
googlePayApi.requestPayment(price, label, requestCode)
}

View File

@@ -121,7 +121,7 @@ class BoostFragment : DSLSettingsBottomSheetFragment(
is DonationEvent.GooglePayUnavailableError -> Unit
is DonationEvent.PaymentConfirmationError -> onPaymentError(event.throwable)
is DonationEvent.PaymentConfirmationSuccess -> onPaymentConfirmed(event.badge)
DonationEvent.RequestTokenError -> onPaymentError(null)
is DonationEvent.RequestTokenError -> onPaymentError(event.throwable)
DonationEvent.RequestTokenSuccess -> Log.i(TAG, "Successfully got request token from Google Pay")
DonationEvent.SubscriptionCancelled -> Unit
is DonationEvent.SubscriptionCancellationFailed -> Unit

View File

@@ -19,6 +19,7 @@ import org.thoughtcrime.securesms.badges.models.Badge
import org.thoughtcrime.securesms.components.settings.app.subscription.DonationEvent
import org.thoughtcrime.securesms.components.settings.app.subscription.DonationPaymentRepository
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.util.InternetConnectionObserver
import org.thoughtcrime.securesms.util.PlatformCurrencyUtil
import org.thoughtcrime.securesms.util.livedata.Store
import java.math.BigDecimal
@@ -42,8 +43,8 @@ class BoostViewModel(
private var boostToPurchase: Boost? = null
init {
networkDisposable = donationPaymentRepository
.internetConnectionObserver()
networkDisposable = InternetConnectionObserver
.observe()
.distinctUntilChanged()
.subscribe { isConnected ->
if (isConnected) {
@@ -152,9 +153,9 @@ class BoostViewModel(
}
}
override fun onError() {
override fun onError(googlePayException: GooglePayApi.GooglePayException) {
store.update { it.copy(stage = BoostState.Stage.READY) }
eventPublisher.onNext(DonationEvent.RequestTokenError)
eventPublisher.onNext(DonationEvent.RequestTokenError(googlePayException))
}
override fun onCancelled() {

View File

@@ -1,9 +1,7 @@
package org.thoughtcrime.securesms.components.settings.app.subscription.manage
import android.os.Bundle
import android.widget.Toast
import androidx.fragment.app.viewModels
import androidx.navigation.NavOptions
import androidx.navigation.fragment.findNavController
import org.signal.core.util.DimensionUnit
import org.thoughtcrime.securesms.R
@@ -37,29 +35,12 @@ class ManageDonationsFragment : DSLSettingsFragment() {
private val lifecycleDisposable = LifecycleDisposable()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val args = ManageDonationsFragmentArgs.fromBundle(requireArguments())
if (args.skipToSubscribe) {
findNavController().navigate(
ManageDonationsFragmentDirections.actionManageDonationsFragmentToSubscribeFragment(),
NavOptions.Builder().setPopUpTo(R.id.manageDonationsFragment, true).build()
)
}
}
override fun onResume() {
super.onResume()
viewModel.refresh()
}
override fun bindAdapter(adapter: DSLSettingsAdapter) {
val args = ManageDonationsFragmentArgs.fromBundle(requireArguments())
if (args.skipToSubscribe) {
return
}
ActiveSubscriptionPreference.register(adapter)
IndeterminateLoadingCircle.register(adapter)
BadgePreview.register(adapter)
@@ -149,7 +130,7 @@ class ManageDonationsFragment : DSLSettingsFragment() {
title = DSLSettingsText.from(R.string.ManageDonationsFragment__badges),
icon = DSLSettingsIcon.from(R.drawable.ic_badge_24),
onClick = {
findNavController().navigate(ManageDonationsFragmentDirections.actionManageDonationsFragmentToSubscriptionBadgeManageFragment())
findNavController().navigate(ManageDonationsFragmentDirections.actionManageDonationsFragmentToSubscribeFragment())
}
)

View File

@@ -16,7 +16,6 @@ import org.thoughtcrime.securesms.badges.models.BadgePreview
import org.thoughtcrime.securesms.components.settings.DSLConfiguration
import org.thoughtcrime.securesms.components.settings.DSLSettingsAdapter
import org.thoughtcrime.securesms.components.settings.DSLSettingsFragment
import org.thoughtcrime.securesms.components.settings.DSLSettingsIcon
import org.thoughtcrime.securesms.components.settings.DSLSettingsText
import org.thoughtcrime.securesms.components.settings.app.AppSettingsActivity
import org.thoughtcrime.securesms.components.settings.app.subscription.DonationEvent
@@ -33,7 +32,6 @@ import org.thoughtcrime.securesms.help.HelpFragment
import org.thoughtcrime.securesms.keyboard.findListener
import org.thoughtcrime.securesms.payments.FiatMoneyUtil
import org.thoughtcrime.securesms.subscription.Subscription
import org.thoughtcrime.securesms.util.CommunicationActions
import org.thoughtcrime.securesms.util.LifecycleDisposable
import org.thoughtcrime.securesms.util.SpanUtil
import java.util.Calendar
@@ -52,7 +50,7 @@ class SubscribeFragment : DSLSettingsFragment(
SpannableStringBuilder(requireContext().getString(R.string.SubscribeFragment__support_technology_that_is_built_for_you_not))
.append(" ")
.append(
SpanUtil.readMore(requireContext(), ContextCompat.getColor(requireContext(), R.color.signal_accent_primary)) {
SpanUtil.readMore(requireContext(), ContextCompat.getColor(requireContext(), R.color.signal_button_secondary_text)) {
findNavController().navigate(SubscribeFragmentDirections.actionSubscribeFragmentToSubscribeLearnMoreBottomSheetDialog())
}
)
@@ -98,7 +96,7 @@ class SubscribeFragment : DSLSettingsFragment(
is DonationEvent.GooglePayUnavailableError -> Unit
is DonationEvent.PaymentConfirmationError -> onPaymentError(it.throwable)
is DonationEvent.PaymentConfirmationSuccess -> onPaymentConfirmed(it.badge)
DonationEvent.RequestTokenError -> onPaymentError(null)
is DonationEvent.RequestTokenError -> onPaymentError(DonationExceptions.SetupFailed(it.throwable))
DonationEvent.RequestTokenSuccess -> Log.w(TAG, "Successfully got request token from Google Pay")
DonationEvent.SubscriptionCancelled -> onSubscriptionCancelled()
is DonationEvent.SubscriptionCancellationFailed -> onSubscriptionFailedToCancel(it.throwable)
@@ -249,13 +247,7 @@ class SubscribeFragment : DSLSettingsFragment(
)
)
secondaryButtonNoOutline(
text = DSLSettingsText.from(R.string.SubscribeFragment__more_payment_options),
icon = DSLSettingsIcon.from(R.drawable.ic_open_20, R.color.signal_accent_primary),
onClick = {
CommunicationActions.openBrowserLink(requireContext(), getString(R.string.donate_url))
}
)
space(DimensionUnit.DP.toPixels(8f).toInt())
}
}
}
@@ -275,7 +267,7 @@ class SubscribeFragment : DSLSettingsFragment(
Log.w(TAG, "Timeout occurred while redeeming token", throwable, true)
MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.DonationsErrors__still_processing)
.setMessage(R.string.DonationsErrors__your_payment)
.setMessage(R.string.DonationsErrors__your_payment_is_still)
.setPositiveButton(android.R.string.ok) { dialog, _ ->
dialog.dismiss()
requireActivity().finish()

View File

@@ -25,6 +25,7 @@ import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.subscription.LevelUpdate
import org.thoughtcrime.securesms.subscription.Subscriber
import org.thoughtcrime.securesms.subscription.Subscription
import org.thoughtcrime.securesms.util.InternetConnectionObserver
import org.thoughtcrime.securesms.util.PlatformCurrencyUtil
import org.thoughtcrime.securesms.util.livedata.Store
import org.whispersystems.signalservice.api.subscriptions.ActiveSubscription
@@ -49,8 +50,8 @@ class SubscribeViewModel(
private val activeSubscriptionSubject = PublishSubject.create<ActiveSubscription>()
init {
networkDisposable = donationPaymentRepository
.internetConnectionObserver()
networkDisposable = InternetConnectionObserver
.observe()
.distinctUntilChanged()
.subscribe { isConnected ->
if (isConnected) {
@@ -218,9 +219,9 @@ class SubscribeViewModel(
}
}
override fun onError() {
override fun onError(googlePayException: GooglePayApi.GooglePayException) {
store.update { it.copy(stage = SubscribeState.Stage.READY) }
eventPublisher.onNext(DonationEvent.RequestTokenError)
eventPublisher.onNext(DonationEvent.RequestTokenError(googlePayException))
}
override fun onCancelled() {

View File

@@ -39,7 +39,7 @@ object AsyncSwitch {
super.bind(model)
switchWidget.isEnabled = model.isEnabled
switchWidget.isChecked = model.isChecked
itemView.isEnabled = !model.isProcessing
itemView.isEnabled = !model.isProcessing && model.isEnabled
switcher.displayedChild = if (model.isProcessing) 1 else 0
itemView.setOnClickListener {