commit 08977f5e2b79ad4ab3ef79cc97511a792dfbbb13
parent 139eb9b3bd91317cbab32d7f0e4e4fcac20044a5
Author: Iván Ávalos <avalos@disroot.org>
Date: Sat, 14 Feb 2026 16:25:50 +0100
[wallet] add Taler logo to taler:// QR and Swiss flag to Swiss QR
Diffstat:
8 files changed, 112 insertions(+), 13 deletions(-)
diff --git a/taler-kotlin-android/src/main/java/net/taler/common/QrCodeManager.kt b/taler-kotlin-android/src/main/java/net/taler/common/QrCodeManager.kt
@@ -18,8 +18,12 @@ package net.taler.common
import android.graphics.Bitmap
import android.graphics.Bitmap.Config.RGB_565
+import android.graphics.Canvas
import android.graphics.Color.BLACK
import android.graphics.Color.WHITE
+import android.graphics.Matrix
+import androidx.core.graphics.createBitmap
+import androidx.core.graphics.set
import com.google.zxing.BarcodeFormat.QR_CODE
import com.google.zxing.EncodeHintType.ERROR_CORRECTION
import com.google.zxing.EncodeHintType.MARGIN
@@ -33,6 +37,7 @@ object QrCodeManager {
size: Int = 256,
margin: Int = 4,
errorCorrection: ErrorCorrectionLevel = ErrorCorrectionLevel.M,
+ centerLogo: ((size: Int) -> Bitmap)? = null,
): Bitmap {
val qrCodeWriter = QRCodeWriter()
val hints = mapOf(
@@ -42,13 +47,26 @@ object QrCodeManager {
val bitMatrix = qrCodeWriter.encode(text, QR_CODE, size, size, hints)
val height = bitMatrix.height
val width = bitMatrix.width
- val bmp = Bitmap.createBitmap(width, height, RGB_565)
+ val bmp = createBitmap(width, height, RGB_565)
for (x in 0 until width) {
for (y in 0 until height) {
- bmp.setPixel(x, y, if (bitMatrix.get(x, y)) BLACK else WHITE)
+ bmp[x, y] = if (bitMatrix.get(x, y)) BLACK else WHITE
}
}
+
+ if (centerLogo != null) {
+ val combined = createBitmap(bmp.width, bmp.height, bmp.config!!)
+ val canvas = Canvas(combined)
+ canvas.drawBitmap(bmp, Matrix(), null)
+
+ val logo = centerLogo(canvas.width / 6)
+ val centreX = (canvas.width - logo.width) / 2f
+ val centreY = (canvas.height - logo.height) / 2f
+
+ canvas.drawBitmap(logo, centreX, centreY, null)
+ return combined
+ }
+
return bmp
}
-
}
diff --git a/wallet/src/main/java/net/taler/wallet/Utils.kt b/wallet/src/main/java/net/taler/wallet/Utils.kt
@@ -44,13 +44,19 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.graphics.Canvas
+import androidx.compose.ui.graphics.ImageBitmap
+import androidx.compose.ui.graphics.drawscope.CanvasDrawScope
+import androidx.compose.ui.graphics.painter.Painter
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.LayoutDirection
import androidx.core.content.getSystemService
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
-import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import net.taler.common.Amount
import net.taler.common.AmountParserException
@@ -200,4 +206,17 @@ fun Modifier.systemBarsPaddingHorizontal() =
@Composable
fun Modifier.systemBarsPaddingAllExceptTop() =
- windowInsetsPadding(WindowInsets.systemBars.only(WindowInsetsSides.Horizontal + WindowInsetsSides.Bottom))
-\ No newline at end of file
+ windowInsetsPadding(WindowInsets.systemBars.only(WindowInsetsSides.Horizontal + WindowInsetsSides.Bottom))
+
+fun Painter.toImageBitmap(
+ size: Size,
+ density: Density,
+ layoutDirection: LayoutDirection,
+): ImageBitmap {
+ val bitmap = ImageBitmap(size.width.toInt(), size.height.toInt())
+ val canvas = Canvas(bitmap)
+ CanvasDrawScope().draw(density, layoutDirection, canvas, size) {
+ draw(size)
+ }
+ return bitmap
+}
+\ No newline at end of file
diff --git a/wallet/src/main/java/net/taler/wallet/compose/ExpandableCard.kt b/wallet/src/main/java/net/taler/wallet/compose/ExpandableCard.kt
@@ -43,8 +43,10 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.rotate
+import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
+import net.taler.wallet.R
@Composable
fun ExpandableCard(
@@ -150,6 +152,7 @@ fun ExpandableCardPreview(
QrCodeUriComposable(
talerUri = "taler://withdraw-exchange",
clipBoardLabel = "",
+ centerLogo = painterResource(R.drawable.ic_swiss_qr),
showContents = false,
)
}
diff --git a/wallet/src/main/java/net/taler/wallet/compose/QrCodeUriComposable.kt b/wallet/src/main/java/net/taler/wallet/compose/QrCodeUriComposable.kt
@@ -42,10 +42,15 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.produceState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.graphics.ImageBitmap
+import androidx.compose.ui.graphics.asAndroidBitmap
import androidx.compose.ui.graphics.asImageBitmap
+import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.platform.LocalInspectionMode
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.unit.Dp
@@ -54,22 +59,38 @@ import androidx.compose.ui.unit.min
import net.taler.common.QrCodeManager
import net.taler.common.copyToClipBoard
import net.taler.wallet.R
+import net.taler.wallet.toImageBitmap
+import kotlin.let
@Composable
fun ColumnScope.QrCodeUriComposable(
talerUri: String,
clipBoardLabel: String,
+ centerLogo: Painter? = null,
buttonText: String = stringResource(R.string.copy),
showContents: Boolean = true,
shareAsQrCode: Boolean = false,
inBetween: (@Composable ColumnScope.() -> Unit)? = null,
) {
val qrCodeSize = getQrCodeSize()
- val qrPlaceHolder = if (LocalInspectionMode.current) {
- QrCodeManager.makeQrCode(talerUri, qrCodeSize.value.toInt()).asImageBitmap()
- } else null
- val qrState = produceState(qrPlaceHolder) {
- value = QrCodeManager.makeQrCode(talerUri, qrCodeSize.value.toInt()).asImageBitmap()
+ val density = LocalDensity.current
+ val direction = LocalLayoutDirection.current
+
+ val qrState = produceState<ImageBitmap?>(null) {
+ value = QrCodeManager.makeQrCode(
+ talerUri,
+ qrCodeSize.value.toInt(),
+ centerLogo = centerLogo?.let {{ size ->
+ centerLogo.toImageBitmap(
+ Size(
+ size * (centerLogo.intrinsicSize.width / centerLogo.intrinsicSize.height),
+ size.toFloat(),
+ ),
+ density,
+ direction,
+ ).asAndroidBitmap()
+ }},
+ ).asImageBitmap()
}
Box(
diff --git a/wallet/src/main/java/net/taler/wallet/peer/TransactionPeerPushDebit.kt b/wallet/src/main/java/net/taler/wallet/peer/TransactionPeerPushDebit.kt
@@ -29,6 +29,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment.Companion.CenterHorizontally
import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
@@ -121,6 +122,7 @@ fun ColumnScope.PeerQrCode(
QrCodeUriComposable(
talerUri = talerUri,
clipBoardLabel = "Push payment",
+ centerLogo = painterResource(R.drawable.ic_taler_qr),
buttonText = stringResource(id = R.string.copy),
) {
Text(
diff --git a/wallet/src/main/java/net/taler/wallet/transactions/TransitionsComposable.kt b/wallet/src/main/java/net/taler/wallet/transactions/TransitionsComposable.kt
@@ -44,7 +44,6 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
-import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import net.taler.common.copyToClipBoard
import net.taler.wallet.R
diff --git a/wallet/src/main/java/net/taler/wallet/transfer/PaytoQrCard.kt b/wallet/src/main/java/net/taler/wallet/transfer/PaytoQrCard.kt
@@ -22,6 +22,7 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import net.taler.wallet.R
@@ -55,6 +56,10 @@ fun PaytoQrCard(
clipBoardLabel = label,
showContents = true,
shareAsQrCode = true,
+ centerLogo = when (qrCode.type) {
+ SPC -> painterResource(R.drawable.ic_swiss_qr)
+ else -> null
+ }
)
Spacer(Modifier.height(8.dp))
diff --git a/wallet/src/main/res/drawable/ic_taler_qr.xml b/wallet/src/main/res/drawable/ic_taler_qr.xml
@@ -0,0 +1,32 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="512dp"
+ android:height="375.49dp"
+ android:viewportWidth="512"
+ android:viewportHeight="375.49">
+ <path
+ android:pathData="M194.29,0H317.71C425.35,0 512,72.69 512,162.98v49.54c0,90.29 -86.65,162.98 -194.29,162.98H194.29C86.65,375.49 0,302.8 0,212.51V162.98C0,72.69 86.65,0 194.29,0Z"
+ android:strokeLineJoin="round"
+ android:strokeWidth="23.4248"
+ android:fillColor="#ffffff"
+ android:strokeLineCap="round"/>
+ <path
+ android:pathData="m169.32,72.37c-52.13,8.79 -91.79,57.24 -91.79,115.51 0,64.4 48.86,116.94 108.96,116.94 60.1,0 108.96,-52.34 108.96,-116.94 0,-31.48 -11.65,-59.9 -30.46,-80.96 3.27,-3.88 12.88,-9.81 13.08,-9.4 20.44,23.71 32.91,55.61 32.91,90.36 0,73.6 -55.81,133.5 -124.5,133.5 -68.69,0 -124.5,-59.9 -124.5,-133.5 0,-73.6 55.81,-133.5 124.5,-133.5 3.48,0 11.65,0.61 11.24,0.82 -8.79,4.7 -19.83,11.04 -28.42,17.38"
+ android:strokeWidth="2.04437"
+ android:fillColor="#3047a3"
+ android:fillType="evenOdd"/>
+ <path
+ android:pathData="m175.86,289.89c-27.39,-24.53 -44.98,-61.13 -44.98,-102.22 0,-73.6 56.02,-133.29 124.91,-133.29 3.07,0 6.13,0.2 9.2,0.41 -9.79,4.76 -18.99,10.66 -27.39,17.58 -51.93,9.2 -91.38,57.45 -91.38,115.3 0,43.55 22.49,81.77 55.81,101.81 -8.58,1.41 -17.32,1.61 -25.96,0.61z"
+ android:strokeWidth="2.04437"
+ android:fillColor="#3047a3"
+ android:fillType="evenOdd"/>
+ <path
+ android:pathData="m342.68,303.8c52.13,-8.79 91.79,-57.24 91.79,-115.51 0,-64.4 -48.86,-116.94 -108.96,-116.94 -60.1,0 -108.96,52.34 -108.96,116.94 0,31.48 11.65,59.9 30.46,80.96 -3.27,3.88 -12.88,9.81 -13.08,9.4 -20.44,-23.71 -32.91,-55.61 -32.91,-90.36 0,-73.6 55.81,-133.5 124.5,-133.5 68.69,0 124.5,59.9 124.5,133.5 0,73.6 -55.81,133.5 -124.5,133.5 -3.48,0 -11.65,-0.61 -11.24,-0.82 8.79,-4.7 19.83,-11.04 28.42,-17.38"
+ android:strokeWidth="2.04437"
+ android:fillColor="#3047a3"
+ android:fillType="evenOdd"/>
+ <path
+ android:pathData="m336.14,86.28c27.39,24.53 44.98,61.13 44.98,102.22 0,73.6 -56.02,133.29 -124.91,133.29 -3.07,0 -6.13,-0.2 -9.2,-0.41 9.79,-4.76 18.99,-10.66 27.39,-17.58 51.93,-9.2 91.38,-57.45 91.38,-115.3 0,-43.55 -22.49,-81.77 -55.81,-101.81 8.58,-1.41 17.32,-1.61 25.96,-0.61z"
+ android:strokeWidth="2.04437"
+ android:fillColor="#3047a3"
+ android:fillType="evenOdd"/>
+</vector>