From 3523d2bbcdec1f339096e09126527c018f663049 Mon Sep 17 00:00:00 2001 From: Milan Cerovsky Date: Mon, 3 Feb 2025 20:53:07 +0100 Subject: [PATCH] QR dialog implementation Closes #1731 --- .../zcash/ui/design/component/ZashiQr.kt | 24 ++++-- .../zcash/ui/design/theme/ZcashTheme.kt | 3 +- .../util/AndroidQrCodeImageGenerator.kt | 3 +- .../co/electriccoin/zcash/ui/Navigation.kt | 12 +++ .../qrcode/viewmodel/QrCodeViewModel.kt | 8 +- .../ui/screen/qrdialog/AndroidQrDialog.kt | 37 ++++++++++ .../zcash/ui/screen/qrdialog/QrDialog.kt | 8 ++ .../zcash/ui/screen/qrdialog/QrDialogView.kt | 74 +++++++++++++++++++ .../request/viewmodel/RequestViewModel.kt | 8 +- .../view/SignKeystoneTransactionView.kt | 4 +- .../SignKeystoneTransactionViewModel.kt | 8 +- 11 files changed, 173 insertions(+), 16 deletions(-) create mode 100644 ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/qrdialog/AndroidQrDialog.kt create mode 100644 ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/qrdialog/QrDialog.kt create mode 100644 ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/qrdialog/QrDialogView.kt diff --git a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/ZashiQr.kt b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/ZashiQr.kt index b64f0175b..2d7bc2db9 100644 --- a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/ZashiQr.kt +++ b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/ZashiQr.kt @@ -4,6 +4,7 @@ import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.Image import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.RoundedCornerShape @@ -13,6 +14,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ImageBitmap +import androidx.compose.ui.graphics.isSpecified import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalDensity @@ -34,6 +36,7 @@ fun ZashiQr( modifier: Modifier = Modifier, qrSize: Dp = ZashiQrDefaults.width, colors: QrCodeColors = QrCodeDefaults.colors(), + contentPadding: PaddingValues = QrCodeDefaults.contentPadding() ) { val qrSizePx = with(LocalDensity.current) { qrSize.roundToPx() } val bitmap = getQrCode(state.qrData, qrSizePx, colors) @@ -41,16 +44,21 @@ fun ZashiQr( Surface( modifier = modifier, shape = RoundedCornerShape(ZashiDimensions.Radius.radius4xl), - border = BorderStroke(width = 1.dp, color = ZashiColors.Surfaces.strokePrimary), + border = BorderStroke(width = 1.dp, color = colors.border).takeIf { colors.border.isSpecified }, color = colors.background, ) { Box( - modifier = Modifier.padding(all = 16.dp) + modifier = Modifier.padding(contentPadding) ) { Image( bitmap = bitmap, contentDescription = state.contentDescription?.getValue(), - Modifier.clickable { state.onClick() } + Modifier then + if (state.onClick != null) { + Modifier.clickable { state.onClick.invoke() } + } else { + Modifier + } ) if (state.centerImageResId != null) { Image( @@ -84,19 +92,23 @@ object ZashiQrDefaults { private const val WIDTH_RATIO = 0.66 object QrCodeDefaults { + fun contentPadding() = PaddingValues(16.dp) + @Composable fun colors( background: Color = Color.White orDark ZashiColors.Surfaces.bgPrimary, - foreground: Color = Color.Black orDark Color.White + foreground: Color = Color.Black orDark Color.White, + border: Color = ZashiColors.Surfaces.strokePrimary ) = QrCodeColors( background = background, - foreground = foreground + foreground = foreground, + border = border ) } data class QrState( val qrData: String, val contentDescription: StringResource? = null, - val onClick: () -> Unit = {}, + val onClick: (() -> Unit)? = null, val centerImageResId: Int? = null, ) diff --git a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/theme/ZcashTheme.kt b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/theme/ZcashTheme.kt index 375771aed..763ea47fd 100644 --- a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/theme/ZcashTheme.kt +++ b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/theme/ZcashTheme.kt @@ -36,10 +36,11 @@ import co.electriccoin.zcash.ui.design.theme.typography.ZashiTypographyInternal @Composable fun ZcashTheme( forceDarkMode: Boolean = false, + forceLightMode: Boolean = false, balancesAvailable: Boolean = true, content: @Composable () -> Unit ) { - val useDarkMode = forceDarkMode || isSystemInDarkTheme() + val useDarkMode = !forceLightMode && (forceDarkMode || isSystemInDarkTheme()) val baseColors = if (useDarkMode) DarkColorPalette else LightColorPalette val extendedColors = if (useDarkMode) DarkExtendedColorPalette else LightExtendedColorPalette val zashiColors = if (useDarkMode) DarkZashiColorsInternal else LightZashiColorsInternal diff --git a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/util/AndroidQrCodeImageGenerator.kt b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/util/AndroidQrCodeImageGenerator.kt index d8da0bfb3..cbb848a1d 100644 --- a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/util/AndroidQrCodeImageGenerator.kt +++ b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/util/AndroidQrCodeImageGenerator.kt @@ -30,5 +30,6 @@ private fun BooleanArray.toThemeColorArray(colors: QrCodeColors) = data class QrCodeColors( val background: Color, - val foreground: Color + val foreground: Color, + val border: Color, ) diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/Navigation.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/Navigation.kt index 7c0209cb9..6da24dab3 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/Navigation.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/Navigation.kt @@ -77,6 +77,8 @@ import co.electriccoin.zcash.ui.screen.feedback.WrapFeedback import co.electriccoin.zcash.ui.screen.home.WrapHome import co.electriccoin.zcash.ui.screen.integrations.WrapIntegrations import co.electriccoin.zcash.ui.screen.qrcode.WrapQrCode +import co.electriccoin.zcash.ui.screen.qrdialog.AndroidQrDialog +import co.electriccoin.zcash.ui.screen.qrdialog.QrDialog import co.electriccoin.zcash.ui.screen.receive.model.ReceiveAddressType import co.electriccoin.zcash.ui.screen.request.WrapRequest import co.electriccoin.zcash.ui.screen.reviewtransaction.AndroidReviewTransaction @@ -425,6 +427,16 @@ internal fun MainActivity.Navigation() { composable { AndroidTransactionDetail(it.toRoute()) } + dialog( + dialogProperties = + DialogProperties( + dismissOnBackPress = false, + dismissOnClickOutside = false, + usePlatformDefaultWidth = false + ) + ) { + AndroidQrDialog(it.toRoute()) + } } } diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/qrcode/viewmodel/QrCodeViewModel.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/qrcode/viewmodel/QrCodeViewModel.kt index d168f7425..444b429b6 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/qrcode/viewmodel/QrCodeViewModel.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/qrcode/viewmodel/QrCodeViewModel.kt @@ -21,6 +21,7 @@ import co.electriccoin.zcash.ui.common.usecase.ObserveSelectedWalletAccountUseCa import co.electriccoin.zcash.ui.screen.qrcode.ext.fromReceiveAddressType import co.electriccoin.zcash.ui.screen.qrcode.model.QrCodeState import co.electriccoin.zcash.ui.screen.qrcode.model.QrCodeType +import co.electriccoin.zcash.ui.screen.qrdialog.QrDialog import co.electriccoin.zcash.ui.screen.receive.model.ReceiveAddressType import co.electriccoin.zcash.ui.util.FileShareUtil import kotlinx.coroutines.Dispatchers @@ -58,8 +59,11 @@ class QrCodeViewModel( onAddressCopy = { address -> onAddressCopyClick(address) }, onQrCodeShare = { onQrCodeShareClick(it, versionInfo) }, onQrCodeClick = { - // TODO [#1731]: Allow QR codes colors switching - // TODO [#1731]: https://github.com/Electric-Coin-Company/zashi-android/issues/1731 + navigationRouter.forward( + QrDialog( + qr = walletAddress.address, + ) + ) }, onBack = ::onBack, qrCodeType = diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/qrdialog/AndroidQrDialog.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/qrdialog/AndroidQrDialog.kt new file mode 100644 index 000000000..f1fb9ed02 --- /dev/null +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/qrdialog/AndroidQrDialog.kt @@ -0,0 +1,37 @@ +package co.electriccoin.zcash.ui.screen.qrdialog + +import androidx.activity.compose.BackHandler +import androidx.compose.runtime.Composable +import androidx.compose.runtime.SideEffect +import androidx.compose.ui.platform.LocalView +import androidx.compose.ui.window.DialogWindowProvider +import co.electriccoin.zcash.ui.NavigationRouter +import co.electriccoin.zcash.ui.design.component.QrState +import org.koin.compose.koinInject + +@Composable +fun AndroidQrDialog(arg: QrDialog) { + val navigationRouter = koinInject() + + val parent = LocalView.current.parent + + SideEffect { + (parent as? DialogWindowProvider)?.window?.setDimAmount(DIALOG_DIM) + } + + BackHandler { + navigationRouter.back() + } + + QrDialogView( + state = + QrState( + qrData = arg.qr, + ), + onBack = { + navigationRouter.back() + } + ) +} + +private const val DIALOG_DIM = .8f diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/qrdialog/QrDialog.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/qrdialog/QrDialog.kt new file mode 100644 index 000000000..af8409cd2 --- /dev/null +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/qrdialog/QrDialog.kt @@ -0,0 +1,8 @@ +package co.electriccoin.zcash.ui.screen.qrdialog + +import kotlinx.serialization.Serializable + +@Serializable +data class QrDialog( + val qr: String, +) diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/qrdialog/QrDialogView.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/qrdialog/QrDialogView.kt new file mode 100644 index 000000000..cbe788918 --- /dev/null +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/qrdialog/QrDialogView.kt @@ -0,0 +1,74 @@ +package co.electriccoin.zcash.ui.screen.qrdialog + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalConfiguration +import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.Dialog +import co.electriccoin.zcash.ui.design.component.QrCodeDefaults +import co.electriccoin.zcash.ui.design.component.QrState +import co.electriccoin.zcash.ui.design.component.ZashiQr +import co.electriccoin.zcash.ui.design.newcomponent.PreviewScreens +import co.electriccoin.zcash.ui.design.theme.ZcashTheme + +@Composable +fun QrDialogView( + state: QrState, + onBack: () -> Unit +) { + Box( + modifier = + Modifier + .fillMaxSize() + .clickable( + interactionSource = remember { MutableInteractionSource() }, + indication = null, + onClick = onBack + ) + .padding(start = 16.dp, end = 16.dp, bottom = 64.dp) + ) { + ZashiQr( + modifier = + Modifier + .fillMaxWidth() + .align(Alignment.Center), + state = state, + qrSize = LocalConfiguration.current.screenWidthDp.dp - 44.dp, + contentPadding = PaddingValues(6.dp), + colors = + QrCodeDefaults.colors( + background = Color.White, + foreground = Color.Black, + border = Color.Unspecified + ) + ) + } +} + +@PreviewScreens +@Composable +private fun Preview() = + ZcashTheme { + Dialog( + onDismissRequest = {} + ) { + QrDialogView( + state = + QrState( + qrData = "test", + centerImageResId = co.electriccoin.zcash.ui.design.R.drawable.ic_item_keystone_qr + ), + onBack = {} + ) + } + } diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/request/viewmodel/RequestViewModel.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/request/viewmodel/RequestViewModel.kt index 897ec73e1..02ea95ac0 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/request/viewmodel/RequestViewModel.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/request/viewmodel/RequestViewModel.kt @@ -24,6 +24,7 @@ import co.electriccoin.zcash.ui.design.util.AndroidQrCodeImageGenerator import co.electriccoin.zcash.ui.design.util.JvmQrCodeGenerator import co.electriccoin.zcash.ui.design.util.QrCodeColors import co.electriccoin.zcash.ui.screen.qrcode.ext.fromReceiveAddressType +import co.electriccoin.zcash.ui.screen.qrdialog.QrDialog import co.electriccoin.zcash.ui.screen.receive.model.ReceiveAddressType import co.electriccoin.zcash.ui.screen.request.ext.convertToDouble import co.electriccoin.zcash.ui.screen.request.model.AmountState @@ -141,8 +142,11 @@ class RequestViewModel( walletAddress = walletAddress, request = request, onQrCodeClick = { - // TODO [#1731]: Allow QR codes colors switching - // TODO [#1731]: https://github.com/Electric-Coin-Company/zashi-android/issues/1731 + navigationRouter.forward( + QrDialog( + qr = request.qrCodeState.requestUri + ) + ) }, onQrCodeShare = { colors, pixels, uri -> onShareQrCode( diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/signkeystonetransaction/view/SignKeystoneTransactionView.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/signkeystonetransaction/view/SignKeystoneTransactionView.kt index e3dd50f45..49d586cd6 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/signkeystonetransaction/view/SignKeystoneTransactionView.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/signkeystonetransaction/view/SignKeystoneTransactionView.kt @@ -23,6 +23,7 @@ import androidx.compose.ui.unit.dp import co.electriccoin.zcash.ui.design.R import co.electriccoin.zcash.ui.design.component.BlankBgScaffold import co.electriccoin.zcash.ui.design.component.ButtonState +import co.electriccoin.zcash.ui.design.component.QrCodeDefaults import co.electriccoin.zcash.ui.design.component.ZashiBadge import co.electriccoin.zcash.ui.design.component.ZashiBadgeDefaults import co.electriccoin.zcash.ui.design.component.ZashiButton @@ -35,7 +36,6 @@ import co.electriccoin.zcash.ui.design.newcomponent.PreviewScreens import co.electriccoin.zcash.ui.design.theme.ZcashTheme import co.electriccoin.zcash.ui.design.theme.colors.ZashiColors import co.electriccoin.zcash.ui.design.theme.typography.ZashiTypography -import co.electriccoin.zcash.ui.design.util.QrCodeColors import co.electriccoin.zcash.ui.design.util.getValue import co.electriccoin.zcash.ui.design.util.scaffoldPadding import co.electriccoin.zcash.ui.design.util.stringRes @@ -132,7 +132,7 @@ private fun ColumnScope.QrContent(ksState: SignKeystoneTransactionState) { state = ksState.toQrState(), modifier = Modifier.align(CenterHorizontally), colors = - QrCodeColors( + QrCodeDefaults.colors( background = Color.White, foreground = Color.Black ) diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/signkeystonetransaction/viewmodel/SignKeystoneTransactionViewModel.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/signkeystonetransaction/viewmodel/SignKeystoneTransactionViewModel.kt index e312bd7ef..ff44dbc77 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/signkeystonetransaction/viewmodel/SignKeystoneTransactionViewModel.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/signkeystonetransaction/viewmodel/SignKeystoneTransactionViewModel.kt @@ -15,6 +15,7 @@ import co.electriccoin.zcash.ui.common.usecase.SharePCZTUseCase import co.electriccoin.zcash.ui.design.component.ButtonState import co.electriccoin.zcash.ui.design.util.stringRes import co.electriccoin.zcash.ui.screen.addressbook.viewmodel.ADDRESS_MAX_LENGTH +import co.electriccoin.zcash.ui.screen.qrdialog.QrDialog import co.electriccoin.zcash.ui.screen.scankeystone.ScanKeystonePCZTRequest import co.electriccoin.zcash.ui.screen.signkeystonetransaction.state.SignKeystoneTransactionState import co.electriccoin.zcash.ui.screen.signkeystonetransaction.state.ZashiAccountInfoListItemState @@ -77,8 +78,11 @@ class SignKeystoneTransactionViewModel( ).takeIf { BuildConfig.DEBUG }, onBack = ::onBack, onQrCodeClick = { - // TODO [#1731]: Allow QR codes colors switching - // TODO [#1731]: https://github.com/Electric-Coin-Company/zashi-android/issues/1731 + navigationRouter.forward( + QrDialog( + qr = qrData.orEmpty(), + ) + ) }, ) }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT), null)