Skip to content

Commit

Permalink
QR dialog refactor
Browse files Browse the repository at this point in the history
Closes #1731
  • Loading branch information
Milan-Cerovsky committed Feb 4, 2025
1 parent 3523d2b commit 8a44c0f
Show file tree
Hide file tree
Showing 21 changed files with 214 additions and 196 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package co.electriccoin.zcash.ui.common.compose
package co.electriccoin.zcash.ui.design.component

import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
Expand Down Expand Up @@ -37,13 +37,12 @@ val LocalScreenBrightness = compositionLocalOf { ScreenBrightness }
fun BrightenScreen() {
val screenBrightness = LocalScreenBrightness.current
DisposableEffect(screenBrightness) {
screenBrightness.fullBrightness()
onDispose { screenBrightness.restoreBrightness() }
ScreenBrightness.fullBrightness()
onDispose { ScreenBrightness.restoreBrightness() }
}
}

@Composable
fun RestoreScreenBrightness() {
val screenBrightness = LocalScreenBrightness.current
screenBrightness.restoreBrightness()
ScreenBrightness.restoreBrightness()
}
Original file line number Diff line number Diff line change
@@ -1,26 +1,39 @@
package co.electriccoin.zcash.ui.design.component

import android.util.Log
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.Image
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.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
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
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import androidx.compose.ui.window.DialogWindowProvider
import co.electriccoin.zcash.ui.design.theme.colors.ZashiColors
import co.electriccoin.zcash.ui.design.theme.dimensions.ZashiDimensions
import co.electriccoin.zcash.ui.design.util.AndroidQrCodeImageGenerator
Expand All @@ -37,9 +50,81 @@ fun ZashiQr(
qrSize: Dp = ZashiQrDefaults.width,
colors: QrCodeColors = QrCodeDefaults.colors(),
contentPadding: PaddingValues = QrCodeDefaults.contentPadding()
) {
var isFullscreenDialogVisible by remember { mutableStateOf(false) }

ZashiQrInternal(
state = state,
modifier =
modifier
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = null,
onClick = {
isFullscreenDialogVisible = true
}
),
colors = colors,
contentPadding = contentPadding,
qrSize = qrSize,
enableBitmapReload = !isFullscreenDialogVisible,
centerImageResId = state.centerImageResId,
tag = "kktina 1"
)

if (isFullscreenDialogVisible) {
Dialog(
onDismissRequest = { isFullscreenDialogVisible = false },
properties =
DialogProperties(
usePlatformDefaultWidth = false,
dismissOnClickOutside = true,
dismissOnBackPress = true
)
) {
val parent = LocalView.current.parent

BrightenScreen()

FullscreenDialogContent(
state = state,
onBack = { isFullscreenDialogVisible = false },
)

SideEffect {
(parent as? DialogWindowProvider)?.window?.setDimAmount(FULLSCREEN_DIM)
}
}
}
}

