- Improves rendering of the QR Code elements

- Makes the QR bigger
- Moves QR Code to rounder corners
- Moves QR Screen to use the arrow icon instead of the close button
This commit is contained in:
Vitor Pamplona 2024-08-27 11:04:13 -04:00
parent ceaa20fabf
commit 2b5e62aaf7
4 changed files with 51 additions and 25 deletions

View File

@ -108,11 +108,11 @@ fun FollowingIcon(modifier: Modifier) {
}
@Composable
fun ArrowBackIcon() {
fun ArrowBackIcon(tint: Color = MaterialTheme.colorScheme.grayText) {
Icon(
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = stringRes(R.string.back),
tint = MaterialTheme.colorScheme.grayText,
tint = tint,
)
}

View File

@ -26,10 +26,11 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.geometry.CornerRadius
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Rect
@ -46,13 +47,16 @@ import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel
import com.google.zxing.qrcode.encoder.ByteMatrix
import com.google.zxing.qrcode.encoder.Encoder
import com.google.zxing.qrcode.encoder.QRCode
import com.vitorpamplona.amethyst.ui.theme.QuoteBorder
const val QR_MARGIN_PX = 100f
@Preview
@Composable
fun QrCodeDrawerPreview() {
QrCodeDrawer("Test QR data")
Box(Modifier.background(Color.Black).padding(10.dp)) {
QrCodeDrawer("Test QR data")
}
}
@Composable
@ -62,19 +66,21 @@ fun QrCodeDrawer(
) {
val qrCode = remember(contents) { createQrCode(contents = contents) }
val foregroundColor = MaterialTheme.colorScheme.onSurface
val foregroundColor = Color.Black
Box(
modifier =
modifier
.clip(shape = QuoteBorder)
.defaultMinSize(48.dp, 48.dp)
.aspectRatio(1f)
.background(MaterialTheme.colorScheme.background),
.background(Color.White),
) {
Canvas(modifier = Modifier.fillMaxSize()) {
// Calculate the height and width of each column/row
val rowHeight = (size.width - QR_MARGIN_PX * 2f) / qrCode.matrix.height
val columnWidth = (size.width - QR_MARGIN_PX * 2f) / qrCode.matrix.width
val radius = CornerRadius(20f)
// Draw all of the finder patterns required by the QR spec. Calculate the ratio
// of the number of rows/columns to the width and height
@ -86,6 +92,7 @@ fun QrCodeDrawer(
height = rowHeight * FINDER_PATTERN_ROW_COUNT,
),
color = foregroundColor,
cornerRadius = radius,
)
// Draw data bits (encoded data part)
@ -162,6 +169,7 @@ fun DrawScope.drawAllQrCodeDataBits(
),
),
).forEach { section ->
val newSize = Size(size.width + 0.5f, size.height + 0.5f)
for (y in section.first.second until section.second.second) {
for (x in section.first.first until section.second.first) {
if (bytes[x, y] == 1.toByte()) {
@ -177,7 +185,7 @@ fun DrawScope.drawAllQrCodeDataBits(
x = QR_MARGIN_PX + x * size.width,
y = QR_MARGIN_PX + y * size.height,
),
size = size,
size = newSize,
),
)
},
@ -191,7 +199,7 @@ fun DrawScope.drawAllQrCodeDataBits(
const val FINDER_PATTERN_ROW_COUNT = 7
private const val INTERIOR_EXTERIOR_SHAPE_RATIO = 3f / FINDER_PATTERN_ROW_COUNT
private const val INTERIOR_EXTERIOR_OFFSET_RATIO = 2f / FINDER_PATTERN_ROW_COUNT
private const val INTERIOR_EXTERIOR_SHAPE_CORNER_RADIUS = 0.12f
private const val INTERIOR_EXTERIOR_SHAPE_CORNER_RADIUS = 0.50f
private const val INTERIOR_BACKGROUND_EXTERIOR_SHAPE_RATIO = 5f / FINDER_PATTERN_ROW_COUNT
private const val INTERIOR_BACKGROUND_EXTERIOR_OFFSET_RATIO = 1f / FINDER_PATTERN_ROW_COUNT
private const val INTERIOR_BACKGROUND_EXTERIOR_SHAPE_CORNER_RADIUS = 0.5f
@ -206,6 +214,7 @@ private const val INTERIOR_BACKGROUND_EXTERIOR_SHAPE_CORNER_RADIUS = 0.5f
internal fun DrawScope.drawQrCodeFinders(
sideLength: Float,
finderPatternSize: Size,
cornerRadius: CornerRadius,
color: Color,
) {
setOf(
@ -219,7 +228,7 @@ internal fun DrawScope.drawQrCodeFinders(
drawQrCodeFinder(
topLeft = offset,
finderPatternSize = finderPatternSize,
cornerRadius = CornerRadius.Zero,
cornerRadius = cornerRadius,
color = color,
)
}

View File

@ -31,8 +31,8 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.FilledTonalButton
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
@ -54,14 +54,16 @@ import androidx.compose.ui.window.DialogProperties
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.FeatureSetType
import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.ui.actions.CloseButton
import com.vitorpamplona.amethyst.ui.components.CreateTextWithEmoji
import com.vitorpamplona.amethyst.ui.components.DisplayNIP05
import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage
import com.vitorpamplona.amethyst.ui.components.nip05VerificationAsAState
import com.vitorpamplona.amethyst.ui.note.ArrowBackIcon
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.mockAccountViewModel
import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.Font14SP
import com.vitorpamplona.amethyst.ui.theme.Size10dp
import com.vitorpamplona.amethyst.ui.theme.Size35dp
import com.vitorpamplona.quartz.events.UserMetadata
@ -87,6 +89,15 @@ fun ShowQRDialogPreview() {
)
}
@Composable
fun BackButton(onPress: () -> Unit) {
IconButton(
onClick = onPress,
) {
ArrowBackIcon(MaterialTheme.colorScheme.onBackground)
}
}
@Composable
fun ShowQRDialog(
user: User,
@ -107,7 +118,7 @@ fun ShowQRDialog(
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
CloseButton(onPress = onClose)
BackButton(onPress = onClose)
}
Column(
@ -126,23 +137,23 @@ fun ShowQRDialog(
contentDescription = stringRes(R.string.profile_image),
modifier =
Modifier
.width(100.dp)
.height(100.dp)
.width(120.dp)
.height(120.dp)
.clip(shape = CircleShape)
.border(3.dp, MaterialTheme.colorScheme.background, CircleShape),
.border(3.dp, MaterialTheme.colorScheme.onBackground, CircleShape),
loadProfilePicture = accountViewModel.settings.showProfilePictures.value,
loadRobohash = accountViewModel.settings.featureSet != FeatureSetType.PERFORMANCE,
)
}
Row(
horizontalArrangement = Arrangement.Center,
modifier = Modifier.fillMaxWidth().padding(top = 5.dp),
modifier = Modifier.fillMaxWidth().padding(top = 10.dp),
) {
CreateTextWithEmoji(
text = user.info?.bestName() ?: "",
tags = user.info?.tags,
fontWeight = FontWeight.Bold,
fontSize = 18.sp,
fontSize = 20.sp,
)
}
@ -160,7 +171,7 @@ fun ShowQRDialog(
} else {
Text(
text = user.pubkeyDisplayHex(),
fontSize = 14.sp,
fontSize = Font14SP,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
@ -170,20 +181,16 @@ fun ShowQRDialog(
Row(
horizontalArrangement = Arrangement.Center,
modifier = Modifier.fillMaxWidth().padding(horizontal = Size35dp),
modifier = Modifier.fillMaxWidth().padding(horizontal = Size10dp),
) {
QrCodeDrawer(user.toNostrUri())
}
Row(modifier = Modifier.padding(horizontal = 30.dp)) {
Button(
FilledTonalButton(
onClick = { presenting = false },
shape = RoundedCornerShape(Size35dp),
modifier = Modifier.fillMaxWidth().height(50.dp),
colors =
ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.primary,
),
) {
Text(text = stringRes(R.string.scan_qr))
}

View File

@ -91,6 +91,16 @@ class Nip19Bech32Test {
Assert.assertEquals(null, actual)
}
@Test()
fun uri_to_route_complete_nprofile_2() {
val actual = Nip19Bech32.uriToRoute("nostr:nprofile1qqsyvrp9u6p0mfur9dfdru3d853tx9mdjuhkphxuxgfwmryja7zsvhqpzamhxue69uhhv6t5daezumn0wd68yvfwvdhk6tcpz9mhxue69uhkummnw3ezuamfdejj7qgwwaehxw309ahx7uewd3hkctcscpyug")
Assert.assertNotNull(actual)
Assert.assertTrue(actual?.entity is Nip19Bech32.NProfile)
Assert.assertEquals("460c25e682fda7832b52d1f22d3d22b3176d972f60dcdc3212ed8c92ef85065c", (actual?.entity as? Nip19Bech32.NProfile)?.hex)
Assert.assertEquals("wss://vitor.nostr1.com/", (actual?.entity as? Nip19Bech32.NProfile)?.relay?.first())
}
@Test()
fun uri_to_route_complete_nprofile() {
val actual = Nip19Bech32.uriToRoute("nostr:nprofile1qy2hwumn8ghj7un9d3shjtnyv9kh2uewd9hj7qgwwaehxw309ahx7uewd3hkctcpr9mhxue69uhhyetvv9ujuumwdae8gtnnda3kjctv9uqzq9thu3vem5gvsc6f3l3uyz7c92h6lq56t9wws0zulzkrgc6nrvym5jfztf")