QR code image logic refactoring (#1732)

* QR code image logic refactor

The QR code image logic of the `QrCode` and `Request` screens has been refactored to work upon the newer `ZashiQr` component

* Refactor values into state

* Changelog update
This commit is contained in:
Honza Rychnovský 2025-01-21 16:32:34 -05:00 committed by GitHub
parent db983c692f
commit 603178fd67
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 159 additions and 236 deletions

View File

@ -6,6 +6,10 @@ and this application adheres to [Semantic Versioning](https://semver.org/spec/v2
## [Unreleased]
### Changed
- The QR code image logic of the `QrCode`, `Request`, and `SignTransaction` screens has been refactored to work
with the newer `ZashiQr` component
### Fixed
- The Disconnected popup trigger when the app is backgrounded has been fixed

View File

@ -2,16 +2,21 @@ package co.electriccoin.zcash.ui.design.component
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.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
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.vector.ImageVector
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import co.electriccoin.zcash.ui.design.theme.colors.ZashiColors
@ -19,31 +24,44 @@ import co.electriccoin.zcash.ui.design.theme.dimensions.ZashiDimensions
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.design.util.StringResource
import co.electriccoin.zcash.ui.design.util.getValue
import co.electriccoin.zcash.ui.design.util.orDark
@Composable
fun ZashiQr(
qrData: String,
state: QrState,
modifier: Modifier = Modifier,
qrSize: Dp = ZashiQrDefaults.width,
colors: QrCodeColors = QrCodeDefaults.colors()
colors: QrCodeColors = QrCodeDefaults.colors(),
) {
val qrSizePx = with(LocalDensity.current) { qrSize.roundToPx() }
val bitmap = getQrCode(qrData, qrSizePx, colors)
val bitmap = getQrCode(state.qrData, qrSizePx, colors)
Surface(
modifier = modifier,
shape = RoundedCornerShape(ZashiDimensions.Radius.radius4xl),
border = BorderStroke(width = 1.dp, color = ZashiColors.Surfaces.strokePrimary),
color = ZashiColors.Surfaces.bgPrimary
color = ZashiColors.Surfaces.bgPrimary,
) {
Box(
modifier = Modifier.padding(all = 12.dp)
) {
Image(
bitmap = bitmap,
contentDescription = null,
contentDescription = state.contentDescription?.getValue(),
Modifier.clickable { state.onClick() }
)
if (state.centerImageResId != null) {
Image(
modifier =
Modifier
.size(64.dp)
.align(Alignment.Center),
imageVector = ImageVector.vectorResource(state.centerImageResId),
contentDescription = null,
)
}
}
}
}
@ -75,3 +93,10 @@ object QrCodeDefaults {
foreground = foreground
)
}
data class QrState(
val qrData: String,
val contentDescription: StringResource? = null,
val onClick: () -> Unit = {},
val centerImageResId: Int? = null,
)

View File

@ -541,20 +541,7 @@ fun NavHostController.popBackStackJustOnce(currentRouteToBePopped: String) {
object NavigationArguments {
const val SEND_SCAN_RECIPIENT_ADDRESS = "send_scan_recipient_address"
const val SEND_SCAN_ZIP_321_URI = "send_scan_zip_321_uri"
const val SEND_CONFIRM_RECIPIENT_ADDRESS = "send_confirm_recipient_address"
const val SEND_CONFIRM_AMOUNT = "send_confirm_amount"
const val SEND_CONFIRM_MEMO = "send_confirm_memo"
const val SEND_CONFIRM_PROPOSAL = "send_confirm_proposal"
const val SEND_CONFIRM_INITIAL_STAGE = "send_confirm_initial_stage"
const val MULTIPLE_SUBMISSION_CLEAR_FORM = "multiple_submission_clear_form"
const val PAYMENT_REQUEST_ADDRESS = "payment_request_address"
const val PAYMENT_REQUEST_AMOUNT = "payment_request_amount"
const val PAYMENT_REQUEST_MEMO = "payment_request_memo"
const val PAYMENT_REQUEST_PROPOSAL = "payment_request_proposal"
const val PAYMENT_REQUEST_URI = "payment_request_uri"
}
object NavigationTargets {

View File

@ -5,7 +5,6 @@ package co.electriccoin.zcash.ui.screen.exchangerate.widget
import androidx.compose.animation.animateContentSize
import androidx.compose.animation.core.MutableTransitionState
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.basicMarquee
import androidx.compose.foundation.layout.Column
@ -93,7 +92,6 @@ fun StyledExchangeBalance(
}
@Suppress("LongParameterList", "LongMethod")
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun ExchangeAvailableRateLabelInternal(
style: TextStyle,

View File

@ -2,8 +2,10 @@ package co.electriccoin.zcash.ui.screen.qrcode.model
import androidx.compose.ui.graphics.ImageBitmap
import cash.z.ecc.android.sdk.model.WalletAddress
import co.electriccoin.zcash.ui.design.component.QrState
import co.electriccoin.zcash.ui.design.util.StringResource
internal sealed class QrCodeState {
sealed class QrCodeState {
data object Loading : QrCodeState()
data class Prepared(
@ -11,8 +13,19 @@ internal sealed class QrCodeState {
val walletAddress: WalletAddress,
val onAddressCopy: (String) -> Unit,
val onQrCodeShare: (ImageBitmap) -> Unit,
val onQrCodeClick: () -> Unit,
val onBack: () -> Unit,
) : QrCodeState()
) : QrCodeState() {
fun toQrState(
contentDescription: StringResource? = null,
centerImageResId: Int? = null
) = QrState(
qrData = walletAddress.address,
onClick = onQrCodeClick,
contentDescription = contentDescription,
centerImageResId = centerImageResId
)
}
}
enum class QrCodeType {

View File

@ -3,15 +3,10 @@
package co.electriccoin.zcash.ui.screen.qrcode.view
import androidx.compose.animation.animateContentSize
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.Spacer
@ -21,7 +16,6 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.IconButton
import androidx.compose.material3.SnackbarHost
@ -33,7 +27,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Alignment.Companion.CenterHorizontally
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.platform.LocalDensity
@ -55,15 +49,16 @@ import co.electriccoin.zcash.ui.design.component.ZashiBadgeColors
import co.electriccoin.zcash.ui.design.component.ZashiBottomBar
import co.electriccoin.zcash.ui.design.component.ZashiButton
import co.electriccoin.zcash.ui.design.component.ZashiButtonDefaults
import co.electriccoin.zcash.ui.design.component.ZashiQr
import co.electriccoin.zcash.ui.design.component.ZashiSmallTopAppBar
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.dimensions.ZashiDimensions
import co.electriccoin.zcash.ui.design.theme.typography.ZashiTypography
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.design.util.stringRes
import co.electriccoin.zcash.ui.screen.qrcode.model.QrCodeState
import co.electriccoin.zcash.ui.screen.qrcode.model.QrCodeType
import kotlinx.coroutines.runBlocking
@ -91,6 +86,7 @@ private fun ZashiPreview() =
walletAddress = runBlocking { WalletAddressFixture.unified() },
onAddressCopy = {},
onQrCodeShare = {},
onQrCodeClick = {},
onBack = {},
),
snackbarHostState = SnackbarHostState(),
@ -109,6 +105,7 @@ private fun KeystonePreview() =
walletAddress = runBlocking { WalletAddressFixture.unified() },
onAddressCopy = {},
onQrCodeShare = {},
onQrCodeClick = {},
onBack = {},
),
snackbarHostState = SnackbarHostState(),
@ -154,10 +151,7 @@ internal fun QrCodeView(
}
) { paddingValues ->
QrCodeContents(
qrCodeType = state.qrCodeType,
walletAddress = state.walletAddress,
onAddressCopy = state.onAddressCopy,
onQrCodeShare = state.onQrCodeShare,
state = state,
modifier =
Modifier.padding(
top = paddingValues.calculateTopPadding(),
@ -239,10 +233,7 @@ private fun QrCodeBottomBar(
@Composable
private fun QrCodeContents(
qrCodeType: QrCodeType,
walletAddress: WalletAddress,
onAddressCopy: (String) -> Unit,
onQrCodeShare: (ImageBitmap) -> Unit,
state: QrCodeState.Prepared,
modifier: Modifier = Modifier,
) {
Column(
@ -254,16 +245,16 @@ private fun QrCodeContents(
) {
Spacer(Modifier.height(ZcashTheme.dimens.spacingDefault))
when (walletAddress) {
when (state.walletAddress) {
// We use the same design for the Sapling address for the Testnet app variant
is WalletAddress.Unified, is WalletAddress.Sapling -> {
UnifiedQrCodePanel(qrCodeType, walletAddress, onAddressCopy, onQrCodeShare)
UnifiedQrCodePanel(state)
}
is WalletAddress.Transparent -> {
TransparentQrCodePanel(qrCodeType, walletAddress, onAddressCopy, onQrCodeShare)
TransparentQrCodePanel(state)
}
else -> {
error("Unsupported address type: $walletAddress")
error("Unsupported address type: ${state.walletAddress}")
}
}
}
@ -272,10 +263,7 @@ private fun QrCodeContents(
@Composable
@Suppress("LongMethod")
fun UnifiedQrCodePanel(
qrCodeType: QrCodeType,
walletAddress: WalletAddress,
onAddressCopy: (String) -> Unit,
onQrCodeShare: (ImageBitmap) -> Unit,
state: QrCodeState.Prepared,
modifier: Modifier = Modifier
) {
var expandedAddress by rememberSaveable { mutableStateOf(false) }
@ -284,15 +272,13 @@ fun UnifiedQrCodePanel(
modifier =
modifier
.padding(vertical = ZcashTheme.dimens.spacingDefault),
horizontalAlignment = Alignment.CenterHorizontally
horizontalAlignment = CenterHorizontally
) {
QrCode(
walletAddress = walletAddress,
onQrImageShare = onQrCodeShare,
preparedState = state,
modifier =
Modifier
.padding(horizontal = 24.dp),
qrCodeType = qrCodeType,
)
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingUpLarge))
@ -312,18 +298,18 @@ fun UnifiedQrCodePanel(
Text(
text =
when (walletAddress) {
when (state.walletAddress) {
is WalletAddress.Unified ->
when (qrCodeType) {
when (state.qrCodeType) {
QrCodeType.ZASHI -> stringResource(R.string.qr_code_wallet_address_shielded)
QrCodeType.KEYSTONE -> stringResource(R.string.qr_code_wallet_address_shielded_keystone)
}
is WalletAddress.Sapling ->
when (qrCodeType) {
when (state.qrCodeType) {
QrCodeType.ZASHI -> stringResource(id = R.string.qr_code_wallet_address_sapling)
QrCodeType.KEYSTONE -> stringResource(id = R.string.qr_code_wallet_address_sapling_keystone)
}
else -> error("Unsupported address type: $walletAddress")
else -> error("Unsupported address type: ${state.walletAddress}")
},
color = ZashiColors.Text.textPrimary,
style = ZashiTypography.textXl,
@ -335,7 +321,7 @@ fun UnifiedQrCodePanel(
@OptIn(ExperimentalFoundationApi::class)
Text(
text = walletAddress.address,
text = state.walletAddress.address,
color = ZashiColors.Text.textTertiary,
style = ZashiTypography.textSm,
textAlign = TextAlign.Center,
@ -353,7 +339,7 @@ fun UnifiedQrCodePanel(
indication = null,
interactionSource = remember { MutableInteractionSource() },
onClick = { expandedAddress = !expandedAddress },
onLongClick = { onAddressCopy(walletAddress.address) }
onLongClick = { state.onAddressCopy(state.walletAddress.address) }
)
)
}
@ -362,10 +348,7 @@ fun UnifiedQrCodePanel(
@Composable
@Suppress("LongMethod")
fun TransparentQrCodePanel(
qrCodeType: QrCodeType,
walletAddress: WalletAddress,
onAddressCopy: (String) -> Unit,
onQrCodeShare: (ImageBitmap) -> Unit,
state: QrCodeState.Prepared,
modifier: Modifier = Modifier
) {
var expandedAddress by rememberSaveable { mutableStateOf(false) }
@ -374,15 +357,13 @@ fun TransparentQrCodePanel(
modifier =
modifier
.padding(vertical = ZcashTheme.dimens.spacingDefault),
horizontalAlignment = Alignment.CenterHorizontally
horizontalAlignment = CenterHorizontally
) {
QrCode(
walletAddress = walletAddress,
onQrImageShare = onQrCodeShare,
preparedState = state,
modifier =
Modifier
.padding(horizontal = 24.dp),
qrCodeType = qrCodeType,
)
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingUpLarge))
@ -412,7 +393,7 @@ fun TransparentQrCodePanel(
@OptIn(ExperimentalFoundationApi::class)
Text(
text = walletAddress.address,
text = state.walletAddress.address,
color = ZashiColors.Text.textTertiary,
style = ZashiTypography.textSm,
textAlign = TextAlign.Center,
@ -430,7 +411,7 @@ fun TransparentQrCodePanel(
indication = null,
interactionSource = remember { MutableInteractionSource() },
onClick = { expandedAddress = !expandedAddress },
onLongClick = { onAddressCopy(walletAddress.address) }
onLongClick = { state.onAddressCopy(state.walletAddress.address) }
)
)
}
@ -438,51 +419,28 @@ fun TransparentQrCodePanel(
@Composable
private fun ColumnScope.QrCode(
qrCodeType: QrCodeType,
walletAddress: WalletAddress,
onQrImageShare: (ImageBitmap) -> Unit,
preparedState: QrCodeState.Prepared,
modifier: Modifier = Modifier
) {
val sizePixels = with(LocalDensity.current) { DEFAULT_QR_CODE_SIZE.toPx() }.roundToInt()
val colors = QrCodeDefaults.colors()
val qrCodeImage =
remember {
qrCodeForAddress(
address = walletAddress.address,
size = sizePixels,
colors = colors
)
}
QrCode(
qrCodeImage = qrCodeImage,
onQrImageBitmapShare = onQrImageShare,
contentDescription =
stringResource(
when (walletAddress) {
is WalletAddress.Unified -> R.string.qr_code_unified_content_description
is WalletAddress.Sapling -> R.string.qr_code_sapling_content_description
is WalletAddress.Transparent -> R.string.qr_code_transparent_content_description
else -> error("Unsupported address type: $walletAddress")
}
ZashiQr(
state =
preparedState.toQrState(
contentDescription =
stringRes(
when (preparedState.walletAddress) {
is WalletAddress.Unified -> R.string.qr_code_unified_content_description
is WalletAddress.Sapling -> R.string.qr_code_sapling_content_description
is WalletAddress.Transparent -> R.string.qr_code_transparent_content_description
else -> error("Unsupported address type: ${preparedState.walletAddress}")
}
),
centerImageResId =
when (preparedState.qrCodeType) {
QrCodeType.ZASHI -> R.drawable.logo_zec_fill_stroke
QrCodeType.KEYSTONE -> co.electriccoin.zcash.ui.design.R.drawable.ic_item_keystone_qr
}
),
modifier =
modifier
.align(Alignment.CenterHorizontally)
.border(
border =
BorderStroke(
width = 1.dp,
color = ZashiColors.Surfaces.strokePrimary
),
shape = RoundedCornerShape(ZashiDimensions.Radius.radius4xl)
)
.background(
color = ZashiColors.Surfaces.bgPrimary,
shape = RoundedCornerShape(ZashiDimensions.Radius.radius4xl)
)
.padding(all = 12.dp),
qrCodeType = qrCodeType
modifier = modifier.align(CenterHorizontally),
)
}
@ -502,45 +460,4 @@ private fun qrCodeForAddress(
return AndroidQrCodeImageGenerator.generate(qrCodePixelArray, size, colors)
}
@Composable
private fun QrCode(
qrCodeType: QrCodeType,
contentDescription: String,
qrCodeImage: ImageBitmap,
onQrImageBitmapShare: (ImageBitmap) -> Unit,
modifier: Modifier = Modifier,
) {
Box(
contentAlignment = Alignment.Center,
modifier =
Modifier
.clickable(
indication = null,
interactionSource = remember { MutableInteractionSource() },
onClick = { onQrImageBitmapShare(qrCodeImage) },
)
.then(modifier)
) {
Image(
bitmap = qrCodeImage,
contentDescription = contentDescription,
)
Image(
modifier = Modifier.size(64.dp),
painter =
when (qrCodeType) {
QrCodeType.ZASHI -> painterResource(id = R.drawable.logo_zec_fill_stroke)
QrCodeType.KEYSTONE ->
painterResource(
id =
co.electriccoin.zcash.ui.design.R.drawable
.ic_item_keystone_qr
)
},
contentDescription = contentDescription,
)
}
}
private val DEFAULT_QR_CODE_SIZE = 320.dp

View File

@ -57,6 +57,10 @@ class QrCodeViewModel(
walletAddress = walletAddress,
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
},
onBack = ::onBack,
qrCodeType =
when (account) {

View File

@ -5,7 +5,9 @@ import cash.z.ecc.android.sdk.model.MonetarySeparators
import cash.z.ecc.android.sdk.model.WalletAddress
import cash.z.ecc.sdk.type.ZcashCurrency
import co.electriccoin.zcash.ui.common.wallet.ExchangeRateState
import co.electriccoin.zcash.ui.design.component.QrState
import co.electriccoin.zcash.ui.design.util.QrCodeColors
import co.electriccoin.zcash.ui.design.util.StringResource
internal sealed class RequestState {
data object Loading : RequestState()
@ -37,10 +39,21 @@ internal sealed class RequestState {
val icon: Int,
val request: Request,
val walletAddress: WalletAddress,
val onQrCodeClick: () -> Unit,
val onQrCodeShare: (ImageBitmap) -> Unit,
val onQrCodeGenerate: (pixels: Int, colors: QrCodeColors) -> Unit,
override val onBack: () -> Unit,
val onClose: () -> Unit,
val zcashCurrency: ZcashCurrency,
) : Prepared(onBack)
) : Prepared(onBack) {
fun toQrState(
contentDescription: StringResource? = null,
centerImageResId: Int? = null,
) = QrState(
qrData = walletAddress.address,
onClick = onQrCodeClick,
contentDescription = contentDescription,
centerImageResId = centerImageResId
)
}
}

View File

@ -1,28 +1,18 @@
package co.electriccoin.zcash.ui.screen.request.view
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Alignment.Companion.CenterHorizontally
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.SpanStyle
@ -32,16 +22,14 @@ import androidx.compose.ui.text.withStyle
import androidx.compose.ui.unit.dp
import cash.z.ecc.android.sdk.model.WalletAddress
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.design.component.CircularScreenProgressIndicator
import co.electriccoin.zcash.ui.design.component.QrCodeDefaults
import co.electriccoin.zcash.ui.design.component.ZashiBadge
import co.electriccoin.zcash.ui.design.component.ZashiBadgeColors
import co.electriccoin.zcash.ui.design.component.ZashiQr
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
import co.electriccoin.zcash.ui.design.theme.colors.ZashiColors
import co.electriccoin.zcash.ui.design.theme.dimensions.ZashiDimensions
import co.electriccoin.zcash.ui.design.theme.typography.ZashiTypography
import co.electriccoin.zcash.ui.design.util.stringRes
import co.electriccoin.zcash.ui.screen.request.model.RequestState
import kotlin.math.roundToInt
@Composable
internal fun RequestQrCodeView(
@ -96,7 +84,7 @@ internal fun RequestQrCodeView(
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingBig))
QrCode(
state = state,
requestState = state,
modifier = Modifier.padding(horizontal = 24.dp),
)
@ -106,71 +94,19 @@ internal fun RequestQrCodeView(
@Composable
private fun ColumnScope.QrCode(
state: RequestState.QrCode,
requestState: RequestState.QrCode,
modifier: Modifier = Modifier
) {
val sizePixels = with(LocalDensity.current) { DEFAULT_QR_CODE_SIZE.toPx() }.roundToInt()
if (state.request.qrCodeState.bitmap == null) {
val colors = QrCodeDefaults.colors()
state.onQrCodeGenerate(sizePixels, colors)
}
QrCode(
state = state,
contentDescription = stringResource(id = R.string.request_qr_code_content_description),
modifier =
modifier
.align(Alignment.CenterHorizontally)
.border(
border =
BorderStroke(
width = 1.dp,
color = ZashiColors.Surfaces.strokePrimary
),
shape = RoundedCornerShape(ZashiDimensions.Radius.radius4xl)
)
.background(
color = ZashiColors.Surfaces.bgPrimary,
shape = RoundedCornerShape(ZashiDimensions.Radius.radius4xl)
)
.padding(all = 12.dp)
ZashiQr(
state =
requestState.toQrState(
contentDescription = stringRes(R.string.request_qr_code_content_description),
centerImageResId = requestState.icon
),
modifier = modifier.align(CenterHorizontally),
)
}
@Composable
private fun QrCode(
state: RequestState.QrCode,
contentDescription: String,
modifier: Modifier = Modifier,
) {
Box(
contentAlignment = Alignment.Center,
modifier =
Modifier
.clickable(
indication = null,
interactionSource = remember { MutableInteractionSource() },
onClick = { state.request.qrCodeState.bitmap?.let { state.onQrCodeShare(it) } },
)
.then(modifier)
) {
if (state.request.qrCodeState.bitmap == null) {
CircularScreenProgressIndicator()
} else {
Image(
bitmap = state.request.qrCodeState.bitmap,
contentDescription = contentDescription,
)
Image(
modifier = Modifier.size(64.dp),
painter = painterResource(id = state.icon),
contentDescription = contentDescription,
)
}
}
}
@Composable
private fun RequestQrCodeZecAmountView(
state: RequestState.QrCode,
@ -196,5 +132,3 @@ private fun RequestQrCodeZecAmountView(
modifier = modifier
)
}
private val DEFAULT_QR_CODE_SIZE = 320.dp

View File

@ -144,6 +144,10 @@ class RequestViewModel(
colors = colors,
)
},
onQrCodeClick = {
// TODO [#1731]: Allow QR codes colors switching
// TODO [#1731]: https://github.com/Electric-Coin-Company/zashi-android/issues/1731
},
onQrCodeShare = { onRequestQrCodeShare(it, shareImageBitmap) },
onBack = ::onBack,
onClose = ::onClose,

View File

@ -2,17 +2,32 @@ package co.electriccoin.zcash.ui.screen.signkeystonetransaction.state
import androidx.annotation.DrawableRes
import co.electriccoin.zcash.ui.design.component.ButtonState
import co.electriccoin.zcash.ui.design.component.QrState
import co.electriccoin.zcash.ui.design.util.StringResource
data class SignKeystoneTransactionState(
val onBack: () -> Unit,
val onQrCodeClick: () -> Unit,
val accountInfo: ZashiAccountInfoListItemState,
val qrData: String?,
val generateNextQrCode: () -> Unit,
val shareButton: ButtonState?,
val positiveButton: ButtonState,
val negativeButton: ButtonState,
)
) {
fun toQrState(
contentDescription: StringResource? = null,
centerImageResId: Int? = null,
): QrState {
requireNotNull(qrData) { "The QR code data needs to be set at this point" }
return QrState(
qrData = qrData,
onClick = onQrCodeClick,
contentDescription = contentDescription,
centerImageResId = centerImageResId
)
}
}
data class ZashiAccountInfoListItemState(
@DrawableRes val icon: Int,

View File

@ -124,14 +124,17 @@ private fun ZashiAccountInfoListItem(
}
@Composable
private fun ColumnScope.QrContent(state: SignKeystoneTransactionState) {
state.qrData?.let {
ZashiQr(qrData = it, modifier = Modifier.align(CenterHorizontally))
private fun ColumnScope.QrContent(ksState: SignKeystoneTransactionState) {
ksState.qrData?.let {
ZashiQr(
state = ksState.toQrState(),
modifier = Modifier.align(CenterHorizontally)
)
}
LaunchedEffect(state.qrData) {
if (state.qrData != null) {
LaunchedEffect(ksState.qrData) {
if (ksState.qrData != null) {
delay(100.milliseconds)
state.generateNextQrCode()
ksState.generateNextQrCode()
}
}
}
@ -182,6 +185,7 @@ private fun Preview() =
positiveButton = ButtonState(stringRes("Get Signature")),
negativeButton = ButtonState(stringRes("Reject")),
onBack = {},
onQrCodeClick = {},
)
)
}
@ -205,6 +209,7 @@ private fun DebugPreview() =
positiveButton = ButtonState(stringRes("Get Signature")),
negativeButton = ButtonState(stringRes("Reject")),
onBack = {},
onQrCodeClick = {},
)
)
}

View File

@ -75,7 +75,11 @@ class SignKeystoneTransactionViewModel(
text = stringRes("Share PCZT"),
onClick = ::onSharePCZTClick
).takeIf { BuildConfig.DEBUG },
onBack = ::onBack
onBack = ::onBack,
onQrCodeClick = {
// TODO [#1731]: Allow QR codes colors switching
// TODO [#1731]: https://github.com/Electric-Coin-Company/zashi-android/issues/1731
},
)
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT), null)