@Composable
private fun ZashiQrInternal(
state: QrState,
qrSize: Dp,
colors: QrCodeColors,
contentPadding: PaddingValues,
enableBitmapReload: Boolean,
centerImageResId: Int?,
tag: String,
modifier: Modifier = Modifier,
) {
val qrSizePx = with(LocalDensity.current) { qrSize.roundToPx() }
val bitmap = getQrCode(state.qrData, qrSizePx, colors)
var bitmap: ImageBitmap? by remember {
Log.d(tag, "loaded")
mutableStateOf(getQrCode(state.qrData, qrSizePx, colors))
}

var reload by remember { mutableStateOf(false) }

LaunchedEffect(state.qrData, qrSizePx, colors) {
if (enableBitmapReload && reload) {
bitmap = getQrCode(state.qrData, qrSizePx, colors)
Log.d(tag, "launchedeffect")
}

reload = true
}

Surface(
modifier = modifier,
Expand All @@ -50,30 +135,69 @@ fun ZashiQr(
Box(
modifier = Modifier.padding(contentPadding)
) {
Image(
bitmap = bitmap,
contentDescription = state.contentDescription?.getValue(),
Modifier then
if (state.onClick != null) {
Modifier.clickable { state.onClick.invoke() }
} else {
Modifier
}
)
if (state.centerImageResId != null) {
if (bitmap == null) {
Box(modifier = Modifier.size(qrSize))
} else {
bitmap?.let {
Image(
modifier = Modifier,
bitmap = it,
contentDescription = state.contentDescription?.getValue(),
)
}
}

if (centerImageResId != null) {
Image(
modifier =
Modifier
.size(64.dp)
.align(Alignment.Center),
imageVector = ImageVector.vectorResource(state.centerImageResId),
painter = painterResource(centerImageResId),
contentDescription = null,
)
}
}
}
}

@Composable
private fun FullscreenDialogContent(
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)
) {
ZashiQrInternal(
modifier =
Modifier
.fillMaxWidth()
.align(Alignment.Center),
state = state,
contentPadding = PaddingValues(6.dp),
colors =
QrCodeDefaults.colors(
background = Color.White,
foreground = Color.Black,
border = Color.Unspecified
),
qrSize = LocalConfiguration.current.screenWidthDp.dp - 44.dp,
enableBitmapReload = true,
centerImageResId = state.fullscreenCenterImageResId,
tag = "kktina 2"
)
}
}

