diff --git a/designsystem/src/androidMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/internal/qr/QrCodeGenerator.kt b/designsystem/src/androidMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/internal/qr/QrCodeGenerator.kt new file mode 100644 index 000000000..0d7add078 --- /dev/null +++ b/designsystem/src/androidMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/internal/qr/QrCodeGenerator.kt @@ -0,0 +1,43 @@ +package org.hisp.dhis.mobile.ui.designsystem.component.internal.qr + +import android.graphics.Bitmap +import android.graphics.Color +import androidx.compose.ui.graphics.ImageBitmap +import androidx.compose.ui.graphics.asImageBitmap +import com.google.zxing.BarcodeFormat +import com.google.zxing.EncodeHintType +import com.google.zxing.common.BitMatrix +import com.google.zxing.qrcode.QRCodeWriter + +internal actual class QrCodeGenerator { + + actual fun generate(data: String): ImageBitmap { + val writer = QRCodeWriter() + val bitMatrix = writer.encode( + data, + BarcodeFormat.QR_CODE, + QR_CODE_SIZE, + QR_CODE_SIZE, + mapOf(Pair(EncodeHintType.MARGIN, 1)), + ) + val bitmap = createAndroidBitmap(bitMatrix) + + return bitmap.asImageBitmap() + } + + private fun createAndroidBitmap(bitMatrix: BitMatrix): Bitmap { + val width = bitMatrix.width + val height = bitMatrix.height + val pixels = IntArray(width * height) + + for (y in 0 until height) { + for (x in 0 until width) { + pixels[y * width + x] = if (bitMatrix[x, y]) Color.BLACK else Color.WHITE + } + } + + return Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888).apply { + setPixels(pixels, 0, width, 0, 0, width, height) + } + } +} diff --git a/designsystem/src/commonMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/internal/qr/QrCodeGenerator.kt b/designsystem/src/commonMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/internal/qr/QrCodeGenerator.kt new file mode 100644 index 000000000..902e0c6d0 --- /dev/null +++ b/designsystem/src/commonMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/internal/qr/QrCodeGenerator.kt @@ -0,0 +1,29 @@ +package org.hisp.dhis.mobile.ui.designsystem.component.internal.qr + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.State +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.staticCompositionLocalOf +import androidx.compose.ui.graphics.ImageBitmap + +@Composable +internal fun rememberQrCodeGenerator(value: String): State { + val qrCodeGenerator = LocalQrCodeGenerator.current + val result = remember(value) { mutableStateOf(null) } + + LaunchedEffect(value) { + result.value = qrCodeGenerator.generate(value) + } + + return result +} + +internal expect class QrCodeGenerator() { + fun generate(data: String): ImageBitmap +} + +internal const val QR_CODE_SIZE = 512 // px + +internal val LocalQrCodeGenerator = staticCompositionLocalOf { QrCodeGenerator() } diff --git a/designsystem/src/desktopMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/internal/qr/QrCodeGenerator.kt b/designsystem/src/desktopMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/internal/qr/QrCodeGenerator.kt new file mode 100644 index 000000000..0eb1797ba --- /dev/null +++ b/designsystem/src/desktopMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/internal/qr/QrCodeGenerator.kt @@ -0,0 +1,45 @@ +package org.hisp.dhis.mobile.ui.designsystem.component.internal.qr + +import androidx.compose.ui.graphics.ImageBitmap +import androidx.compose.ui.graphics.toComposeImageBitmap +import com.google.zxing.BarcodeFormat +import com.google.zxing.EncodeHintType +import com.google.zxing.common.BitMatrix +import com.google.zxing.qrcode.QRCodeWriter +import java.awt.image.BufferedImage + +internal actual class QrCodeGenerator { + + private val colorBlack = 0xFF000000.toInt() + private val colorWhite = 0xFFFFFFFF.toInt() + + actual fun generate(data: String): ImageBitmap { + val writer = QRCodeWriter() + val bitMatrix = writer.encode( + data, + BarcodeFormat.QR_CODE, + QR_CODE_SIZE, + QR_CODE_SIZE, + mapOf(Pair(EncodeHintType.MARGIN, 1)), + ) + val image = createBufferedImage(bitMatrix) + + return image.toComposeImageBitmap() + } + + private fun createBufferedImage(bitMatrix: BitMatrix): BufferedImage { + val width = bitMatrix.width + val height = bitMatrix.height + val pixels = IntArray(width * height) + + for (y in 0 until height) { + for (x in 0 until width) { + pixels[y * width + x] = if (bitMatrix[x, y]) colorBlack else colorWhite + } + } + + return BufferedImage(width, height, BufferedImage.TYPE_INT_RGB).apply { + setRGB(0, 0, width, height, pixels, 0, width) + } + } +}