Skip to content

Commit

Permalink
Implement barcode block
Browse files Browse the repository at this point in the history
  • Loading branch information
msasikanth committed Sep 27, 2023
1 parent c7ce3df commit 2a82140
Show file tree
Hide file tree
Showing 7 changed files with 213 additions and 0 deletions.
2 changes: 2 additions & 0 deletions common/src/commonMain/kotlin/org/hisp/dhis/common/App.kt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import org.hisp.dhis.common.screens.BadgesScreen
import org.hisp.dhis.common.screens.BarcodeBlockScreen
import org.hisp.dhis.common.screens.BottomSheetHeaderScreen
import org.hisp.dhis.common.screens.ButtonBlockScreen
import org.hisp.dhis.common.screens.ButtonScreen
Expand Down Expand Up @@ -139,6 +140,7 @@ fun Main() {
Components.BADGES -> BadgesScreen()
Components.SWITCH -> SwitchScreen()
Components.INPUT_RADIO_BUTTON -> InputRadioButtonScreen()
Components.BARCODE_BLOCK -> BarcodeBlockScreen()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.hisp.dhis.common.screens

import androidx.compose.material3.Divider
import androidx.compose.runtime.Composable
import org.hisp.dhis.mobile.ui.designsystem.component.BarcodeBlock
import org.hisp.dhis.mobile.ui.designsystem.component.ColumnComponentContainer
import org.hisp.dhis.mobile.ui.designsystem.component.QrCodeBlock
import org.hisp.dhis.mobile.ui.designsystem.component.RowComponentContainer

@Composable
fun BarcodeBlockScreen() {
ColumnComponentContainer {
BarcodeBlock(data = "1111111111")
Divider()
RowComponentContainer {
BarcodeBlock(data = "3981239131092310")
BarcodeBlock(data = "9038192381902381029831")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,5 @@ enum class Components(val label: String) {
METADATA_AVATAR("Metadata Avatar"),
INPUT_RADIO_BUTTON("Input Radio Button"),
SWITCH("Switch"),
BARCODE_BLOCK("Barcode Block"),
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package org.hisp.dhis.mobile.ui.designsystem.component.internal.barcode

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.MultiFormatWriter
import com.google.zxing.WriterException
import com.google.zxing.common.BitMatrix

internal actual class BarcodeGenerator {

actual fun generate(data: String): ImageBitmap? {
return try {
val writer = MultiFormatWriter()
val bitMatrix = writer.encode(
data,
BarcodeFormat.CODABAR,
BARCODE_WIDTH,
BARCODE_HEIGHT,
)
val bitmap = createAndroidBitmap(bitMatrix)

bitmap.asImageBitmap()
} catch (e: WriterException) {
null
}
}

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)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package org.hisp.dhis.mobile.ui.designsystem.component

import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.requiredSizeIn
import androidx.compose.foundation.layout.size
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
import org.hisp.dhis.mobile.ui.designsystem.component.internal.barcode.rememberBarcodeGenerator
import org.hisp.dhis.mobile.ui.designsystem.theme.Spacing
import org.hisp.dhis.mobile.ui.designsystem.theme.TextColor

/**
* Container to render barcode image and text for given
* data
*
* @param data: Data to render barcode and text for
*/
@Composable
fun BarcodeBlock(
data: String,
modifier: Modifier = Modifier,
barcodeSize: DpSize = DpSize(312.dp, 76.dp)
) {
if (data.isNotBlank()) {
Column(
modifier = modifier.padding(vertical = Spacing.Spacing16)
.requiredSizeIn(maxWidth = barcodeSize.width)
.testTag("BARCODE_BLOCK_CONTAINER"),
horizontalAlignment = Alignment.CenterHorizontally,
) {
val barcode by rememberBarcodeGenerator(data)

Box(Modifier.size(barcodeSize)) {
barcode?.let { bitmap ->
Image(
bitmap = bitmap,
contentDescription = null,
modifier = Modifier.matchParentSize(),
contentScale = ContentScale.FillBounds,
)
}
}

Text(
text = data,
style = MaterialTheme.typography.titleMedium,
color = TextColor.OnSurface,
textAlign = TextAlign.Center,
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package org.hisp.dhis.mobile.ui.designsystem.component.internal.barcode

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 rememberBarcodeGenerator(value: String): State<ImageBitmap?> {
val barcodeGenerator = LocalBarcodeGenerator.current
val result = remember(value) { mutableStateOf<ImageBitmap?>(null) }

LaunchedEffect(value) {
result.value = barcodeGenerator.generate(value)
}

return result
}

internal expect class BarcodeGenerator() {
fun generate(data: String): ImageBitmap?
}

internal const val BARCODE_WIDTH = 512 // px
internal const val BARCODE_HEIGHT = 256 // px

internal val LocalBarcodeGenerator = staticCompositionLocalOf { BarcodeGenerator() }
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package org.hisp.dhis.mobile.ui.designsystem.component.internal.barcode

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.MultiFormatWriter
import com.google.zxing.WriterException
import com.google.zxing.common.BitMatrix
import java.awt.image.BufferedImage

internal actual class BarcodeGenerator {

private val colorBlack = 0xFF000000.toInt()
private val colorWhite = 0xFFFFFFFF.toInt()

actual fun generate(data: String): ImageBitmap? {
return try {
val writer = MultiFormatWriter()
val bitMatrix = writer.encode(
data,
BarcodeFormat.CODABAR,
BARCODE_WIDTH,
BARCODE_HEIGHT,
)
val image = createBufferedImage(bitMatrix)

image.toComposeImageBitmap()
} catch (e: WriterException) {
null
}
}

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)
}
}
}

0 comments on commit 2a82140

Please sign in to comment.