Compare commits

..

2 Commits

Author SHA1 Message Date
moon
665bf1d5df signature pad 2026-07-01 11:39:55 +06:30
moon
f7510c9bad database add new column 2026-07-01 10:39:26 +06:30
12 changed files with 192 additions and 87 deletions

View File

@ -86,9 +86,19 @@ fun AppNavGraph(
} }
composable(Routes.Signature.route) { composable(Routes.Signature.route) {
val sharedViewModel: SharedViewModel = hiltViewModel(activity)
SignaturePad( SignaturePad(
onBack = { navController.popBackStack() }, onBack = { navController.popBackStack() },
onConfirm = { navController.popBackStack() } onConfirm = { _, bitmap, signatureBlob ->
sharedViewModel.signBitmap = bitmap
sharedViewModel.updateSignatureBlob(signatureBlob)
navController.navigate(Routes.TransactionResult.route) {
popUpTo(Routes.Signature.route) {
inclusive = true
}
launchSingleTop = true
}
}
) )
} }
@ -663,6 +673,14 @@ fun AppNavGraph(
launchSingleTop = true launchSingleTop = true
} }
}, },
onNavigateSignature = {
navController.navigate(Routes.Signature.route) {
popUpTo(Routes.Processing.route) {
inclusive = true
}
launchSingleTop = true
}
},
onNavigateMain = { onNavigateMain = {
navController.navigate(Routes.Dashboard.route) { navController.navigate(Routes.Dashboard.route) {
popUpTo(Routes.Dashboard.route) { popUpTo(Routes.Dashboard.route) {

View File

@ -2,21 +2,15 @@ package com.mob.utsmyanmar.ui.sending_to_host
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.mob.utsmyanmar.model.TransResultStatus import com.mob.utsmyanmar.model.TransResultStatus
import com.mob.utsmyanmar.viewmodel.SharedViewModel
import com.mob.utsmyanmar.viewmodel.TransProcessViewModel
import kotlinx.coroutines.delay
import androidx.compose.runtime.getValue
import com.mob.utsmyanmar.ui.transaction_result.TransactionResultState
import com.mob.utsmyanmar.viewmodel.ProcessingTransaction
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import androidx.compose.runtime.collectAsState
import com.mob.utsmyanmar.model.TransactionStatus import com.mob.utsmyanmar.model.TransactionStatus
import com.mob.utsmyanmar.ui.settlement.SettlementViewModel import com.mob.utsmyanmar.ui.settlement.SettlementViewModel
import com.mob.utsmyanmar.viewmodel.SharedViewModel
import com.mob.utsmyanmar.viewmodel.TransProcessViewModel
import com.utsmyanmar.paylibs.model.enums.TransCVM
import com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType import com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType
import kotlinx.coroutines.channels.Channel
private const val MOCK_HOST_DELAY_MS = 2000L private const val MOCK_HOST_DELAY_MS = 2000L
@ -26,6 +20,7 @@ fun ProcessingRoute(
transProcessViewModel: TransProcessViewModel, transProcessViewModel: TransProcessViewModel,
settlementViewModel: SettlementViewModel, settlementViewModel: SettlementViewModel,
onNavigateTransactionResult: () -> Unit, onNavigateTransactionResult: () -> Unit,
onNavigateSignature: () -> Unit,
onNavigateMain: ()-> Unit onNavigateMain: ()-> Unit
) { ) {
@ -60,7 +55,11 @@ fun ProcessingRoute(
when(transStatus) { when(transStatus) {
TransResultStatus.SUCCESS -> { TransResultStatus.SUCCESS -> {
sharedViewModel.payDetail.value = transProcessViewModel.payDetailResult.value sharedViewModel.payDetail.value = transProcessViewModel.payDetailResult.value
onNavigateTransactionResult() if (requiresSignature(sharedViewModel.payDetail.value)) {
onNavigateSignature()
} else {
onNavigateTransactionResult()
}
} }
TransResultStatus.FAIL -> { TransResultStatus.FAIL -> {
@ -75,12 +74,6 @@ fun ProcessingRoute(
} }
} }
if(sharedViewModel.transactionsType.value == TransactionsType.SETTLEMENT) { if(sharedViewModel.transactionsType.value == TransactionsType.SETTLEMENT) {
ProcessingScreen(settlementStatus.processingState) ProcessingScreen(settlementStatus.processingState)
} else { } else {
@ -89,3 +82,8 @@ fun ProcessingRoute(
} }
private fun requiresSignature(payDetail: com.utsmyanmar.paylibs.model.PayDetail?): Boolean {
return payDetail?.transCVM == TransCVM.SIGNATURE && !payDetail.isFreeSign
// return true;
}

View File

@ -1,5 +1,7 @@
package com.mob.utsmyanmar.ui.signature_pad package com.mob.utsmyanmar.ui.signature_pad
import android.graphics.Bitmap
import android.graphics.Paint
import androidx.compose.foundation.Canvas import androidx.compose.foundation.Canvas
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.detectDragGestures import androidx.compose.foundation.gestures.detectDragGestures
@ -27,34 +29,88 @@ import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Path import androidx.compose.ui.graphics.Path
import androidx.compose.ui.graphics.drawscope.Stroke import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.core.graphics.createBitmap
import com.mob.utsmyanmar.ui.components.appbar.AppBar import com.mob.utsmyanmar.ui.components.appbar.AppBar
import com.mob.utsmyanmar.ui.theme.MOBPOSTheme import com.mob.utsmyanmar.ui.theme.MOBPOSTheme
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 import com.mob.utsmyanmar.ui.theme.Color as AppColor
@Composable @Composable
fun SignaturePad( fun SignaturePad(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
signatureColor: Color = Color.Black, signatureColor: Color = Color.Black,
strokeWidth: Float = 8f,
onBack: () -> Unit = {}, onBack: () -> Unit = {},
onConfirm: () -> Unit = {} // signatureBlob is null for Digital mode (using stored signature) or when nothing was drawn
onConfirm: (SignatureMode, Bitmap?, signatureBlob: ByteArray?) -> Unit = { _, _, _ -> }
) { ) {
val paths = remember { mutableStateListOf<List<Offset>>() } val paths = remember { mutableStateListOf<List<Offset>>() }
val currentPath = remember { mutableStateListOf<Offset>() } val currentPath = remember { mutableStateListOf<Offset>() }
var selectedMode by remember { mutableStateOf(SignatureMode.Manual) } var selectedMode by remember { mutableStateOf(SignatureMode.Manual) }
var canvasSize by remember { mutableStateOf(Size.Zero) }
var strokeWidth = 6f
fun clearSignature() { fun clearSignature() {
paths.clear() paths.clear()
currentPath.clear() currentPath.clear()
} }
fun buildBitmap(): Bitmap? {
if (selectedMode == SignatureMode.Digital) 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) // fill white background first
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 {
ByteArrayOutputStream().use { stream ->
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream)
return stream.toByteArray()
}
}
Scaffold( Scaffold(
topBar = { topBar = {
AppBar( AppBar(
@ -73,10 +129,14 @@ fun SignaturePad(
) { ) {
Box( Box(
modifier = Modifier modifier = Modifier
.weight(1f) .fillMaxWidth(0.85f) // narrower than full width, adjust as needed
.fillMaxWidth() .height(180.dp) // fixed height instead of weight(1f)
.align(Alignment.CenterHorizontally)
.background(Color.White, RoundedCornerShape(16.dp)) .background(Color.White, RoundedCornerShape(16.dp))
.padding(12.dp) .padding(12.dp)
.onSizeChanged { size ->
canvasSize = Size(size.width.toFloat(), size.height.toFloat())
}
.pointerInput(Unit) { .pointerInput(Unit) {
detectDragGestures( detectDragGestures(
onDragStart = { startOffset -> onDragStart = { startOffset ->
@ -126,9 +186,10 @@ fun SignaturePad(
} }
} }
Column ( Column(
modifier = Modifier.fillMaxWidth().height(150.dp), modifier = Modifier
// horizontalArrangement = Arrangement.spacedBy(12.dp) .fillMaxWidth()
.height(150.dp)
) { ) {
SignatureModeOption( SignatureModeOption(
title = "Manual Sign", title = "Manual Sign",
@ -163,7 +224,15 @@ fun SignaturePad(
} }
Button( Button(
onClick = onConfirm, onClick = {
val bitmap = buildBitmap()
if (selectedMode == SignatureMode.Manual && bitmap == null) {
// nothing drawn yet on Manual mode — block confirm
return@Button
}
val signatureBlob = bitmap?.let { bitmapToBlob(it) }
onConfirm(selectedMode, bitmap, signatureBlob)
},
modifier = Modifier modifier = Modifier
.weight(1f) .weight(1f)
.height(50.dp), .height(50.dp),
@ -179,7 +248,7 @@ fun SignaturePad(
} }
} }
private enum class SignatureMode { enum class SignatureMode {
Manual, Manual,
Digital Digital
} }

View File

@ -4,9 +4,9 @@ import android.graphics.Bitmap
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.mob.utsmyanmar.model.SettlementType import com.mob.utsmyanmar.model.SettlementType
import com.mob.utsmyanmar.ui.processing_card.ProcessingCardState import com.utsmyanmar.baselib.db.dao.PayDetailDao
import com.mob.utsmyanmar.ui.sending_to_host.ProcessingState
import com.utsmyanmar.baselib.network.model.sirius.SiriusRequest import com.utsmyanmar.baselib.network.model.sirius.SiriusRequest
import com.utsmyanmar.baselib.network.model.sirius.SiriusResponse import com.utsmyanmar.baselib.network.model.sirius.SiriusResponse
import com.utsmyanmar.baselib.repo.Repository import com.utsmyanmar.baselib.repo.Repository
@ -21,19 +21,20 @@ import com.utsmyanmar.paylibs.utils.AccountType
import com.utsmyanmar.paylibs.utils.PrintStatus import com.utsmyanmar.paylibs.utils.PrintStatus
import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation
import com.utsmyanmar.paylibs.utils.enums.HostType import com.utsmyanmar.paylibs.utils.enums.HostType
import com.utsmyanmar.paylibs.utils.iso_utils.TransactionType
import com.utsmyanmar.paylibs.utils.enums.TransMenu import com.utsmyanmar.paylibs.utils.enums.TransMenu
import com.utsmyanmar.paylibs.utils.iso_utils.TransactionType
import com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType import com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import io.reactivex.rxjava3.core.Observable import io.reactivex.rxjava3.core.Observable
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch
import sunmi.sunmiui.utils.LogUtil import sunmi.sunmiui.utils.LogUtil
import javax.inject.Inject import javax.inject.Inject
@HiltViewModel @HiltViewModel
class SharedViewModel @Inject constructor( class SharedViewModel @Inject constructor(
private val repository: Repository private val repository: Repository,
private val payDetailDao: PayDetailDao
) : ViewModel() { ) : ViewModel() {
companion object { companion object {
@ -316,7 +317,7 @@ class SharedViewModel @Inject constructor(
private fun printReceipt(isMerchantCopy: Boolean) { private fun printReceipt(isMerchantCopy: Boolean) {
PrintXReceipt.getInstance() PrintXReceipt.getInstance()
.printSmileReceipt(payDetail.value, isMerchantCopy, object : PrintXStatus { .printSmileReceipt(payDetail.value, isMerchantCopy, signBitmap, object : PrintXStatus {
override fun onSuccess() { override fun onSuccess() {
@ -351,7 +352,7 @@ class SharedViewModel @Inject constructor(
} }
PrintXReceipt.getInstance() PrintXReceipt.getInstance()
.printSmileReceipt(getPayDetail(), isFirstPrint, object : PrintXStatus { .printSmileReceipt(getPayDetail(), isFirstPrint, signBitmap, object : PrintXStatus {
override fun onSuccess() { override fun onSuccess() {
@ -666,4 +667,13 @@ class SharedViewModel @Inject constructor(
SystemParamsOperation.getInstance().wavePayStatus SystemParamsOperation.getInstance().wavePayStatus
) )
} }
fun updateSignatureBlob(signatureBlob: ByteArray?) {
val current = payDetail.value ?: return
current.signatureBlob = signatureBlob
payDetail.value = current
viewModelScope.launch(Dispatchers.IO) {
payDetailDao.updateSignatureBlob(current.voucherNo, signatureBlob)
}
}
} }

View File

@ -25,9 +25,9 @@ import com.utsmyanmar.baselib.db.model.RidEntity;
import com.utsmyanmar.paylibs.model.PayDetail; import com.utsmyanmar.paylibs.model.PayDetail;
@Database(entities = {PayDetail.class, ChipAidEntity.class, PayWaveEntity.class, PayPassEntity.class, QuickPassEntity.class, RidEntity.class, MPUCtlsEntity.class, BinEntity.class},version = 12,exportSchema = false) @Database(entities = {PayDetail.class, ChipAidEntity.class, PayWaveEntity.class, PayPassEntity.class, QuickPassEntity.class, RidEntity.class, MPUCtlsEntity.class, BinEntity.class}, version = 13, exportSchema = false)
@TypeConverters({SettleDataConverter.class, TransCVMConverter.class, CardSchemeConverter.class}) @TypeConverters({SettleDataConverter.class, TransCVMConverter.class, CardSchemeConverter.class})
public abstract class AppDatabase extends RoomDatabase { public abstract class AppDatabase extends RoomDatabase {
public static volatile AppDatabase INSTANCE; public static volatile AppDatabase INSTANCE;
public abstract PayDetailDao payDetailDao(); public abstract PayDetailDao payDetailDao();

View File

@ -5,13 +5,9 @@ import androidx.room.Dao;
import androidx.room.Delete; import androidx.room.Delete;
import androidx.room.Insert; import androidx.room.Insert;
import androidx.room.Query; import androidx.room.Query;
import androidx.room.Transaction;
import androidx.room.Update; import androidx.room.Update;
import com.utsmyanmar.paylibs.model.PayDetail; import com.utsmyanmar.paylibs.model.PayDetail;
import com.utsmyanmar.paylibs.system.SingleLiveEvent;
import com.utsmyanmar.paylibs.utils.iso_utils.TransactionType;
import com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType;
import java.util.List; import java.util.List;
@ -157,4 +153,7 @@ public interface PayDetailDao {
@Query("SELECT * FROM paydetail ORDER BY PID DESC") @Query("SELECT * FROM paydetail ORDER BY PID DESC")
LiveData<List<PayDetail>> getAllTrans(); LiveData<List<PayDetail>> getAllTrans();
@Query("UPDATE paydetail SET signatureBlob = :blob WHERE voucherNo = :voucherNo")
void updateSignatureBlob(String voucherNo, byte[] blob);
} }

View File

@ -1,10 +1,6 @@
package com.utsmyanmar.baselib.di; package com.utsmyanmar.baselib.di;
import static com.utsmyanmar.baselib.db.AppDatabase.INSTANCE;
import android.app.Application; import android.app.Application;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
@ -16,8 +12,6 @@ import androidx.room.Room;
import androidx.room.RoomDatabase; import androidx.room.RoomDatabase;
import androidx.room.migration.Migration; import androidx.room.migration.Migration;
import androidx.sqlite.db.SupportSQLiteDatabase; import androidx.sqlite.db.SupportSQLiteDatabase;
import androidx.sqlite.db.SupportSQLiteOpenHelper;
import com.utsmyanmar.baselib.db.AppDatabase; import com.utsmyanmar.baselib.db.AppDatabase;
import com.utsmyanmar.baselib.db.dao.BinDao; import com.utsmyanmar.baselib.db.dao.BinDao;
@ -28,8 +22,6 @@ import com.utsmyanmar.baselib.db.dao.PayPassDao;
import com.utsmyanmar.baselib.db.dao.PayWaveDao; import com.utsmyanmar.baselib.db.dao.PayWaveDao;
import com.utsmyanmar.baselib.db.dao.QuickPassDao; import com.utsmyanmar.baselib.db.dao.QuickPassDao;
import com.utsmyanmar.baselib.db.dao.RidDao; import com.utsmyanmar.baselib.db.dao.RidDao;
import com.utsmyanmar.baselib.util.SecureKeyUtil;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@ -39,13 +31,12 @@ import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException; import java.security.NoSuchProviderException;
import java.security.UnrecoverableEntryException; import java.security.UnrecoverableEntryException;
import java.security.cert.CertificateException;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import javax.crypto.KeyGenerator; import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey; import javax.crypto.SecretKey;
import javax.inject.Provider;
import javax.inject.Singleton; import javax.inject.Singleton;
import java.security.cert.CertificateException;
import dagger.Module; import dagger.Module;
import dagger.Provides; import dagger.Provides;
@ -353,6 +344,14 @@ public class DatabaseModule {
} }
}; };
static final Migration MIGRATION_12_13 = new Migration(12, 13) {
@Override
public void migrate(SupportSQLiteDatabase database) {
database.execSQL("ALTER TABLE paydetail "
+ " ADD COLUMN signatureBlob BLOB");
}
};
private static byte[] getDatabaseKey(Context context) throws KeyStoreException, CertificateException, private static byte[] getDatabaseKey(Context context) throws KeyStoreException, CertificateException,
NoSuchAlgorithmException, IOException, NoSuchProviderException, NoSuchAlgorithmException, IOException, NoSuchProviderException,
InvalidAlgorithmParameterException, UnrecoverableEntryException { InvalidAlgorithmParameterException, UnrecoverableEntryException {
@ -398,13 +397,14 @@ public class DatabaseModule {
@Provides @Provides
@Singleton @Singleton
public static AppDatabase provideAppDatabase(Application application) { public static AppDatabase provideAppDatabase(Application application) {
return Room.databaseBuilder(application, AppDatabase.class,"yoma") return Room.databaseBuilder(application, AppDatabase.class, "mob")
// .fallbackToDestructiveMigration() // .fallbackToDestructiveMigration()
.addMigrations(MIGRATION_7_8) .addMigrations(MIGRATION_7_8)
.addMigrations(MIGRATION_8_9) .addMigrations(MIGRATION_8_9)
.addMigrations(MIGRATION_9_10) .addMigrations(MIGRATION_9_10)
.addMigrations(MIGRATION_10_11) .addMigrations(MIGRATION_10_11)
.addMigrations(MIGRATION_11_12) .addMigrations(MIGRATION_11_12)
.addMigrations(MIGRATION_12_13)
.allowMainThreadQueries() .allowMainThreadQueries()
.addCallback(new RoomDatabase.Callback() { .addCallback(new RoomDatabase.Callback() {
@Override @Override
@ -414,9 +414,9 @@ public class DatabaseModule {
@Override @Override
public void run() { public void run() {
/* /*
* init here * init here
* */ * */
} }
}); });
} }
@ -425,8 +425,6 @@ public class DatabaseModule {
} }
private static boolean isDatabaseFileCorruptOrWrongKey(Application application) { private static boolean isDatabaseFileCorruptOrWrongKey(Application application) {
File dbFile = application.getDatabasePath(DATABASE_NAME); File dbFile = application.getDatabasePath(DATABASE_NAME);
return dbFile.exists() && dbFile.length() > 0; return dbFile.exists() && dbFile.length() > 0;
@ -470,7 +468,7 @@ public class DatabaseModule {
@Provides @Provides
@Singleton @Singleton
public static PayDetailDao providePayDetailDao(AppDatabase appDatabase){ public static PayDetailDao providePayDetailDao(AppDatabase appDatabase) {
return appDatabase.payDetailDao(); return appDatabase.payDetailDao();
} }
@ -507,9 +505,13 @@ public class DatabaseModule {
@Provides @Provides
@Singleton @Singleton
public static MPUCtlsDao provideMPUCtlsDao(AppDatabase appDatabase) { return appDatabase.mpuCtlsDao(); } public static MPUCtlsDao provideMPUCtlsDao(AppDatabase appDatabase) {
return appDatabase.mpuCtlsDao();
}
@Provides @Provides
@Singleton @Singleton
public static BinDao provideBinDao(AppDatabase appDatabase) { return appDatabase.binDao(); } public static BinDao provideBinDao(AppDatabase appDatabase) {
return appDatabase.binDao();
}
} }

View File

@ -74,6 +74,7 @@ public class PayDetail implements Serializable {
public String cardOrgCode = ""; // International credit card company code (domain 63.1) public String cardOrgCode = ""; // International credit card company code (domain 63.1)
public String reference = ""; // Remarks public String reference = ""; // Remarks
public String eSignHexData = ""; // Electronic signature hexadecimal data public String eSignHexData = ""; // Electronic signature hexadecimal data
private byte[] signatureBlob; // Signature store as blob type
public int transPlatform; // Supported trading platforms: 0: bank card 1: Alipay 2: WeChat 3: UnionPay wallet public int transPlatform; // Supported trading platforms: 0: bank card 1: Alipay 2: WeChat 3: UnionPay wallet
public int transactionType; // 1: Consumption 2: Consumption cancellation; 3: Return 4. Pre-authorization 5. Pre-authorization cancellation 6. Pre-authorization completed 7. Pre-authorization completed cancellation 8. Settlement 9. Sign-in 10. Sign-out 11. Transaction query public int transactionType; // 1: Consumption 2: Consumption cancellation; 3: Return 4. Pre-authorization 5. Pre-authorization cancellation 6. Pre-authorization completed 7. Pre-authorization completed cancellation 8. Settlement 9. Sign-in 10. Sign-out 11. Transaction query
@ -1359,4 +1360,11 @@ public class PayDetail implements Serializable {
this.currency = currency; this.currency = currency;
} }
public byte[] getSignatureBlob() {
return signatureBlob;
}
public void setSignatureBlob(byte[] signatureBlob) {
this.signatureBlob = signatureBlob;
}
} }

View File

@ -722,8 +722,7 @@ public abstract class BaseXPrint {
sunmiPrinterService.sendRAWData(boldOn(), innerResultCallback); sunmiPrinterService.sendRAWData(boldOn(), innerResultCallback);
sunmiPrinterService.setAlignment(1, innerResultCallback); sunmiPrinterService.setAlignment(1, innerResultCallback);
// default String cvmText;
String cvmText = "NO SIGNATURE REQUIRED";
switch (payDetail.getTransCVM()) { switch (payDetail.getTransCVM()) {
case SIGNATURE: cvmText = "SIGN : -----------------------------"; case SIGNATURE: cvmText = "SIGN : -----------------------------";
break; break;
@ -735,9 +734,10 @@ public abstract class BaseXPrint {
cvmText = "NO SIGNATURE REQUIRED"; cvmText = "NO SIGNATURE REQUIRED";
break; break;
} }
addSignatureBitmap(payDetail);
sunmiPrinterService.printText(cvmText, innerResultCallback); sunmiPrinterService.printText(cvmText, innerResultCallback);
// if (payDetail.getIsFreeSign()) { // if (payDetail.getIsFreeSign()) {
// sunmiPrinterService.printText("NO SIGNATURE REQUIRED", innerResultCallback); // sunmiPrinterService.printText("NO SIGNATURE REQUIRED", innerResultCallback);
// } else { // } else {
@ -948,4 +948,14 @@ public abstract class BaseXPrint {
} }
}; };
private void addSignatureBitmap(PayDetail payDetail) throws RemoteException {
byte[] signatureBlob = payDetail.getSignatureBlob();
if (signatureBlob != null && signatureBlob.length > 0) {
Bitmap signatureBitmap = android.graphics.BitmapFactory.decodeByteArray(signatureBlob, 0, signatureBlob.length);
if (signatureBitmap != null) {
sunmiPrinterService.printBitmap(signatureBitmap, innerResultCallback);
}
}
}
} }

View File

@ -2,6 +2,7 @@ package com.utsmyanmar.paylibs.print.printx;
import com.utsmyanmar.paylibs.model.PayDetail; import com.utsmyanmar.paylibs.model.PayDetail;
import com.utsmyanmar.paylibs.utils.enums.HostType; import com.utsmyanmar.paylibs.utils.enums.HostType;
import android.graphics.Bitmap;
import java.util.List; import java.util.List;
@ -9,7 +10,7 @@ public interface PrintX {
void printSimple(); void printSimple();
void printSmileReceipt(PayDetail payDetail, boolean isMerchantCopy,PrintXStatus printXStatus); void printSmileReceipt(PayDetail payDetail, boolean isMerchantCopy, Bitmap signBitmap, PrintXStatus printXStatus);
void printISOReceipt(PayDetail payDetail); void printISOReceipt(PayDetail payDetail);

View File

@ -3,16 +3,8 @@ package com.utsmyanmar.paylibs.print.printx;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.RemoteException; import android.os.RemoteException;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
import com.utsmyanmar.paylibs.Constant; import com.utsmyanmar.paylibs.Constant;
import com.utsmyanmar.paylibs.PayLibsUtils; import com.utsmyanmar.paylibs.PayLibsUtils;
import com.utsmyanmar.paylibs.R; import com.utsmyanmar.paylibs.R;
@ -31,10 +23,8 @@ import com.utsmyanmar.paylibs.utils.enums.HostType;
import com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType; import com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType;
import com.utsmyanmar.paylibs.utils.print_utils.BitmapUtils; import com.utsmyanmar.paylibs.utils.print_utils.BitmapUtils;
import java.io.ByteArrayOutputStream;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Date; import java.util.Date;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
@ -120,7 +110,7 @@ public class PrintXImpl extends BaseXPrint implements PrintX {
@Override @Override
public void printSmileReceipt(PayDetail payDetail, boolean isMerchantCopy,PrintXStatus printXStatus) { public void printSmileReceipt(PayDetail payDetail, boolean isMerchantCopy, Bitmap signBitmap, PrintXStatus printXStatus) {
try { try {
this.callbackStatus = printXStatus; this.callbackStatus = printXStatus;
@ -129,9 +119,9 @@ public class PrintXImpl extends BaseXPrint implements PrintX {
} else if (payDetail.getTransactionType() == TransactionsType.SETTLEMENT.value) { } else if (payDetail.getTransactionType() == TransactionsType.SETTLEMENT.value) {
printSmileSettlementReport(payDetail,printXStatus); printSmileSettlementReport(payDetail,printXStatus);
} else if (payDetail.getICC55() == null || payDetail.getICC55().isEmpty() || payDetail.getAccountType().equals(CardScheme.MPU_CTLS.name)) { } else if (payDetail.getICC55() == null || payDetail.getICC55().isEmpty() || payDetail.getAccountType().equals(CardScheme.MPU_CTLS.name)) {
printSmileMPUReceipt(payDetail, isMerchantCopy); printSmileMPUReceipt(payDetail, isMerchantCopy, signBitmap);
} else if (!payDetail.getICC55().equals("")) { } else if (!payDetail.getICC55().equals("")) {
printSmileEMVReceipt(payDetail, isMerchantCopy); printSmileEMVReceipt(payDetail, isMerchantCopy, signBitmap);
} }
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
@ -256,7 +246,7 @@ public class PrintXImpl extends BaseXPrint implements PrintX {
} }
} }
private void printSmileMPUReceipt(PayDetail payDetail, boolean isMerchantCopy) { private void printSmileMPUReceipt(PayDetail payDetail, boolean isMerchantCopy, Bitmap signBitmap) {
try { try {
setHeight(0x12); setHeight(0x12);
@ -268,6 +258,7 @@ public class PrintXImpl extends BaseXPrint implements PrintX {
if (payDetail.getTradeAnswerCode().equals(Constant.ANSWER_CODE_APPROVED) || payDetail.getTradeAnswerCode().equals(Constant.ANSWER_CODE_ACCEPT)) { if (payDetail.getTradeAnswerCode().equals(Constant.ANSWER_CODE_APPROVED) || payDetail.getTradeAnswerCode().equals(Constant.ANSWER_CODE_ACCEPT)) {
printTransFooter(false,payDetail, isMerchantCopy); printTransFooter(false,payDetail, isMerchantCopy);
// printSignatureBlock(payDetail, signBitmap);
} else { } else {
String receiptFooter = SystemParamsOperation.getInstance().getReceiptFooter(); String receiptFooter = SystemParamsOperation.getInstance().getReceiptFooter();
sunmiPrinterService.printText("\n", null); sunmiPrinterService.printText("\n", null);
@ -282,7 +273,7 @@ public class PrintXImpl extends BaseXPrint implements PrintX {
} }
} }
private void printSmileEMVReceipt(PayDetail payDetail, boolean isMerchantCopy) { private void printSmileEMVReceipt(PayDetail payDetail, boolean isMerchantCopy, Bitmap signBitmap) {
try { try {
setHeight(0x12); setHeight(0x12);
printLogo(); printLogo();
@ -292,6 +283,7 @@ public class PrintXImpl extends BaseXPrint implements PrintX {
if (payDetail.getTradeAnswerCode().equals(Constant.ANSWER_CODE_APPROVED) || payDetail.getTradeAnswerCode().equals(Constant.ANSWER_CODE_ACCEPT)) { if (payDetail.getTradeAnswerCode().equals(Constant.ANSWER_CODE_APPROVED) || payDetail.getTradeAnswerCode().equals(Constant.ANSWER_CODE_ACCEPT)) {
printTransFooter(false,payDetail, isMerchantCopy); printTransFooter(false,payDetail, isMerchantCopy);
// printSignatureBlock(payDetail, signBitmap);
} else { } else {
String receiptFooter = SystemParamsOperation.getInstance().getReceiptFooter(); String receiptFooter = SystemParamsOperation.getInstance().getReceiptFooter();
sunmiPrinterService.setFontSize(textSize, null); sunmiPrinterService.setFontSize(textSize, null);
@ -307,13 +299,10 @@ public class PrintXImpl extends BaseXPrint implements PrintX {
} }
} }
private void printTransDetailWave(PayDetail payDetail) { private void printTransDetailWave(PayDetail payDetail) {
try { try {
// payDetail.getCustomerMobile() // as channel type // payDetail.getCustomerMobile() // as channel typeိ
if(payDetail.getTransactionType() == TransactionsType.WAVEPAY.value && payDetail.getQrTransStatus() == 1 ) { if(payDetail.getTransactionType() == TransactionsType.WAVEPAY.value && payDetail.getQrTransStatus() == 1 ) {
sunmiPrinterService.printColumnsString(new String[]{"PAYMENT TYPE ",": "+ (payDetail.getCustomerMobile() == null || payDetail.getCustomerMobile().isEmpty() ? "-": payDetail.getCustomerMobile().toUpperCase())}, new int[]{2, 2}, new int[]{0, 0}, null); sunmiPrinterService.printColumnsString(new String[]{"PAYMENT TYPE ",": "+ (payDetail.getCustomerMobile() == null || payDetail.getCustomerMobile().isEmpty() ? "-": payDetail.getCustomerMobile().toUpperCase())}, new int[]{2, 2}, new int[]{0, 0}, null);

View File

@ -3,6 +3,7 @@ package com.utsmyanmar.paylibs.print.printx;
import com.utsmyanmar.paylibs.model.PayDetail; import com.utsmyanmar.paylibs.model.PayDetail;
import com.utsmyanmar.paylibs.utils.enums.HostType; import com.utsmyanmar.paylibs.utils.enums.HostType;
import android.graphics.Bitmap;
import java.util.List; import java.util.List;
public class PrintXReceipt { public class PrintXReceipt {
@ -21,8 +22,8 @@ public class PrintXReceipt {
printX.printSimple(); printX.printSimple();
} }
public void printSmileReceipt(PayDetail payDetail,boolean isMerchantCopy,PrintXStatus printXStatus) { public void printSmileReceipt(PayDetail payDetail,boolean isMerchantCopy, Bitmap signBitmap,PrintXStatus printXStatus) {
printX.printSmileReceipt(payDetail,isMerchantCopy,printXStatus); printX.printSmileReceipt(payDetail,isMerchantCopy,signBitmap,printXStatus);
} }
public void printSmileSettlementReport(PayDetail payDetail,PrintXStatus printXStatus) { public void printSmileSettlementReport(PayDetail payDetail,PrintXStatus printXStatus) {