kopia lustrzana https://github.com/ryukoposting/Signal-Android
Implement PayPal confirm donation sheet.
rodzic
e686a09ce4
commit
961057f620
|
@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.components.settings.app.subscription
|
|||
import io.reactivex.rxjava3.core.Completable
|
||||
import io.reactivex.rxjava3.core.Single
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.signal.core.util.money.FiatMoney
|
||||
import org.signal.donations.PaymentSourceType
|
||||
import org.thoughtcrime.securesms.components.settings.app.subscription.donate.paypal.PayPalConfirmationResult
|
||||
|
@ -24,6 +25,8 @@ class PayPalRepository(private val donationsService: DonationsService) {
|
|||
const val ONE_TIME_RETURN_URL = "https://signaldonations.org/return/onetime"
|
||||
const val MONTHLY_RETURN_URL = "https://signaldonations.org/return/monthly"
|
||||
const val CANCEL_URL = "https://signaldonations.org/cancel"
|
||||
|
||||
private val TAG = Log.tag(PayPalRepository::class.java)
|
||||
}
|
||||
|
||||
fun createOneTimePaymentIntent(
|
||||
|
@ -53,6 +56,7 @@ class PayPalRepository(private val donationsService: DonationsService) {
|
|||
paypalConfirmationResult: PayPalConfirmationResult
|
||||
): Single<PayPalConfirmPaymentIntentResponse> {
|
||||
return Single.fromCallable {
|
||||
Log.d(TAG, "Confirming one-time payment intent...", true)
|
||||
donationsService
|
||||
.confirmPayPalOneTimePaymentIntent(
|
||||
amount.currency.currencyCode,
|
||||
|
@ -78,11 +82,14 @@ class PayPalRepository(private val donationsService: DonationsService) {
|
|||
|
||||
fun setDefaultPaymentMethod(paymentMethodId: String): Completable {
|
||||
return Single.fromCallable {
|
||||
Log.d(TAG, "Setting default payment method...", true)
|
||||
donationsService.setDefaultPayPalPaymentMethod(
|
||||
SignalStore.donationsValues().requireSubscriber().subscriberId,
|
||||
paymentMethodId
|
||||
)
|
||||
}.flatMap { it.flattenResult() }.ignoreElement().andThen {
|
||||
}.flatMap { it.flattenResult() }.ignoreElement().doOnComplete {
|
||||
Log.d(TAG, "Set default payment method.", true)
|
||||
Log.d(TAG, "Storing the subscription payment source type locally.", true)
|
||||
SignalStore.donationsValues().setSubscriptionPaymentSourceType(PaymentSourceType.PayPal)
|
||||
}.subscribeOn(Schedulers.io())
|
||||
}
|
||||
|
|
|
@ -2,10 +2,19 @@ package org.thoughtcrime.securesms.components.settings.app.subscription.donate
|
|||
|
||||
import android.os.Parcelable
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import org.thoughtcrime.securesms.components.settings.app.subscription.errors.DonationErrorSource
|
||||
|
||||
@Parcelize
|
||||
enum class DonateToSignalType(val requestCode: Short) : Parcelable {
|
||||
ONE_TIME(16141),
|
||||
MONTHLY(16142),
|
||||
GIFT(16143)
|
||||
GIFT(16143);
|
||||
|
||||
fun toErrorSource(): DonationErrorSource {
|
||||
return when (this) {
|
||||
ONE_TIME -> DonationErrorSource.BOOST
|
||||
MONTHLY -> DonationErrorSource.SUBSCRIPTION
|
||||
GIFT -> DonationErrorSource.GIFT
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -235,6 +235,12 @@ class DonationCheckoutDelegate(
|
|||
return
|
||||
}
|
||||
|
||||
if (throwable is DonationError.PayPalError.UserCancelledPaymentError) {
|
||||
Log.d(TAG, "User cancelled out of paypal flow.", true)
|
||||
fragment?.findNavController()?.popBackStack()
|
||||
return
|
||||
}
|
||||
|
||||
Log.d(TAG, "Displaying donation error dialog.", true)
|
||||
errorDialog = DonationErrorDialogs.show(
|
||||
fragment!!.requireContext(), throwable,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package org.thoughtcrime.securesms.components.settings.app.subscription.donate.gateway
|
||||
|
||||
import android.content.Context
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.fragment.app.setFragmentResult
|
||||
|
@ -60,11 +61,7 @@ class GatewaySelectorBottomSheet : DSLSettingsBottomSheetFragment() {
|
|||
|
||||
space(12.dp)
|
||||
|
||||
when (args.request.donateToSignalType) {
|
||||
DonateToSignalType.MONTHLY -> presentMonthlyText()
|
||||
DonateToSignalType.ONE_TIME -> presentOneTimeText()
|
||||
DonateToSignalType.GIFT -> presentGiftText()
|
||||
}
|
||||
presentTitleAndSubtitle(requireContext(), args.request)
|
||||
|
||||
space(66.dp)
|
||||
|
||||
|
@ -114,64 +111,72 @@ class GatewaySelectorBottomSheet : DSLSettingsBottomSheetFragment() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun DSLConfiguration.presentMonthlyText() {
|
||||
noPadTextPref(
|
||||
title = DSLSettingsText.from(
|
||||
getString(R.string.GatewaySelectorBottomSheet__donate_s_month_to_signal, FiatMoneyUtil.format(resources, args.request.fiat)),
|
||||
DSLSettingsText.CenterModifier,
|
||||
DSLSettingsText.TitleLargeModifier
|
||||
)
|
||||
)
|
||||
space(6.dp)
|
||||
noPadTextPref(
|
||||
title = DSLSettingsText.from(
|
||||
getString(R.string.GatewaySelectorBottomSheet__get_a_s_badge, args.request.badge.name),
|
||||
DSLSettingsText.CenterModifier,
|
||||
DSLSettingsText.BodyLargeModifier,
|
||||
DSLSettingsText.ColorModifier(ContextCompat.getColor(requireContext(), R.color.signal_colorOnSurfaceVariant))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun DSLConfiguration.presentOneTimeText() {
|
||||
noPadTextPref(
|
||||
title = DSLSettingsText.from(
|
||||
getString(R.string.GatewaySelectorBottomSheet__donate_s_to_signal, FiatMoneyUtil.format(resources, args.request.fiat)),
|
||||
DSLSettingsText.CenterModifier,
|
||||
DSLSettingsText.TitleLargeModifier
|
||||
)
|
||||
)
|
||||
space(6.dp)
|
||||
noPadTextPref(
|
||||
title = DSLSettingsText.from(
|
||||
resources.getQuantityString(R.plurals.GatewaySelectorBottomSheet__get_a_s_badge_for_d_days, 30, args.request.badge.name, 30),
|
||||
DSLSettingsText.CenterModifier,
|
||||
DSLSettingsText.BodyLargeModifier,
|
||||
DSLSettingsText.ColorModifier(ContextCompat.getColor(requireContext(), R.color.signal_colorOnSurfaceVariant))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun DSLConfiguration.presentGiftText() {
|
||||
noPadTextPref(
|
||||
title = DSLSettingsText.from(
|
||||
getString(R.string.GatewaySelectorBottomSheet__donate_s_to_signal, FiatMoneyUtil.format(resources, args.request.fiat)),
|
||||
DSLSettingsText.CenterModifier,
|
||||
DSLSettingsText.TitleLargeModifier
|
||||
)
|
||||
)
|
||||
space(6.dp)
|
||||
noPadTextPref(
|
||||
title = DSLSettingsText.from(
|
||||
R.string.GatewaySelectorBottomSheet__send_a_gift_badge,
|
||||
DSLSettingsText.CenterModifier,
|
||||
DSLSettingsText.BodyLargeModifier,
|
||||
DSLSettingsText.ColorModifier(ContextCompat.getColor(requireContext(), R.color.signal_colorOnSurfaceVariant))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val REQUEST_KEY = "payment_checkout_mode"
|
||||
|
||||
fun DSLConfiguration.presentTitleAndSubtitle(context: Context, request: GatewayRequest) {
|
||||
when (request.donateToSignalType) {
|
||||
DonateToSignalType.MONTHLY -> presentMonthlyText(context, request)
|
||||
DonateToSignalType.ONE_TIME -> presentOneTimeText(context, request)
|
||||
DonateToSignalType.GIFT -> presentGiftText(context, request)
|
||||
}
|
||||
}
|
||||
|
||||
private fun DSLConfiguration.presentMonthlyText(context: Context, request: GatewayRequest) {
|
||||
noPadTextPref(
|
||||
title = DSLSettingsText.from(
|
||||
context.getString(R.string.GatewaySelectorBottomSheet__donate_s_month_to_signal, FiatMoneyUtil.format(context.resources, request.fiat)),
|
||||
DSLSettingsText.CenterModifier,
|
||||
DSLSettingsText.TitleLargeModifier
|
||||
)
|
||||
)
|
||||
space(6.dp)
|
||||
noPadTextPref(
|
||||
title = DSLSettingsText.from(
|
||||
context.getString(R.string.GatewaySelectorBottomSheet__get_a_s_badge, request.badge.name),
|
||||
DSLSettingsText.CenterModifier,
|
||||
DSLSettingsText.BodyLargeModifier,
|
||||
DSLSettingsText.ColorModifier(ContextCompat.getColor(context, R.color.signal_colorOnSurfaceVariant))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun DSLConfiguration.presentOneTimeText(context: Context, request: GatewayRequest) {
|
||||
noPadTextPref(
|
||||
title = DSLSettingsText.from(
|
||||
context.getString(R.string.GatewaySelectorBottomSheet__donate_s_to_signal, FiatMoneyUtil.format(context.resources, request.fiat)),
|
||||
DSLSettingsText.CenterModifier,
|
||||
DSLSettingsText.TitleLargeModifier
|
||||
)
|
||||
)
|
||||
space(6.dp)
|
||||
noPadTextPref(
|
||||
title = DSLSettingsText.from(
|
||||
context.resources.getQuantityString(R.plurals.GatewaySelectorBottomSheet__get_a_s_badge_for_d_days, 30, request.badge.name, 30),
|
||||
DSLSettingsText.CenterModifier,
|
||||
DSLSettingsText.BodyLargeModifier,
|
||||
DSLSettingsText.ColorModifier(ContextCompat.getColor(context, R.color.signal_colorOnSurfaceVariant))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun DSLConfiguration.presentGiftText(context: Context, request: GatewayRequest) {
|
||||
noPadTextPref(
|
||||
title = DSLSettingsText.from(
|
||||
context.getString(R.string.GatewaySelectorBottomSheet__donate_s_to_signal, FiatMoneyUtil.format(context.resources, request.fiat)),
|
||||
DSLSettingsText.CenterModifier,
|
||||
DSLSettingsText.TitleLargeModifier
|
||||
)
|
||||
)
|
||||
space(6.dp)
|
||||
noPadTextPref(
|
||||
title = DSLSettingsText.from(
|
||||
R.string.GatewaySelectorBottomSheet__send_a_gift_badge,
|
||||
DSLSettingsText.CenterModifier,
|
||||
DSLSettingsText.BodyLargeModifier,
|
||||
DSLSettingsText.ColorModifier(ContextCompat.getColor(context, R.color.signal_colorOnSurfaceVariant))
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
package org.thoughtcrime.securesms.components.settings.app.subscription.donate.paypal
|
||||
|
||||
import android.content.DialogInterface
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.fragment.app.setFragmentResult
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.navigation.fragment.navArgs
|
||||
import org.signal.core.util.dp
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.badges.models.BadgeDisplay112
|
||||
import org.thoughtcrime.securesms.components.settings.DSLConfiguration
|
||||
import org.thoughtcrime.securesms.components.settings.DSLSettingsAdapter
|
||||
import org.thoughtcrime.securesms.components.settings.DSLSettingsBottomSheetFragment
|
||||
import org.thoughtcrime.securesms.components.settings.DSLSettingsText
|
||||
import org.thoughtcrime.securesms.components.settings.app.subscription.donate.gateway.GatewaySelectorBottomSheet.Companion.presentTitleAndSubtitle
|
||||
import org.thoughtcrime.securesms.components.settings.configure
|
||||
|
||||
/**
|
||||
* Bottom sheet for final order confirmation from PayPal
|
||||
*/
|
||||
class PayPalCompleteOrderBottomSheet : DSLSettingsBottomSheetFragment() {
|
||||
|
||||
companion object {
|
||||
const val REQUEST_KEY = "complete_order"
|
||||
}
|
||||
|
||||
private var didConfirmOrder = false
|
||||
private val args: PayPalCompleteOrderBottomSheetArgs by navArgs()
|
||||
|
||||
override fun bindAdapter(adapter: DSLSettingsAdapter) {
|
||||
BadgeDisplay112.register(adapter)
|
||||
PayPalCompleteOrderPaymentItem.register(adapter)
|
||||
|
||||
adapter.submitList(getConfiguration().toMappingModelList())
|
||||
}
|
||||
|
||||
override fun onDismiss(dialog: DialogInterface) {
|
||||
setFragmentResult(REQUEST_KEY, bundleOf(REQUEST_KEY to didConfirmOrder))
|
||||
}
|
||||
|
||||
private fun getConfiguration(): DSLConfiguration {
|
||||
return configure {
|
||||
customPref(
|
||||
BadgeDisplay112.Model(
|
||||
badge = args.request.badge,
|
||||
withDisplayText = false
|
||||
)
|
||||
)
|
||||
|
||||
space(12.dp)
|
||||
|
||||
presentTitleAndSubtitle(requireContext(), args.request)
|
||||
|
||||
space(24.dp)
|
||||
|
||||
customPref(PayPalCompleteOrderPaymentItem.Model())
|
||||
|
||||
space(82.dp)
|
||||
|
||||
primaryButton(
|
||||
text = DSLSettingsText.from(R.string.PaypalCompleteOrderBottomSheet__donate),
|
||||
onClick = {
|
||||
didConfirmOrder = true
|
||||
findNavController().popBackStack()
|
||||
}
|
||||
)
|
||||
|
||||
secondaryButtonNoOutline(
|
||||
text = DSLSettingsText.from(android.R.string.cancel),
|
||||
onClick = {
|
||||
findNavController().popBackStack()
|
||||
}
|
||||
)
|
||||
|
||||
space(16.dp)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package org.thoughtcrime.securesms.components.settings.app.subscription.donate.paypal
|
||||
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.util.adapter.mapping.LayoutFactory
|
||||
import org.thoughtcrime.securesms.util.adapter.mapping.MappingAdapter
|
||||
import org.thoughtcrime.securesms.util.adapter.mapping.MappingModel
|
||||
import org.thoughtcrime.securesms.util.adapter.mapping.MappingViewHolder.SimpleViewHolder
|
||||
|
||||
/**
|
||||
* Line item on the PayPal order confirmation screen.
|
||||
*/
|
||||
object PayPalCompleteOrderPaymentItem {
|
||||
fun register(mappingAdapter: MappingAdapter) {
|
||||
mappingAdapter.registerFactory(Model::class.java, LayoutFactory(::SimpleViewHolder, R.layout.paypal_complete_order_payment_item))
|
||||
}
|
||||
|
||||
class Model : MappingModel<Model> {
|
||||
override fun areItemsTheSame(newItem: Model): Boolean = true
|
||||
|
||||
override fun areContentsTheSame(newItem: Model): Boolean = true
|
||||
}
|
||||
}
|
|
@ -11,6 +11,8 @@ import android.webkit.WebViewClient
|
|||
import androidx.core.os.bundleOf
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.fragment.app.setFragmentResult
|
||||
import androidx.lifecycle.DefaultLifecycleObserver
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.navigation.fragment.navArgs
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.thoughtcrime.securesms.R
|
||||
|
@ -47,7 +49,9 @@ class PayPalConfirmationDialogFragment : DialogFragment(R.layout.donation_webvie
|
|||
|
||||
@SuppressLint("SetJavaScriptEnabled")
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
binding.webView.webViewClient = PayPalWebClient()
|
||||
val client = PayPalWebClient()
|
||||
viewLifecycleOwner.lifecycle.addObserver(client)
|
||||
binding.webView.webViewClient = client
|
||||
binding.webView.settings.javaScriptEnabled = true
|
||||
binding.webView.settings.cacheMode = WebSettings.LOAD_NO_CACHE
|
||||
binding.webView.loadUrl(args.uri.toString())
|
||||
|
@ -59,21 +63,31 @@ class PayPalConfirmationDialogFragment : DialogFragment(R.layout.donation_webvie
|
|||
setFragmentResult(REQUEST_KEY, result ?: Bundle())
|
||||
}
|
||||
|
||||
private inner class PayPalWebClient : WebViewClient() {
|
||||
private inner class PayPalWebClient : WebViewClient(), DefaultLifecycleObserver {
|
||||
|
||||
private var isDestroyed = false
|
||||
|
||||
override fun onDestroy(owner: LifecycleOwner) {
|
||||
isDestroyed = true
|
||||
}
|
||||
|
||||
override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
|
||||
if (!isFinished) {
|
||||
if (!isDestroyed) {
|
||||
binding.progress.visible = true
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPageCommitVisible(view: WebView?, url: String?) {
|
||||
if (!isFinished) {
|
||||
if (!isDestroyed) {
|
||||
binding.progress.visible = false
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPageFinished(view: WebView?, url: String?) {
|
||||
if (isDestroyed) {
|
||||
return
|
||||
}
|
||||
|
||||
if (url?.startsWith(PayPalRepository.ONE_TIME_RETURN_URL) == true) {
|
||||
val confirmationResult = PayPalConfirmationResult.fromUrl(url)
|
||||
if (confirmationResult != null) {
|
||||
|
|
|
@ -23,6 +23,7 @@ import org.thoughtcrime.securesms.components.ViewBinderDelegate
|
|||
import org.thoughtcrime.securesms.components.settings.app.subscription.donate.DonationProcessorAction
|
||||
import org.thoughtcrime.securesms.components.settings.app.subscription.donate.DonationProcessorActionResult
|
||||
import org.thoughtcrime.securesms.components.settings.app.subscription.donate.DonationProcessorStage
|
||||
import org.thoughtcrime.securesms.components.settings.app.subscription.errors.DonationError
|
||||
import org.thoughtcrime.securesms.databinding.DonationInProgressFragmentBinding
|
||||
import org.thoughtcrime.securesms.util.LifecycleDisposable
|
||||
import org.thoughtcrime.securesms.util.navigation.safeNavigate
|
||||
|
@ -57,7 +58,7 @@ class PayPalPaymentInProgressFragment : DialogFragment(R.layout.donation_in_prog
|
|||
viewModel.onBeginNewAction()
|
||||
when (args.action) {
|
||||
DonationProcessorAction.PROCESS_NEW_DONATION -> {
|
||||
viewModel.processNewDonation(args.request, this::routeToOneTimeConfirmation, this::routeToMonthlyConfirmation)
|
||||
viewModel.processNewDonation(args.request, this::oneTimeConfirmationPipeline, this::monthlyConfirmationPipeline)
|
||||
}
|
||||
DonationProcessorAction.UPDATE_SUBSCRIPTION -> {
|
||||
viewModel.updateSubscription(args.request)
|
||||
|
@ -110,6 +111,18 @@ class PayPalPaymentInProgressFragment : DialogFragment(R.layout.donation_in_prog
|
|||
}
|
||||
}
|
||||
|
||||
private fun oneTimeConfirmationPipeline(createPaymentIntentResponse: PayPalCreatePaymentIntentResponse): Single<PayPalConfirmationResult> {
|
||||
return routeToOneTimeConfirmation(createPaymentIntentResponse).flatMap {
|
||||
displayCompleteOrderSheet(it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun monthlyConfirmationPipeline(createPaymentIntentResponse: PayPalCreatePaymentMethodResponse): Single<PayPalPaymentMethodId> {
|
||||
return routeToMonthlyConfirmation(createPaymentIntentResponse).flatMap {
|
||||
displayCompleteOrderSheet(it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun routeToOneTimeConfirmation(createPaymentIntentResponse: PayPalCreatePaymentIntentResponse): Single<PayPalConfirmationResult> {
|
||||
return Single.create<PayPalConfirmationResult> { emitter ->
|
||||
val listener = FragmentResultListener { _, bundle ->
|
||||
|
@ -117,10 +130,11 @@ class PayPalPaymentInProgressFragment : DialogFragment(R.layout.donation_in_prog
|
|||
if (result != null) {
|
||||
emitter.onSuccess(result)
|
||||
} else {
|
||||
emitter.onError(Exception("User did not complete paypal confirmation."))
|
||||
emitter.onError(DonationError.PayPalError.UserCancelledPaymentError(args.request.donateToSignalType.toErrorSource()))
|
||||
}
|
||||
}
|
||||
|
||||
parentFragmentManager.clearFragmentResult(PayPalConfirmationDialogFragment.REQUEST_KEY)
|
||||
parentFragmentManager.setFragmentResultListener(PayPalConfirmationDialogFragment.REQUEST_KEY, this, listener)
|
||||
|
||||
findNavController().safeNavigate(
|
||||
|
@ -130,6 +144,8 @@ class PayPalPaymentInProgressFragment : DialogFragment(R.layout.donation_in_prog
|
|||
)
|
||||
|
||||
emitter.setCancellable {
|
||||
Log.d(TAG, "Clearing one-time confirmation result listener.")
|
||||
parentFragmentManager.clearFragmentResult(PayPalConfirmationDialogFragment.REQUEST_KEY)
|
||||
parentFragmentManager.clearFragmentResultListener(PayPalConfirmationDialogFragment.REQUEST_KEY)
|
||||
}
|
||||
}.subscribeOn(AndroidSchedulers.mainThread()).observeOn(Schedulers.io())
|
||||
|
@ -138,14 +154,15 @@ class PayPalPaymentInProgressFragment : DialogFragment(R.layout.donation_in_prog
|
|||
private fun routeToMonthlyConfirmation(createPaymentIntentResponse: PayPalCreatePaymentMethodResponse): Single<PayPalPaymentMethodId> {
|
||||
return Single.create<PayPalPaymentMethodId> { emitter ->
|
||||
val listener = FragmentResultListener { _, bundle ->
|
||||
val result: Boolean = bundle.getBoolean(REQUEST_KEY)
|
||||
val result: Boolean = bundle.getBoolean(PayPalConfirmationDialogFragment.REQUEST_KEY)
|
||||
if (result) {
|
||||
emitter.onSuccess(PayPalPaymentMethodId(createPaymentIntentResponse.token))
|
||||
} else {
|
||||
emitter.onError(Exception("User did not confirm paypal setup."))
|
||||
emitter.onError(DonationError.PayPalError.UserCancelledPaymentError(args.request.donateToSignalType.toErrorSource()))
|
||||
}
|
||||
}
|
||||
|
||||
parentFragmentManager.clearFragmentResult(PayPalConfirmationDialogFragment.REQUEST_KEY)
|
||||
parentFragmentManager.setFragmentResultListener(PayPalConfirmationDialogFragment.REQUEST_KEY, this, listener)
|
||||
|
||||
findNavController().safeNavigate(
|
||||
|
@ -155,8 +172,37 @@ class PayPalPaymentInProgressFragment : DialogFragment(R.layout.donation_in_prog
|
|||
)
|
||||
|
||||
emitter.setCancellable {
|
||||
Log.d(TAG, "Clearing monthly confirmation result listener.")
|
||||
parentFragmentManager.clearFragmentResult(PayPalConfirmationDialogFragment.REQUEST_KEY)
|
||||
parentFragmentManager.clearFragmentResultListener(PayPalConfirmationDialogFragment.REQUEST_KEY)
|
||||
}
|
||||
}.subscribeOn(AndroidSchedulers.mainThread()).observeOn(Schedulers.io())
|
||||
}
|
||||
|
||||
private fun <T : Any> displayCompleteOrderSheet(confirmationData: T): Single<T> {
|
||||
return Single.create<T> { emitter ->
|
||||
val listener = FragmentResultListener { _, bundle ->
|
||||
val result: Boolean = bundle.getBoolean(PayPalCompleteOrderBottomSheet.REQUEST_KEY)
|
||||
if (result) {
|
||||
Log.d(TAG, "User confirmed order. Continuing...")
|
||||
emitter.onSuccess(confirmationData)
|
||||
} else {
|
||||
emitter.onError(DonationError.PayPalError.UserCancelledPaymentError(args.request.donateToSignalType.toErrorSource()))
|
||||
}
|
||||
}
|
||||
|
||||
parentFragmentManager.clearFragmentResult(PayPalCompleteOrderBottomSheet.REQUEST_KEY)
|
||||
parentFragmentManager.setFragmentResultListener(PayPalCompleteOrderBottomSheet.REQUEST_KEY, this, listener)
|
||||
|
||||
findNavController().safeNavigate(
|
||||
PayPalPaymentInProgressFragmentDirections.actionPaypalPaymentInProgressFragmentToPaypalCompleteOrderBottomSheet(args.request)
|
||||
)
|
||||
|
||||
emitter.setCancellable {
|
||||
Log.d(TAG, "Clearing complete order result listener.")
|
||||
parentFragmentManager.clearFragmentResult(PayPalCompleteOrderBottomSheet.REQUEST_KEY)
|
||||
parentFragmentManager.clearFragmentResultListener(PayPalCompleteOrderBottomSheet.REQUEST_KEY)
|
||||
}
|
||||
}.subscribeOn(AndroidSchedulers.mainThread()).observeOn(Schedulers.io())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,10 @@ sealed class DonationError(val source: DonationErrorSource, cause: Throwable) :
|
|||
class RequestTokenError(source: DonationErrorSource, cause: Throwable) : GooglePayError(source, cause)
|
||||
}
|
||||
|
||||
sealed class PayPalError(source: DonationErrorSource, cause: Throwable) : DonationError(source, cause) {
|
||||
class UserCancelledPaymentError(source: DonationErrorSource) : DonationError(source, Exception("User cancelled payment."))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gifting recipient validation errors, which occur before the user could be charged for a gift.
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="92dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="92"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M34.179,5.992H29.121C28.774,5.992 28.48,6.244 28.426,6.586L26.38,19.557C26.34,19.812 26.538,20.043 26.797,20.043H29.212C29.558,20.043 29.853,19.792 29.907,19.449L30.458,15.951C30.512,15.608 30.807,15.357 31.152,15.357H32.754C36.086,15.357 38.009,13.744 38.511,10.549C38.737,9.151 38.521,8.053 37.866,7.283C37.147,6.439 35.872,5.992 34.179,5.992M34.763,10.729C34.486,12.545 33.099,12.545 31.758,12.545H30.995L31.53,9.155C31.562,8.95 31.74,8.799 31.947,8.799H32.296C33.21,8.799 34.072,8.799 34.517,9.319C34.782,9.63 34.864,10.092 34.763,10.729Z"
|
||||
android:fillColor="#F3F3F3"/>
|
||||
<path
|
||||
android:pathData="M49.3,10.67H46.877C46.672,10.67 46.493,10.821 46.461,11.026L46.354,11.704L46.185,11.458C45.661,10.698 44.491,10.442 43.324,10.442C40.647,10.442 38.361,12.47 37.916,15.313C37.684,16.733 38.014,18.089 38.818,19.035C39.556,19.905 40.612,20.267 41.869,20.267C44.025,20.267 45.221,18.881 45.221,18.881L45.113,19.554C45.073,19.811 45.271,20.042 45.529,20.042H47.711C48.058,20.042 48.351,19.791 48.406,19.448L49.715,11.158C49.757,10.902 49.559,10.67 49.3,10.67M45.924,15.385C45.69,16.768 44.591,17.698 43.191,17.698C42.488,17.698 41.925,17.472 41.564,17.045C41.206,16.62 41.07,16.016 41.184,15.342C41.403,13.971 42.519,13.012 43.899,13.012C44.587,13.012 45.145,13.24 45.514,13.671C45.883,14.107 46.03,14.715 45.924,15.385"
|
||||
android:fillColor="#F3F3F3"/>
|
||||
<path
|
||||
android:pathData="M62.2,10.671H59.766C59.534,10.671 59.315,10.787 59.184,10.98L55.827,15.925L54.403,11.173C54.314,10.875 54.039,10.671 53.728,10.671H51.336C51.046,10.671 50.845,10.956 50.937,11.229L53.618,19.097L51.097,22.656C50.899,22.937 51.099,23.321 51.441,23.321H53.872C54.103,23.321 54.319,23.209 54.45,23.02L62.546,11.333C62.74,11.054 62.541,10.671 62.2,10.671"
|
||||
android:fillColor="#F3F3F3"/>
|
||||
<path
|
||||
android:pathData="M70.26,5.992H65.201C64.855,5.992 64.561,6.244 64.507,6.586L62.461,19.557C62.421,19.812 62.619,20.043 62.877,20.043H65.473C65.714,20.043 65.921,19.868 65.958,19.627L66.539,15.951C66.592,15.608 66.888,15.357 67.233,15.357H68.834C72.166,15.357 74.088,13.744 74.591,10.549C74.818,9.151 74.601,8.053 73.945,7.283C73.227,6.439 71.953,5.992 70.26,5.992M70.844,10.729C70.568,12.545 69.181,12.545 67.84,12.545H67.076L67.613,9.155C67.644,8.95 67.821,8.799 68.028,8.799H68.378C69.291,8.799 70.154,8.799 70.599,9.319C70.864,9.63 70.945,10.092 70.844,10.729"
|
||||
android:fillColor="#F3F3F3"/>
|
||||
<path
|
||||
android:pathData="M85.379,10.67H82.958C82.751,10.67 82.573,10.821 82.542,11.026L82.435,11.704L82.265,11.458C81.741,10.698 80.572,10.442 79.405,10.442C76.729,10.442 74.443,12.47 73.998,15.313C73.767,16.733 74.094,18.089 74.899,19.035C75.639,19.905 76.694,20.267 77.95,20.267C80.107,20.267 81.303,18.881 81.303,18.881L81.195,19.554C81.154,19.811 81.352,20.042 81.612,20.042H83.794C84.138,20.042 84.433,19.791 84.487,19.448L85.797,11.158C85.837,10.902 85.638,10.67 85.379,10.67M82.003,15.385C81.771,16.768 80.671,17.698 79.27,17.698C78.568,17.698 78.004,17.472 77.643,17.045C77.285,16.62 77.15,16.016 77.263,15.342C77.482,13.971 78.598,13.012 79.978,13.012C80.666,13.012 81.224,13.24 81.593,13.671C81.963,14.107 82.11,14.715 82.003,15.385"
|
||||
android:fillColor="#F3F3F3"/>
|
||||
<path
|
||||
android:pathData="M88.235,6.347L86.159,19.556C86.118,19.812 86.316,20.042 86.574,20.042H88.662C89.009,20.042 89.303,19.791 89.356,19.449L91.404,6.478C91.445,6.222 91.247,5.99 90.988,5.99H88.651C88.445,5.99 88.267,6.143 88.235,6.347"
|
||||
android:fillColor="#F3F3F3"/>
|
||||
<path
|
||||
android:pathData="M5.374,22.564L5.761,20.107L4.899,20.086H0.785L3.644,1.956C3.654,1.901 3.682,1.85 3.724,1.814C3.766,1.777 3.821,1.758 3.877,1.758H10.814C13.117,1.758 14.707,2.237 15.537,3.183C15.926,3.627 16.174,4.09 16.293,4.601C16.419,5.136 16.422,5.776 16.299,6.557L16.289,6.613V7.113L16.679,7.334C17.007,7.508 17.267,7.707 17.467,7.935C17.8,8.314 18.015,8.796 18.106,9.367C18.2,9.956 18.169,10.655 18.015,11.448C17.837,12.359 17.55,13.152 17.163,13.802C16.806,14.4 16.352,14.897 15.813,15.281C15.298,15.647 14.686,15.924 13.995,16.101C13.325,16.276 12.561,16.364 11.722,16.364H11.182C10.797,16.364 10.421,16.503 10.127,16.753C9.833,17.007 9.637,17.354 9.577,17.735L9.536,17.955L8.852,22.286L8.822,22.445C8.814,22.495 8.8,22.521 8.779,22.537C8.76,22.553 8.734,22.564 8.708,22.564H5.374V22.564Z"
|
||||
android:fillColor="#253B80"/>
|
||||
<path
|
||||
android:pathData="M17.047,6.671C17.026,6.804 17.003,6.939 16.975,7.078C16.061,11.775 12.931,13.398 8.933,13.398H6.898C6.409,13.398 5.996,13.753 5.92,14.236L4.878,20.845L4.584,22.719C4.534,23.035 4.778,23.32 5.097,23.32H8.708C9.135,23.32 9.498,23.01 9.566,22.588L9.6,22.405L10.281,18.091L10.324,17.855C10.391,17.431 10.755,17.121 11.182,17.121H11.722C15.22,17.121 17.958,15.7 18.758,11.591C19.092,9.874 18.92,8.441 18.035,7.433C17.767,7.129 17.435,6.877 17.047,6.671"
|
||||
android:fillColor="#B6B6B6"/>
|
||||
<path
|
||||
android:pathData="M16.089,6.289C15.949,6.249 15.805,6.211 15.657,6.178C15.509,6.145 15.356,6.116 15.199,6.092C14.651,6.003 14.049,5.96 13.405,5.96H7.967C7.834,5.96 7.706,5.99 7.592,6.046C7.342,6.166 7.155,6.404 7.11,6.695L5.953,14.021L5.919,14.235C5.996,13.752 6.408,13.397 6.897,13.397H8.932C12.93,13.397 16.06,11.774 16.975,7.078C17.003,6.939 17.025,6.803 17.047,6.671C16.815,6.548 16.564,6.443 16.294,6.354C16.227,6.331 16.159,6.31 16.089,6.289"
|
||||
android:fillColor="#E9E9E9"/>
|
||||
<path
|
||||
android:pathData="M7.111,6.695C7.156,6.404 7.343,6.166 7.594,6.047C7.708,5.992 7.835,5.961 7.968,5.961H13.406C14.05,5.961 14.651,6.004 15.2,6.093C15.357,6.117 15.509,6.146 15.658,6.179C15.806,6.212 15.95,6.25 16.09,6.29C16.16,6.311 16.228,6.332 16.296,6.354C16.565,6.443 16.817,6.549 17.048,6.671C17.32,4.935 17.046,3.753 16.107,2.683C15.073,1.504 13.205,1 10.815,1H3.877C3.389,1 2.973,1.355 2.897,1.838L0.007,20.156C-0.049,20.518 0.23,20.845 0.596,20.845H4.879L5.954,14.022L7.111,6.695V6.695Z"
|
||||
android:fillColor="#F3F3F3"/>
|
||||
</vector>
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape android:shape="rectangle" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@color/signal_colorSurface1" />
|
||||
<stroke android:color="@color/signal_colorOutline" android:width="1dp" />
|
||||
<corners android:radius="12dp" />
|
||||
</shape>
|
|
@ -0,0 +1,35 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/dsl_settings_gutter"
|
||||
android:layout_marginEnd="@dimen/dsl_settings_gutter"
|
||||
android:background="@drawable/rounded_outline_12_surface1"
|
||||
tools:viewBindingIgnore="true">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/paypal_text"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="16dp"
|
||||
android:text="@string/PaypalCompleteOrderBottomSheet__payment"
|
||||
android:textAppearance="@style/Signal.Text.BodyLarge"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/paypal_image"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/paypal_image"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:importantForAccessibility="no"
|
||||
android:padding="16dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@drawable/paypal_themed" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -176,6 +176,9 @@
|
|||
<action
|
||||
android:id="@+id/action_paypalPaymentInProgressFragment_to_paypalConfirmationFragment"
|
||||
app:destination="@id/paypalConfirmationFragment" />
|
||||
<action
|
||||
android:id="@+id/action_paypalPaymentInProgressFragment_to_paypalCompleteOrderBottomSheet"
|
||||
app:destination="@id/paypalCompleteOrderBottomSheet" />
|
||||
</dialog>
|
||||
|
||||
<dialog
|
||||
|
@ -191,4 +194,16 @@
|
|||
|
||||
</dialog>
|
||||
|
||||
<dialog
|
||||
android:id="@+id/paypalCompleteOrderBottomSheet"
|
||||
android:name="org.thoughtcrime.securesms.components.settings.app.subscription.donate.paypal.PayPalCompleteOrderBottomSheet"
|
||||
android:label="paypal_complete_order_bottom_sheet"
|
||||
tools:layout="@layout/dsl_settings_bottom_sheet">
|
||||
|
||||
<argument
|
||||
android:name="request"
|
||||
app:argType="org.thoughtcrime.securesms.components.settings.app.subscription.donate.gateway.GatewayRequest"
|
||||
app:nullable="false" />
|
||||
</dialog>
|
||||
|
||||
</navigation>
|
|
@ -157,6 +157,9 @@
|
|||
<action
|
||||
android:id="@+id/action_paypalPaymentInProgressFragment_to_paypalConfirmationFragment"
|
||||
app:destination="@id/paypalConfirmationFragment" />
|
||||
<action
|
||||
android:id="@+id/action_paypalPaymentInProgressFragment_to_paypalCompleteOrderBottomSheet"
|
||||
app:destination="@id/paypalCompleteOrderBottomSheet" />
|
||||
</dialog>
|
||||
|
||||
<dialog
|
||||
|
@ -171,4 +174,16 @@
|
|||
app:nullable="false" />
|
||||
|
||||
</dialog>
|
||||
|
||||
<dialog
|
||||
android:id="@+id/paypalCompleteOrderBottomSheet"
|
||||
android:name="org.thoughtcrime.securesms.components.settings.app.subscription.donate.paypal.PayPalCompleteOrderBottomSheet"
|
||||
android:label="paypal_complete_order_bottom_sheet"
|
||||
tools:layout="@layout/dsl_settings_bottom_sheet">
|
||||
|
||||
<argument
|
||||
android:name="request"
|
||||
app:argType="org.thoughtcrime.securesms.components.settings.app.subscription.donate.gateway.GatewayRequest"
|
||||
app:nullable="false" />
|
||||
</dialog>
|
||||
</navigation>
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<drawable name="paypal_themed">@drawable/paypal_dark</drawable>
|
||||
</resources>
|
|
@ -5447,4 +5447,8 @@
|
|||
|
||||
<!-- EOF -->
|
||||
|
||||
<!-- PaypalCompleteOrderBottomSheet -->
|
||||
<string name="PaypalCompleteOrderBottomSheet__donate">Donate</string>
|
||||
<string name="PaypalCompleteOrderBottomSheet__payment">Payment</string>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -3,4 +3,5 @@
|
|||
<bool name="enable_alarm_manager">true</bool>
|
||||
<bool name="enable_job_service">false</bool>
|
||||
<integer name="image_scale_flip">1</integer>
|
||||
<drawable name="paypal_themed">@drawable/paypal</drawable>
|
||||
</resources>
|
||||
|
|
Ładowanie…
Reference in New Issue