Compare commits

..

No commits in common. "6f61532741e6a6ed392cfb84e22df138fee945d6" and "c3faee2eb0966d28352f68a612fc1d3ad9b9ab0c" have entirely different histories.

8 changed files with 182 additions and 48 deletions

View File

@ -125,6 +125,4 @@ dependencies {
implementation(project(":ecr"))
implementation(project(":xpay"))
implementation(project(":cmhl"))
implementation("se.warting.signature:signature-pad:0.2.0")
}

View File

@ -108,6 +108,11 @@ fun InputAmount(
modifier = Modifier.align(Alignment.CenterHorizontally)
)
Spacer(modifier = Modifier.height(8.dp))
// Row(
// modifier = Modifier.align(Alignment.CenterHorizontally),
// verticalAlignment = Alignment.Bottom
// ) {
Text(
text = formatAmount(amount.ifEmpty { "0" }),
color = Color.LegacyRed,
@ -122,7 +127,12 @@ fun InputAmount(
modifier = Modifier.padding(end = 10.dp, bottom = 6.dp)
)
}
// }
Spacer(modifier = Modifier.height(14.dp))
Text(
text = supportingText,
color = Color.Gray,
@ -131,6 +141,8 @@ fun InputAmount(
)
}
}
//second container
Column(
modifier = Modifier
.fillMaxWidth()
@ -234,15 +246,14 @@ private fun formatAmount(value: String): String {
val groupedWholePart = "%,d".format(wholePart.toLongOrNull() ?: 0L)
if (!normalized.contains(".")) {
return "$groupedWholePart.00"
return groupedWholePart
}
val decimalPart = normalized.substringAfter(".", "")
return if (normalized.endsWith(".")) {
"$groupedWholePart.00"
"$groupedWholePart."
} else {
val padded = decimalPart.padEnd(2, '0').take(2)
"$groupedWholePart.$padded"
"$groupedWholePart.$decimalPart"
}
}

View File

@ -26,7 +26,6 @@ import com.mob.utsmyanmar.ui.device_info.DeviceInfoViewModel
import com.mob.utsmyanmar.ui.error.TransactionErrorScreen
import com.mob.utsmyanmar.ui.functions.FunctionsScreen
import com.mob.utsmyanmar.ui.history_functions.HistoryRoute
import com.mob.utsmyanmar.ui.history_functions.prepareReprintPayDetail
import com.mob.utsmyanmar.ui.input_amount.AmountRoute
import com.mob.utsmyanmar.ui.password_input.InputPassword
import com.mob.utsmyanmar.ui.password_input.PasswordType
@ -37,6 +36,7 @@ import com.mob.utsmyanmar.ui.processing_card.ProcessingCardRoute
import com.mob.utsmyanmar.ui.processing_card.ProcessingCardViewModel
import com.mob.utsmyanmar.ui.refund_rrn.InputRrnRoute
import com.mob.utsmyanmar.ui.refund_rrn.RRNViewModel
import com.mob.utsmyanmar.ui.history_functions.prepareReprintPayDetail
import com.mob.utsmyanmar.ui.sale_void.TranDetailPage
import com.mob.utsmyanmar.ui.sale_void.VoidTraceScreen
import com.mob.utsmyanmar.ui.sale_void.VoidViewModel
@ -92,7 +92,7 @@ fun AppNavGraph(
val sharedViewModel: SharedViewModel = hiltViewModel(activity)
SignaturePad(
onBack = { navController.popBackStack() },
onConfirm = { bitmap, signatureBlob ->
onConfirm = { _, bitmap, signatureBlob ->
sharedViewModel.signBitmap = bitmap
sharedViewModel.updateSignatureBlob(signatureBlob)
navController.navigate(Routes.TransactionResult.route) {
@ -179,6 +179,7 @@ fun AppNavGraph(
launchSingleTop = true
}
}
"Pre-Auth",
"Cash Adv" -> {
navController.navigate(Routes.Amount.createRoute(action)) {
@ -801,14 +802,7 @@ fun AppNavGraph(
}
private fun formatAmountForDisplay(amount: String?): String {
val raw = amount.orEmpty().trim()
if (raw.isBlank()) return "0.00"
if (raw.contains(".")) {
val whole = raw.substringBefore(".").ifEmpty { "0" }
val decimal = raw.substringAfter(".", "")
val grouped = "%,d".format(whole.toLongOrNull() ?: 0L)
return "$grouped.${decimal.padEnd(2, '0').take(2)}"
}
val value = raw.toLongOrNull() ?: return raw
return "%,d.00".format(value)
val normalizedAmount = amount.orEmpty()
val value = normalizedAmount.toLongOrNull() ?: return normalizedAmount.ifBlank { "0" }
return "%,d".format(value)
}

View File

@ -1,10 +1,15 @@
package com.mob.utsmyanmar.ui.signature_pad
import android.graphics.Bitmap
import androidx.compose.foundation.border
import android.graphics.Paint
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.detectDragGestures
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
@ -19,31 +24,82 @@ import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Path
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.core.graphics.createBitmap
import com.mob.utsmyanmar.ui.components.appbar.AppBar
import com.mob.utsmyanmar.ui.theme.MOBPOSTheme
import se.warting.signaturepad.SignaturePadAdapter
import se.warting.signaturepad.SignaturePadView
import java.io.ByteArrayOutputStream
import android.graphics.Canvas as AndroidCanvas
import android.graphics.Path as AndroidPath
import com.mob.utsmyanmar.ui.theme.Color as AppColor
@Composable
fun SignaturePad(
modifier: Modifier = Modifier,
signatureColor: Color = Color.Black,
onBack: () -> Unit = {},
onConfirm: (Bitmap?, ByteArray?) -> Unit = { _, _ -> }
onConfirm: (SignatureMode, Bitmap?, signatureBlob: ByteArray?) -> Unit = { _, _, _ -> }
) {
var signaturePadAdapter: SignaturePadAdapter? = null
val paths = remember { mutableStateListOf<List<Offset>>() }
val currentPath = remember { mutableStateListOf<Offset>() }
var selectedMode by remember { mutableStateOf(SignatureMode.Digital) }
var canvasSize by remember { mutableStateOf(Size.Zero) }
var strokeWidth = 6f
val scaledWidth = 300
fun clearSignature() {
paths.clear()
currentPath.clear()
}
fun scaleBitmap(source: Bitmap): Bitmap {
val ratio = scaledWidth.toFloat() / source.width
val height = (source.height * ratio).toInt()
return Bitmap.createScaledBitmap(source, scaledWidth, height, true)
fun buildBitmap(): Bitmap? {
if (selectedMode == SignatureMode.Manual) return null
if (paths.isEmpty() && currentPath.isEmpty()) return null
if (canvasSize.width <= 0f || canvasSize.height <= 0f) return null
val bitmap =
createBitmap(canvasSize.width.toInt(), canvasSize.height.toInt(), Bitmap.Config.RGB_565)
val canvas = AndroidCanvas(bitmap)
canvas.drawColor(android.graphics.Color.WHITE)
val paint = Paint().apply {
color = android.graphics.Color.BLACK
style = Paint.Style.STROKE
isAntiAlias = true
strokeCap = Paint.Cap.ROUND
strokeJoin = Paint.Join.ROUND
strokeWidth = 10f
}
fun drawStroke(points: List<Offset>) {
if (points.size < 2) return
val path = AndroidPath().apply {
moveTo(points.first().x, points.first().y)
for (index in 1 until points.size) {
lineTo(points[index].x, points[index].y)
}
}
canvas.drawPath(path, paint)
}
paths.forEach(::drawStroke)
drawStroke(currentPath)
return bitmap
}
fun bitmapToBlob(bitmap: Bitmap): ByteArray {
@ -69,20 +125,103 @@ fun SignaturePad(
.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
SignaturePadView(
onReady = { signaturePadAdapter = it },
Box(
modifier = Modifier
.fillMaxWidth(0.85f)
.height(180.dp)
.align(Alignment.CenterHorizontally)
.background(
color = if (selectedMode == SignatureMode.Digital) {
Color.White
} else {
Color.White.copy(alpha = 0.55f)
},
shape = RoundedCornerShape(16.dp)
)
.padding(12.dp)
.onSizeChanged { size ->
canvasSize = Size(size.width.toFloat(), size.height.toFloat())
}
.pointerInput(selectedMode) {
if (selectedMode == SignatureMode.Manual) return@pointerInput
detectDragGestures(
onDragStart = { startOffset ->
currentPath.add(startOffset)
},
onDrag = { change, _ ->
change.consume()
currentPath.add(change.position)
},
onDragEnd = {
paths.add(currentPath.toList())
currentPath.clear()
}
)
}
) {
Canvas(modifier = Modifier.fillMaxSize()) {
paths.forEach { points ->
if (points.size > 1) {
val path = Path().apply {
moveTo(points.first().x, points.first().y)
for (i in 1 until points.size) {
lineTo(points[i].x, points[i].y)
}
}
drawPath(
path = path,
color = signatureColor,
style = Stroke(width = strokeWidth)
)
}
}
if (currentPath.size > 1) {
val path = Path().apply {
moveTo(currentPath.first().x, currentPath.first().y)
for (i in 1 until currentPath.size) {
lineTo(currentPath[i].x, currentPath[i].y)
}
}
drawPath(
path = path,
color = signatureColor,
style = Stroke(width = strokeWidth)
)
}
}
}
Column(
modifier = Modifier
.fillMaxWidth()
.height(200.dp)
.border(width = 1.dp, color = AppColor.LegacyRed)
)
.height(150.dp)
) {
SignatureModeOption(
title = "Manual Sign",
subtitle = "Signature on Paper",
selected = selectedMode == SignatureMode.Manual,
onClick = { selectedMode = SignatureMode.Manual },
modifier = Modifier.fillMaxWidth()
)
Spacer(Modifier.height(12.dp))
SignatureModeOption(
title = "Digital Sign",
subtitle = "Draw on pad",
selected = selectedMode == SignatureMode.Digital,
onClick = { selectedMode = SignatureMode.Digital },
modifier = Modifier.fillMaxWidth()
)
}
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(12.dp)
) {
OutlinedButton(
onClick = { signaturePadAdapter?.clear() },
onClick = { clearSignature() },
modifier = Modifier
.weight(1f)
.height(50.dp)
@ -92,10 +231,9 @@ fun SignaturePad(
Button(
onClick = {
val rawBitmap = signaturePadAdapter?.getSignatureBitmap()
val bitmap = rawBitmap?.let { scaleBitmap(it) }
val bitmap = buildBitmap()
val signatureBlob = bitmap?.let { bitmapToBlob(it) }
onConfirm(bitmap, signatureBlob)
onConfirm(selectedMode, bitmap, signatureBlob)
},
modifier = Modifier
.weight(1f)

View File

@ -1189,11 +1189,10 @@ public class PrintReceipt {
private void printSignature(Bitmap signatureBitmap) {
try {
sunmiPrinterService.setAlignment(1, null);
int targetHeight = 200;
if (signatureBitmap.getHeight() > targetHeight) {
double ratio = 1.0 * targetHeight / signatureBitmap.getHeight();
int newWidth = (int) (signatureBitmap.getWidth() * ratio);
signatureBitmap = BitmapUtils.scale(signatureBitmap, newWidth, targetHeight);
if (signatureBitmap.getWidth() > 130) {
double size = 1.0 * signatureBitmap.getHeight() * 130 / signatureBitmap.getWidth();
int newHeight = (int) size;
signatureBitmap = BitmapUtils.scale(signatureBitmap, 130, newHeight);
}
sunmiPrinterService.printBitmap(signatureBitmap, null);
sunmiPrinterService.printText("\n", null);

View File

@ -954,12 +954,6 @@ public abstract class BaseXPrint {
if (signatureBlob != null && signatureBlob.length > 0) {
Bitmap signatureBitmap = android.graphics.BitmapFactory.decodeByteArray(signatureBlob, 0, signatureBlob.length);
if (signatureBitmap != null) {
int targetHeight = 200;
if (signatureBitmap.getHeight() > targetHeight) {
double ratio = 1.0 * targetHeight / signatureBitmap.getHeight();
int newWidth = (int) (signatureBitmap.getWidth() * ratio);
signatureBitmap = BitmapUtils.scale(signatureBitmap, newWidth, targetHeight);
}
sunmiPrinterService.printBitmap(signatureBitmap, innerResultCallback);
}
}