private fun getQrCode(
address: String,
size: Int,
Expand Down Expand Up @@ -109,6 +233,8 @@ object QrCodeDefaults {
data class QrState(
val qrData: String,
val contentDescription: StringResource? = null,
val onClick: (() -> Unit)? = null,
val centerImageResId: Int? = null,
val fullscreenCenterImageResId: Int? = null,
)

private const val FULLSCREEN_DIM = .9f
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="61dp"
android:height="60dp"
android:viewportWidth="61"
android:viewportHeight="60">
<path
android:pathData="M0.5,30C0.5,13.432 13.932,0 30.5,0C47.069,0 60.5,13.432 60.5,30C60.5,46.569 47.069,60 30.5,60C13.932,60 0.5,46.569 0.5,30Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M15.5,13.5H33.5L21.5,39H12.5L15.5,13.5Z"
android:fillColor="#000000"/>
<path
android:pathData="M45.5,46.5H27.5L39.5,21H48.5L45.5,46.5Z"
android:fillColor="#1F5AFF"/>
</vector>
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ import androidx.compose.ui.test.junit4.ComposeContentTestRule
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.test.filters.MediumTest
import co.electriccoin.zcash.test.UiTestPrerequisites
import co.electriccoin.zcash.ui.common.compose.BrightenScreen
import co.electriccoin.zcash.ui.common.compose.LocalScreenBrightness
import co.electriccoin.zcash.ui.common.compose.ScreenBrightness
import co.electriccoin.zcash.ui.common.compose.ScreenBrightnessState
import co.electriccoin.zcash.ui.design.component.BrightenScreen
import co.electriccoin.zcash.ui.design.component.LocalScreenBrightness
import co.electriccoin.zcash.ui.design.component.ScreenBrightness
import co.electriccoin.zcash.ui.design.component.ScreenBrightnessState
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.update
Expand Down
12 changes: 0 additions & 12 deletions ui-lib/src/main/java/co/electriccoin/zcash/ui/Navigation.kt
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,6 @@ 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
Expand Down Expand Up @@ -427,16 +425,6 @@ internal fun MainActivity.Navigation() {
composable<TransactionDetail> {
AndroidTransactionDetail(it.toRoute())
}
dialog<QrDialog>(
dialogProperties =
DialogProperties(
dismissOnBackPress = false,
dismissOnClickOutside = false,
usePlatformDefaultWidth = false
)
) {
AndroidQrDialog(it.toRoute())
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import cash.z.ecc.android.sdk.ext.collectWith
import co.electriccoin.zcash.spackle.EmulatorWtfUtil
import co.electriccoin.zcash.spackle.FirebaseTestLabUtil
import co.electriccoin.zcash.ui.MainActivity
import co.electriccoin.zcash.ui.design.component.LocalScreenBrightness
import co.electriccoin.zcash.ui.design.component.ScreenBrightness
import co.electriccoin.zcash.ui.design.component.ScreenBrightnessState
import kotlinx.coroutines.flow.map

@Composable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ import co.electriccoin.zcash.di.koinActivityViewModel
import co.electriccoin.zcash.ui.HomeTabNavigationRouter
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.common.compose.LocalActivity
import co.electriccoin.zcash.ui.common.compose.RestoreScreenBrightness
import co.electriccoin.zcash.ui.common.model.WalletRestoringState
import co.electriccoin.zcash.ui.common.model.WalletSnapshot
import co.electriccoin.zcash.ui.common.viewmodel.HomeViewModel
import co.electriccoin.zcash.ui.common.viewmodel.WalletViewModel
import co.electriccoin.zcash.ui.common.viewmodel.isSynced
import co.electriccoin.zcash.ui.design.component.RestoreScreenBrightness
import co.electriccoin.zcash.ui.screen.account.WrapAccount
import co.electriccoin.zcash.ui.screen.balances.WrapBalances
import co.electriccoin.zcash.ui.screen.home.model.TabItem
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,17 @@ sealed class QrCodeState {
val walletAddress: WalletAddress,
val onAddressCopy: (String) -> Unit,
val onQrCodeShare: (ImageBitmap) -> Unit,
val onQrCodeClick: () -> Unit,
val onBack: () -> Unit,
) : QrCodeState() {
fun toQrState(
contentDescription: StringResource? = null,
centerImageResId: Int? = null
centerImageResId: Int? = null,
fullscreenCenterImageResId: Int? = null
) = QrState(
qrData = walletAddress.address,
onClick = onQrCodeClick,
contentDescription = contentDescription,
centerImageResId = centerImageResId
centerImageResId = centerImageResId,
fullscreenCenterImageResId = fullscreenCenterImageResId
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,6 @@ private fun ZashiPreview() =
walletAddress = runBlocking { WalletAddressFixture.unified() },
onAddressCopy = {},
onQrCodeShare = {},
onQrCodeClick = {},
onBack = {},
),
snackbarHostState = SnackbarHostState(),
Expand All @@ -105,7 +104,6 @@ private fun KeystonePreview() =
walletAddress = runBlocking { WalletAddressFixture.unified() },
onAddressCopy = {},
onQrCodeShare = {},
onQrCodeClick = {},
onBack = {},
),
snackbarHostState = SnackbarHostState(),
Expand Down Expand Up @@ -438,6 +436,11 @@ private fun ColumnScope.QrCode(
when (preparedState.qrCodeType) {
QrCodeType.ZASHI -> R.drawable.logo_zec_fill_stroke
QrCodeType.KEYSTONE -> co.electriccoin.zcash.ui.design.R.drawable.ic_item_keystone_qr
},
fullscreenCenterImageResId =
when (preparedState.qrCodeType) {
QrCodeType.ZASHI -> R.drawable.logo_zec_fill_stroke_white
QrCodeType.KEYSTONE -> co.electriccoin.zcash.ui.design.R.drawable.ic_item_keystone_qr_white
}
),
modifier = modifier.align(CenterHorizontally),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ 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
Expand Down Expand Up @@ -58,13 +57,6 @@ class QrCodeViewModel(
walletAddress = walletAddress,
onAddressCopy = { address -> onAddressCopyClick(address) },
onQrCodeShare = { onQrCodeShareClick(it, versionInfo) },
onQrCodeClick = {
navigationRouter.forward(
QrDialog(
qr = walletAddress.address,
)
)
},
onBack = ::onBack,
qrCodeType =
when (account) {
Expand Down
Loading

0 comments on commit 8a44c0f

Please sign in to comment.