mirror of
https://github.com/openMF/mobile-wallet.git
synced 2026-02-06 13:36:53 +00:00
feat(qr): Enhance QR code scanner UI with instructions and warning
This commit introduces several UI improvements to the QR code scanner: - Adds an instruction text to guide users on aligning the QR code. - Includes a warning message card about potential risks of scanning QR codes (phishing, malware). - Implements visual corner guides on the scanner view. - Adds a new "Warning" icon to `MifosIcons`. - Adds corresponding string resources for the new UI elements.
This commit is contained in:
parent
c0a14316f6
commit
298c1933e2
@ -45,6 +45,7 @@ import androidx.compose.material.icons.filled.RadioButtonUnchecked
|
||||
import androidx.compose.material.icons.filled.Share
|
||||
import androidx.compose.material.icons.filled.Visibility
|
||||
import androidx.compose.material.icons.filled.VisibilityOff
|
||||
import androidx.compose.material.icons.filled.Warning
|
||||
import androidx.compose.material.icons.outlined.AccountCircle
|
||||
import androidx.compose.material.icons.outlined.Cancel
|
||||
import androidx.compose.material.icons.outlined.DeleteOutline
|
||||
@ -140,4 +141,5 @@ object MifosIcons {
|
||||
val History = Icons.Default.History
|
||||
val Filter = Icons.Default.FilterList
|
||||
val OpenInNew = Icons.AutoMirrored.Filled.OpenInNew
|
||||
val Warning = Icons.Default.Warning
|
||||
}
|
||||
|
||||
@ -17,5 +17,7 @@
|
||||
<string name="feature_qr_loading">Loading</string>
|
||||
<string name="feature_qr_oops">Oops</string>
|
||||
<string name="feature_qr_unexpected_error_subtitle">An unexpected error occurred. Please try again.</string>
|
||||
|
||||
<string name="feature_qr_instruction">Please, align QR Code within the frame to make scanning easily detectable.</string>
|
||||
<string name="feature_qr_warning_title">Potential Warning</string>
|
||||
<string name="feature_qr_warning_message">If you scan the QR code, it could take you to a phishing website that steals your personal information, like credit card numbers or usernames and passwords. It could also download malware onto your phone and give hackers access to your device.</string>
|
||||
</resources>
|
||||
@ -9,15 +9,38 @@
|
||||
*/
|
||||
package org.mifospay.feature.qr
|
||||
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.draw.clipToBounds
|
||||
import androidx.compose.ui.draw.drawWithContent
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import mobile_wallet.feature.qr.generated.resources.Res
|
||||
import mobile_wallet.feature.qr.generated.resources.feature_qr_instruction
|
||||
import mobile_wallet.feature.qr.generated.resources.feature_qr_warning_message
|
||||
import mobile_wallet.feature.qr.generated.resources.feature_qr_warning_title
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
import org.mifospay.core.designsystem.component.MifosCard
|
||||
import org.mifospay.core.designsystem.icon.MifosIcons
|
||||
import template.core.base.designsystem.theme.KptTheme
|
||||
|
||||
@Composable
|
||||
@ -35,27 +58,94 @@ fun QrScannerWithPermissions(
|
||||
openSettingsLabel: String = "Open Settings",
|
||||
onScanned: (String) -> Boolean,
|
||||
) {
|
||||
QrScannerWithPermissions(
|
||||
types = types,
|
||||
modifier = modifier.clipToBounds(),
|
||||
onScanned = onScanned,
|
||||
permissionDeniedContent = { permissionState ->
|
||||
Column(
|
||||
modifier = modifier,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(KptTheme.spacing.lg)
|
||||
.padding(top = KptTheme.spacing.lg),
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(bottom = 56.dp + 24.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(Res.string.feature_qr_instruction),
|
||||
textAlign = TextAlign.Center,
|
||||
color = Color.Black,
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(KptTheme.spacing.md))
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.fillMaxWidth()
|
||||
.clip(KptTheme.shapes.medium)
|
||||
.drawQrCorners()
|
||||
.border(1.dp, Color.Transparent, KptTheme.shapes.medium),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.padding(KptTheme.spacing.sm),
|
||||
text = permissionText,
|
||||
QrScannerWithPermissions(
|
||||
types = types,
|
||||
modifier = modifier.clipToBounds(),
|
||||
onScanned = onScanned,
|
||||
permissionDeniedContent = { permissionState ->
|
||||
Column(
|
||||
modifier = modifier,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.padding(KptTheme.spacing.sm),
|
||||
text = permissionText,
|
||||
)
|
||||
Button(
|
||||
onClick = permissionState::goToSettings,
|
||||
) {
|
||||
Text(openSettingsLabel)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
Button(
|
||||
onClick = permissionState::goToSettings,
|
||||
|
||||
MifosCard(
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomCenter)
|
||||
.fillMaxWidth()
|
||||
.padding(KptTheme.spacing.sm),
|
||||
shape = KptTheme.shapes.medium,
|
||||
) {
|
||||
Text(openSettingsLabel)
|
||||
Column(
|
||||
modifier = Modifier.padding(KptTheme.spacing.lg),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(KptTheme.spacing.sm),
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(KptTheme.spacing.sm),
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.size(20.dp),
|
||||
imageVector = MifosIcons.Warning,
|
||||
contentDescription = "Warning",
|
||||
tint = Color.Unspecified,
|
||||
)
|
||||
Text(
|
||||
text = stringResource(Res.string.feature_qr_warning_title),
|
||||
color = Color.Black,
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text = stringResource(Res.string.feature_qr_warning_message),
|
||||
color = Color.Black,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@ -79,3 +169,72 @@ fun QrScannerWithPermissions(
|
||||
permissionDeniedContent(permissionState)
|
||||
}
|
||||
}
|
||||
|
||||
private fun Modifier.drawQrCorners(): Modifier = drawWithContent {
|
||||
drawContent()
|
||||
|
||||
val strokeWidth = 5.dp.toPx()
|
||||
val lineLength = 40.dp.toPx()
|
||||
|
||||
val horizontalPadding = 50.dp.toPx() // for left & right
|
||||
val verticalPaddingTop = 50.dp.toPx() // for top corners
|
||||
val verticalPaddingBottom = 300.dp.toPx() // more padding for bottom corners
|
||||
|
||||
val color = Color.White
|
||||
|
||||
// Top-left
|
||||
drawLine(
|
||||
color,
|
||||
Offset(horizontalPadding, verticalPaddingTop),
|
||||
Offset(horizontalPadding + lineLength, verticalPaddingTop),
|
||||
strokeWidth,
|
||||
)
|
||||
drawLine(
|
||||
color,
|
||||
Offset(horizontalPadding, verticalPaddingTop),
|
||||
Offset(horizontalPadding, verticalPaddingTop + lineLength),
|
||||
strokeWidth,
|
||||
)
|
||||
|
||||
// Top-right
|
||||
drawLine(
|
||||
color,
|
||||
Offset(size.width - horizontalPadding, verticalPaddingTop),
|
||||
Offset(size.width - horizontalPadding - lineLength, verticalPaddingTop),
|
||||
strokeWidth,
|
||||
)
|
||||
drawLine(
|
||||
color,
|
||||
Offset(size.width - horizontalPadding, verticalPaddingTop),
|
||||
Offset(size.width - horizontalPadding, verticalPaddingTop + lineLength),
|
||||
strokeWidth,
|
||||
)
|
||||
|
||||
// Bottom-left
|
||||
drawLine(
|
||||
color,
|
||||
Offset(horizontalPadding, size.height - verticalPaddingBottom),
|
||||
Offset(horizontalPadding + lineLength, size.height - verticalPaddingBottom),
|
||||
strokeWidth,
|
||||
)
|
||||
drawLine(
|
||||
color,
|
||||
Offset(horizontalPadding, size.height - verticalPaddingBottom),
|
||||
Offset(horizontalPadding, size.height - verticalPaddingBottom - lineLength),
|
||||
strokeWidth,
|
||||
)
|
||||
|
||||
// Bottom-right
|
||||
drawLine(
|
||||
color,
|
||||
Offset(size.width - horizontalPadding, size.height - verticalPaddingBottom),
|
||||
Offset(size.width - horizontalPadding - lineLength, size.height - verticalPaddingBottom),
|
||||
strokeWidth,
|
||||
)
|
||||
drawLine(
|
||||
color,
|
||||
Offset(size.width - horizontalPadding, size.height - verticalPaddingBottom),
|
||||
Offset(size.width - horizontalPadding, size.height - verticalPaddingBottom - lineLength),
|
||||
strokeWidth,
|
||||
)
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user