Compare commits

...

2 Commits

Author SHA1 Message Date
moon
6f61532741 signature fixed 2026-07-14 15:06:00 +06:30
moon
e0baafb84d fixed 0.00 places for amounts 2026-07-14 11:41:11 +06:30
8 changed files with 48 additions and 182 deletions

View File

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

View File

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

View File

@ -26,6 +26,7 @@ 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
@ -36,7 +37,6 @@ 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,7 +179,6 @@ fun AppNavGraph(
launchSingleTop = true
}
}
"Pre-Auth",
"Cash Adv" -> {
navController.navigate(Routes.Amount.createRoute(action)) {
@ -802,7 +801,14 @@ fun AppNavGraph(
}
private fun formatAmountForDisplay(amount: String?): String {
val normalizedAmount = amount.orEmpty()
val value = normalizedAmount.toLongOrNull() ?: return normalizedAmount.ifBlank { "0" }
return "%,d".format(value)
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)
}

View File

@ -1,15 +1,10 @@
package com.mob.utsmyanmar.ui.signature_pad
import android.graphics.Bitmap
import android.graphics.Paint
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.foundation.border
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
@ -24,82 +19,31 @@ 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: (SignatureMode, Bitmap?, signatureBlob: ByteArray?) -> Unit = { _, _, _ -> }
onConfirm: (Bitmap?, ByteArray?) -> Unit = { _, _ -> }
) {
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
var signaturePadAdapter: SignaturePadAdapter? = null
fun clearSignature() {
paths.clear()
currentPath.clear()
}
val scaledWidth = 300
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 scaleBitmap(source: Bitmap): Bitmap {
val ratio = scaledWidth.toFloat() / source.width
val height = (source.height * ratio).toInt()
return Bitmap.createScaledBitmap(source, scaledWidth, height, true)
}
fun bitmapToBlob(bitmap: Bitmap): ByteArray {
@ -125,103 +69,20 @@ fun SignaturePad(
.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
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(
SignaturePadView(
onReady = { signaturePadAdapter = it },
modifier = Modifier
.fillMaxWidth()
.height(150.dp)
) {
SignatureModeOption(
title = "Manual Sign",
subtitle = "Signature on Paper",
selected = selectedMode == SignatureMode.Manual,
onClick = { selectedMode = SignatureMode.Manual },
modifier = Modifier.fillMaxWidth()
.height(200.dp)
.border(width = 1.dp, color = AppColor.LegacyRed)
)
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 = { clearSignature() },
onClick = { signaturePadAdapter?.clear() },
modifier = Modifier
.weight(1f)
.height(50.dp)
@ -231,9 +92,10 @@ fun SignaturePad(
Button(
onClick = {
val bitmap = buildBitmap()
val rawBitmap = signaturePadAdapter?.getSignatureBitmap()
val bitmap = rawBitmap?.let { scaleBitmap(it) }
val signatureBlob = bitmap?.let { bitmapToBlob(it) }
onConfirm(selectedMode, bitmap, signatureBlob)
onConfirm(bitmap, signatureBlob)
},
modifier = Modifier
.weight(1f)

View File

@ -1189,10 +1189,11 @@ public class PrintReceipt {
private void printSignature(Bitmap signatureBitmap) {
try {
sunmiPrinterService.setAlignment(1, null);
if (signatureBitmap.getWidth() > 130) {
double size = 1.0 * signatureBitmap.getHeight() * 130 / signatureBitmap.getWidth();
int newHeight = (int) size;
signatureBitmap = BitmapUtils.scale(signatureBitmap, 130, newHeight);
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, null);
sunmiPrinterService.printText("\n", null);

View File

@ -954,6 +954,12 @@ 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);
}
}