From 0678b266284d2f3affe1d47bdc28f5e65d88d831 Mon Sep 17 00:00:00 2001 From: Mohsen Mirhoseini Date: Mon, 30 Dec 2024 12:35:34 +0100 Subject: [PATCH] [#109] Handle potential init exceptions and fallback --- README.md | 9 +++ .../example/composepaybutton/MainActivity.kt | 7 ++ .../java/com/google/pay/button/PayButton.kt | 72 ++++++++++++------- 3 files changed, 61 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 2c5c221..9ef9078 100644 --- a/README.md +++ b/README.md @@ -86,3 +86,12 @@ class MainActivity : ComponentActivity() { } } ``` + +## Error handling and Fallback UI + +The `PayButton` composable wraps the underlying `PayButton` Android View, which may encounter errors during initialization. To handle these situations, the `PayButton` composable provides: + +* **`onError` Callback:** Invoked when an error occurs during the button's initialization. This callback receives the `Throwable` that caused the error, allowing you to log it or take other actions. +* **`fallbackUi` Composable:** An optional composable function that is displayed in place of the button if an error occurs. If not provided, nothing will be displayed in case of error. + +This mechanism ensures that your app can gracefully handle potential issues with the `PayButton` and provide a fallback experience to the user. diff --git a/app/src/main/java/com/example/composepaybutton/MainActivity.kt b/app/src/main/java/com/example/composepaybutton/MainActivity.kt index d830c35..78769cb 100644 --- a/app/src/main/java/com/example/composepaybutton/MainActivity.kt +++ b/app/src/main/java/com/example/composepaybutton/MainActivity.kt @@ -24,6 +24,7 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.width +import androidx.compose.material.Button import androidx.compose.material.Divider import androidx.compose.material.Text import androidx.compose.ui.Alignment @@ -103,6 +104,12 @@ class MainActivity : ComponentActivity() { // Disabled buttons PayButton(onClick = onClick, allowedPaymentMethods = allowedPaymentMethods, type = ButtonType.Checkout, enabled = false) PayButton(onClick = onClick, allowedPaymentMethods = allowedPaymentMethods, type = ButtonType.Checkout, theme = ButtonTheme.Light, enabled = false) + + Divider(thickness = 1.dp, color = Color.LightGray) + Text("Fallback UI") + + // Fallback UI in case of init failure + PayButton(onClick = onClick, allowedPaymentMethods = allowedPaymentMethods, onError = { println("Error: $it") }) { Button(onClick = onClick) { Text("Fallback UI") }} } } } diff --git a/compose-pay-button/src/main/java/com/google/pay/button/PayButton.kt b/compose-pay-button/src/main/java/com/google/pay/button/PayButton.kt index dee3c9a..c493fd0 100644 --- a/compose-pay-button/src/main/java/com/google/pay/button/PayButton.kt +++ b/compose-pay-button/src/main/java/com/google/pay/button/PayButton.kt @@ -17,15 +17,18 @@ package com.google.pay.button import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidView - +import com.google.android.gms.wallet.button.ButtonConstants import com.google.android.gms.wallet.button.ButtonOptions import com.google.android.gms.wallet.button.PayButton as GmsPayButton -import com.google.android.gms.wallet.button.ButtonConstants enum class ButtonTheme(val value: Int) { Dark(ButtonConstants.ButtonTheme.DARK), @@ -55,35 +58,50 @@ fun PayButton( type: ButtonType = ButtonType.Buy, radius: Dp = 100.dp, enabled: Boolean = true, + onError: (Throwable) -> Unit = {}, + fallbackUi: @Composable (() -> Unit)? = null, ) { + var showFallback by remember { mutableStateOf(false) } + val radiusPixelValue = with(LocalDensity.current) { radius.toPx().toInt() } - AndroidView( - modifier = modifier, - factory = { context -> - GmsPayButton(context).apply { - this.initialize( - ButtonOptions.newBuilder() - .setButtonTheme(theme.value) - .setButtonType(type.value) - .setCornerRadius(radiusPixelValue) - .setAllowedPaymentMethods(allowedPaymentMethods) - .build() - ) - } - }, - update = { button -> - button.apply { - alpha = if (enabled) FULL_ALPHA else HALF_ALPHA - isEnabled = enabled + if (!showFallback) { + AndroidView( + modifier = modifier, + factory = { context -> + GmsPayButton(context).apply { + kotlin.runCatching { + this.initialize( + ButtonOptions.newBuilder() + .setButtonTheme(theme.value) + .setButtonType(type.value) + .setCornerRadius(radiusPixelValue) + .setAllowedPaymentMethods(allowedPaymentMethods) + .build() + ) + }.onFailure { + onError(it) + showFallback = true + } + } + }, + update = { button -> + if (!showFallback) { + button.apply { + alpha = if (enabled) FULL_ALPHA else HALF_ALPHA + isEnabled = enabled - if (enabled) { - setOnClickListener { onClick() } - } else { - setOnClickListener(null) + if (enabled) { + setOnClickListener { onClick() } + } else { + setOnClickListener(null) + } + } } } - } - ) -} \ No newline at end of file + ) + } else { + fallbackUi?.invoke() + } +}