Compare commits
10 Commits
8ae270b625
...
4a417c3603
| Author | SHA1 | Date | |
|---|---|---|---|
| 4a417c3603 | |||
| a5f0139307 | |||
| 0abcf81d79 | |||
| d931dfeff9 | |||
| 79515f1205 | |||
| 427c75b93c | |||
| 31ab97af95 | |||
| 3bf1ae3648 | |||
| 0c168dd33e | |||
| 083d642186 |
61
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
61
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<profile version="1.0">
|
||||||
|
<option name="myName" value="Project Default" />
|
||||||
|
<inspection_tool class="ComposePreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="ComposePreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="ComposePreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="ComposePreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="GlancePreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="GlancePreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="GlancePreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="GlancePreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="PreviewAnnotationInFunctionWithParameters" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="PreviewApiLevelMustBeValid" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="PreviewDeviceShouldUseNewSpec" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="PreviewFontScaleMustBeGreaterThanZero" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="PreviewMultipleParameterProviders" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="PreviewParameterProviderOnFirstParameter" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="PreviewPickerAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
</profile>
|
||||||
|
</component>
|
||||||
@ -12,18 +12,29 @@ android {
|
|||||||
applicationId = "com.example.mypos"
|
applicationId = "com.example.mypos"
|
||||||
minSdk = 23
|
minSdk = 23
|
||||||
targetSdk = 36
|
targetSdk = 36
|
||||||
versionCode = 1
|
versionCode = 2
|
||||||
versionName = "1.0"
|
versionName = "1.0"
|
||||||
buildConfigField ("String", "ACTIVATION_CODE", "\"\"")
|
buildConfigField ("String", "ACTIVATION_CODE", "\"\"")
|
||||||
buildConfigField ("String", "APPLICATION_TOKEN", "\"\"")
|
buildConfigField ("String", "APPLICATION_TOKEN", "\"\"")
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
signingConfigs {
|
||||||
|
create("release") {
|
||||||
|
storeFile = file("mypos.jks")
|
||||||
|
storePassword = "app644223"
|
||||||
|
keyAlias = "pos"
|
||||||
|
keyPassword = "app644223"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
debug {
|
debug {
|
||||||
|
buildConfigField ("String", "ACTIVATION_CODE", "\"436596309\"")
|
||||||
buildConfigField ("String", "APPLICATION_TOKEN", "\"mk_MbQ92RRHEOcGFRIf9/R1A\"")
|
buildConfigField ("String", "APPLICATION_TOKEN", "\"mk_MbQ92RRHEOcGFRIf9/R1A\"")
|
||||||
}
|
}
|
||||||
release {
|
release {
|
||||||
|
signingConfig = signingConfigs.getByName("release")
|
||||||
isMinifyEnabled = false
|
isMinifyEnabled = false
|
||||||
proguardFiles(
|
proguardFiles(
|
||||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||||
@ -46,7 +57,8 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(files("libs/AditumSdkIntegration-2.3.6.67824-release.aar"))
|
// implementation(files("libs/AditumSdkIntegration-2.3.4.860743-release.aar"))
|
||||||
|
implementation(files("libs/AditumSdkIntegration-2.3.7-App2App.763915-release.aar"))
|
||||||
implementation("com.google.code.gson:gson:2.13.1")
|
implementation("com.google.code.gson:gson:2.13.1")
|
||||||
implementation("androidx.datastore:datastore-preferences:1.1.7")
|
implementation("androidx.datastore:datastore-preferences:1.1.7")
|
||||||
implementation(libs.androidx.core.ktx)
|
implementation(libs.androidx.core.ktx)
|
||||||
|
|||||||
BIN
app/libs/AditumSdkIntegration-2.3.4.860743-release.aar
Normal file
BIN
app/libs/AditumSdkIntegration-2.3.4.860743-release.aar
Normal file
Binary file not shown.
BIN
app/libs/AditumSdkIntegration-2.3.7-App2App.763915-release.aar
Normal file
BIN
app/libs/AditumSdkIntegration-2.3.7-App2App.763915-release.aar
Normal file
Binary file not shown.
BIN
app/mypos.jks
Normal file
BIN
app/mypos.jks
Normal file
Binary file not shown.
@ -2,6 +2,10 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<queries>
|
||||||
|
<package android:name="br.com.aditum.smartpostef" />
|
||||||
|
</queries>
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".services.PaymentApplication"
|
android:name=".services.PaymentApplication"
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
|
|||||||
@ -1,25 +1,29 @@
|
|||||||
package com.example.mypos
|
package com.example.mypos
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.activity.enableEdgeToEdge
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.material3.Scaffold
|
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import com.example.mypos.models.PaymentViewModel
|
||||||
|
import com.example.mypos.screen.PaymentScreen
|
||||||
|
import com.example.mypos.services.PaymentApplication
|
||||||
import com.example.mypos.ui.theme.POSTheme
|
import com.example.mypos.ui.theme.POSTheme
|
||||||
|
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
enableEdgeToEdge()
|
val context = this
|
||||||
|
var paymentApplication = application as PaymentApplication
|
||||||
|
var paymentViewModel = ViewModelProvider(
|
||||||
|
this,
|
||||||
|
PaymentViewModel.provideFactory(paymentApplication, context)
|
||||||
|
)[PaymentViewModel::class.java]
|
||||||
setContent {
|
setContent {
|
||||||
POSTheme {
|
POSTheme {
|
||||||
|
PaymentScreen(paymentViewModel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
12
app/src/main/java/com/example/mypos/data/PaymentRegister.kt
Normal file
12
app/src/main/java/com/example/mypos/data/PaymentRegister.kt
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package com.example.mypos.data
|
||||||
|
|
||||||
|
import br.com.aditum.data.v2.enums.AbecsCommands
|
||||||
|
import br.com.aditum.data.v2.enums.TransactionStatus
|
||||||
|
|
||||||
|
interface PaymentRegister {
|
||||||
|
fun notification(
|
||||||
|
message: String?,
|
||||||
|
transactionStatus: TransactionStatus?,
|
||||||
|
command: AbecsCommands?
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -1,31 +1,681 @@
|
|||||||
package com.example.mypos.models
|
package com.example.mypos.models
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.datastore.preferences.core.edit
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import br.com.aditum.data.v2.IPaymentCallback
|
||||||
|
import br.com.aditum.data.v2.enums.AbecsCommands
|
||||||
|
import br.com.aditum.data.v2.enums.InstallmentType
|
||||||
|
import br.com.aditum.data.v2.enums.PayOperationType
|
||||||
|
import br.com.aditum.data.v2.enums.PaymentType
|
||||||
|
import br.com.aditum.data.v2.enums.TransactionStatus
|
||||||
|
import br.com.aditum.data.v2.model.Charge
|
||||||
|
import br.com.aditum.data.v2.model.MerchantData
|
||||||
|
import br.com.aditum.data.v2.model.PinpadMessages
|
||||||
|
import br.com.aditum.data.v2.model.callbacks.GetClearDataFinishedCallback
|
||||||
|
import br.com.aditum.data.v2.model.callbacks.GetClearDataRequest
|
||||||
|
import br.com.aditum.data.v2.model.callbacks.GetMenuSelectionFinishedCallback
|
||||||
|
import br.com.aditum.data.v2.model.callbacks.GetMenuSelectionRequest
|
||||||
|
import br.com.aditum.data.v2.model.cancelation.CancelationRequest
|
||||||
|
import br.com.aditum.data.v2.model.cancelation.CancelationResponse
|
||||||
|
import br.com.aditum.data.v2.model.cancelation.CancelationResponseCallback
|
||||||
|
import br.com.aditum.data.v2.model.deactivation.DeactivationResponseCallback
|
||||||
|
import br.com.aditum.data.v2.model.init.InitRequest
|
||||||
|
import br.com.aditum.data.v2.model.init.InitResponse
|
||||||
|
import br.com.aditum.data.v2.model.init.InitResponseCallback
|
||||||
|
import br.com.aditum.data.v2.model.payment.PaymentRequest
|
||||||
|
import br.com.aditum.data.v2.model.payment.PaymentResponse
|
||||||
|
import br.com.aditum.data.v2.model.payment.PaymentResponseCallback
|
||||||
|
import br.com.aditum.data.v2.model.transactions.ConfirmTransactionCallback
|
||||||
|
import br.com.aditum.data.v2.model.transactions.PendingTransactionsCallback
|
||||||
|
import com.example.mypos.BuildConfig
|
||||||
|
import com.example.mypos.data.AditumError
|
||||||
|
import com.example.mypos.preferences.PrefKeys
|
||||||
|
import com.example.mypos.preferences.dataStore
|
||||||
import com.example.mypos.services.PaymentApplication
|
import com.example.mypos.services.PaymentApplication
|
||||||
|
import com.google.gson.Gson
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.flow.firstOrNull
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
|
import kotlin.coroutines.resume
|
||||||
|
import kotlin.coroutines.resumeWithException
|
||||||
|
|
||||||
|
enum class SdkState {
|
||||||
|
NOT_INITIALIZED,
|
||||||
|
INITIALIZING,
|
||||||
|
IDLE,
|
||||||
|
PROCESSING,
|
||||||
|
PAYING,
|
||||||
|
CANCELING
|
||||||
|
}
|
||||||
|
|
||||||
class PaymentViewModel(
|
class PaymentViewModel(
|
||||||
private val application: PaymentApplication,
|
private val application: PaymentApplication,
|
||||||
private val context: Context
|
private val context: Context
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
private val paymentApplication = application as PaymentApplication
|
private val paymentApplication = application
|
||||||
|
private val gson = Gson()
|
||||||
|
private val TAG = "PaymentViewModel"
|
||||||
|
|
||||||
|
// Estado do SDK
|
||||||
|
private val _sdkState = MutableStateFlow(SdkState.NOT_INITIALIZED)
|
||||||
|
val sdkState: StateFlow<SdkState> = _sdkState.asStateFlow()
|
||||||
|
|
||||||
|
// Estado de conexão do serviço
|
||||||
private val _isServiceConnected = MutableStateFlow(false)
|
private val _isServiceConnected = MutableStateFlow(false)
|
||||||
val isServiceConnected: StateFlow<Boolean> = _isServiceConnected.asStateFlow()
|
val isServiceConnected: StateFlow<Boolean> = _isServiceConnected.asStateFlow()
|
||||||
|
|
||||||
|
// Mensagens e dados do PIN pad
|
||||||
|
private val _messageFlow = MutableStateFlow("")
|
||||||
|
val messageFlow: StateFlow<String> = _messageFlow.asStateFlow()
|
||||||
|
private val _displayFlow = MutableStateFlow("")
|
||||||
|
val displayFlow: StateFlow<String> = _displayFlow.asStateFlow()
|
||||||
|
private val _pinLengthFlow = MutableStateFlow(0)
|
||||||
|
val pinLengthFlow: StateFlow<Int> = _pinLengthFlow.asStateFlow()
|
||||||
|
|
||||||
|
// Estado de erro
|
||||||
|
private val _errorFlow = MutableStateFlow<Pair<String, String?>?>(null)
|
||||||
|
val errorFlow: StateFlow<Pair<String, String?>?> = _errorFlow.asStateFlow()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
paymentApplication.isServiceConnectedFlow.collect { isConnected ->
|
paymentApplication.isServiceConnectedFlow.collect { isConnected ->
|
||||||
_isServiceConnected.value = isConnected
|
_isServiceConnected.value = isConnected
|
||||||
|
if (isConnected) {
|
||||||
|
try {
|
||||||
|
paymentApplication.communicationService?.registerPaymentCallback(notificationCallback)
|
||||||
|
?: Log.e(TAG, "Failed to register payment callback: communicationService is null")
|
||||||
|
// Tentar inicializar automaticamente quando o serviço se conectar
|
||||||
|
autoInitialize()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Error registering payment callback: ${e.message}")
|
||||||
|
setError(AditumError.SERVICE_NULL, e.message)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Quando o serviço desconecta, voltar para NOT_INITIALIZED
|
||||||
|
if (_sdkState.value != SdkState.NOT_INITIALIZED) {
|
||||||
|
_sdkState.value = SdkState.NOT_INITIALIZED
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun startService() {
|
private val notificationCallback = object : IPaymentCallback.Stub() {
|
||||||
paymentApplication.startAditumSdkService()
|
override fun notification(
|
||||||
|
message: String,
|
||||||
|
transactionStatus: TransactionStatus,
|
||||||
|
command: AbecsCommands
|
||||||
|
) {
|
||||||
|
val filteredMessage = message.replace("\\s+".toRegex(), " ").trim()
|
||||||
|
viewModelScope.launch {
|
||||||
|
if (command == AbecsCommands.Display && filteredMessage != "Callback Added") {
|
||||||
|
_displayFlow.emit(filteredMessage)
|
||||||
|
}
|
||||||
|
_messageFlow.emit(filteredMessage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun pinNotification(message: String, length: Int) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
_pinLengthFlow.emit(length)
|
||||||
|
_messageFlow.emit("PIN entrada: $length dígitos")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun startGetClearData(
|
||||||
|
clearDataRequest: GetClearDataRequest?,
|
||||||
|
finished: GetClearDataFinishedCallback?
|
||||||
|
) {
|
||||||
|
if (clearDataRequest != null) {
|
||||||
|
Log.d(TAG, "clearDataRequest: ${clearDataRequest.title} ${clearDataRequest.alphaNumeric} ${clearDataRequest.timeout}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun startGetMenuSelection(
|
||||||
|
clearDataRequest: GetMenuSelectionRequest?,
|
||||||
|
finished: GetMenuSelectionFinishedCallback?
|
||||||
|
) {
|
||||||
|
// Implementação customizada se necessário
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun qrCodeGenerated(qrCode: String, expirationTime: Int) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
_messageFlow.emit("QR Code gerado - Expira em: $expirationTime segundos")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setError(errorCode: String, errorMessage: String?) {
|
||||||
|
_errorFlow.value = Pair(errorCode, errorMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun clearError() {
|
||||||
|
_errorFlow.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun getActivationCode(): String? {
|
||||||
|
return context.dataStore.data.map { preferences ->
|
||||||
|
preferences[PrefKeys.ACTIVATION_CODE]
|
||||||
|
}.firstOrNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun getNewActivationCode(): String? {
|
||||||
|
return context.dataStore.data.map { preferences ->
|
||||||
|
preferences[PrefKeys.NEW_ACTIVATION_CODE]
|
||||||
|
}.firstOrNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun saveActivationCode(code: String?) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
context.dataStore.edit { preferences ->
|
||||||
|
if (code != null) {
|
||||||
|
preferences[PrefKeys.ACTIVATION_CODE] = code
|
||||||
|
} else {
|
||||||
|
preferences.remove(PrefKeys.ACTIVATION_CODE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun saveNewActivationCode(code: String?) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
context.dataStore.edit { preferences ->
|
||||||
|
if (code != null) {
|
||||||
|
preferences[PrefKeys.NEW_ACTIVATION_CODE] = code
|
||||||
|
} else {
|
||||||
|
preferences.remove(PrefKeys.NEW_ACTIVATION_CODE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun clearNewActivationCode() {
|
||||||
|
context.dataStore.edit { preferences ->
|
||||||
|
preferences.remove(PrefKeys.NEW_ACTIVATION_CODE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun autoInitialize() {
|
||||||
|
Log.d(TAG, "autoInitialize - Iniciando inicialização automática")
|
||||||
|
|
||||||
|
try {
|
||||||
|
val activationCode = getActivationCode()
|
||||||
|
val newActivationCode = getNewActivationCode()
|
||||||
|
|
||||||
|
Log.d(TAG, "autoInitialize - activation_code: $activationCode, new_activation_code: $newActivationCode")
|
||||||
|
|
||||||
|
if (activationCode == null && newActivationCode == null) {
|
||||||
|
Log.d(TAG, "autoInitialize - Nenhum código de ativação encontrado")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Se os códigos são iguais, não precisa fazer nada
|
||||||
|
if (activationCode != null && newActivationCode != null && activationCode == newActivationCode) {
|
||||||
|
Log.d(TAG, "autoInitialize - Códigos são iguais, limpando new_activation_code")
|
||||||
|
clearNewActivationCode()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Se existe um novo código diferente do atual, fazer a troca
|
||||||
|
if (newActivationCode != null && activationCode != null && activationCode != newActivationCode) {
|
||||||
|
Log.d(TAG, "autoInitialize - Desativando código antigo")
|
||||||
|
val deactivationSuccess = suspendCancellableCoroutine<Boolean> { continuation ->
|
||||||
|
deactivateInternal(
|
||||||
|
resolve = { success -> continuation.resume(success) },
|
||||||
|
reject = { error, message ->
|
||||||
|
Log.e(TAG, "Erro na desativação: $error - $message")
|
||||||
|
continuation.resume(false)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!deactivationSuccess) {
|
||||||
|
Log.e(TAG, "autoInitialize - Falha ao desativar código antigo")
|
||||||
|
setError(AditumError.DEACTIVATE_ERROR, "Falha ao desativar para usar novo código")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Usar o código apropriado para inicialização
|
||||||
|
val codeToUse = newActivationCode ?: activationCode
|
||||||
|
Log.d(TAG, "autoInitialize - Inicializando com código: $codeToUse")
|
||||||
|
|
||||||
|
initAditumSdkInternal(
|
||||||
|
applicationName = "MyPOS",
|
||||||
|
applicationVersion = BuildConfig.VERSION_NAME,
|
||||||
|
activationCode = codeToUse,
|
||||||
|
resolve = { success ->
|
||||||
|
if (success && newActivationCode != null) {
|
||||||
|
// Se usou o novo código com sucesso, salvar como atual e limpar o novo
|
||||||
|
viewModelScope.launch {
|
||||||
|
saveActivationCode(newActivationCode)
|
||||||
|
clearNewActivationCode()
|
||||||
|
Log.d(TAG, "autoInitialize - Novo código salvo como atual")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
reject = { error, message ->
|
||||||
|
Log.e(TAG, "autoInitialize - Erro na inicialização: $error - $message")
|
||||||
|
setError(error, message)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "autoInitialize - Exceção: ${e.message}")
|
||||||
|
setError(AditumError.INIT_ERROR, e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun ensureServiceReady(): Boolean {
|
||||||
|
if (!isServiceConnected.value) {
|
||||||
|
Log.d(TAG, "ensureServiceReady - Serviço não conectado, tentando conectar")
|
||||||
|
val connected = paymentApplication.startAditumSdkService(context)
|
||||||
|
if (!connected) {
|
||||||
|
setError(AditumError.SERVICE_NOT_AVAILABLE, AditumError.SERVICE_NOT_AVAILABLE_MESSAGE)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verificar se o SDK está em estado apropriado
|
||||||
|
when (_sdkState.value) {
|
||||||
|
SdkState.NOT_INITIALIZED -> {
|
||||||
|
Log.d(TAG, "ensureServiceReady - SDK não inicializado, tentando inicializar")
|
||||||
|
return initializeIfNeeded()
|
||||||
|
}
|
||||||
|
SdkState.INITIALIZING, SdkState.PROCESSING, SdkState.PAYING, SdkState.CANCELING -> {
|
||||||
|
setError(AditumError.SERVICE_NULL, "SDK ocupado com outra operação")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
SdkState.IDLE -> {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun initializeIfNeeded(): Boolean = suspendCancellableCoroutine { continuation ->
|
||||||
|
val activationCode = viewModelScope.launch {
|
||||||
|
val currentCode = getActivationCode()
|
||||||
|
val newCode = getNewActivationCode()
|
||||||
|
val codeToUse = newCode ?: currentCode
|
||||||
|
|
||||||
|
if (codeToUse == null) {
|
||||||
|
setError(AditumError.INIT_ERROR, "Nenhum código de ativação disponível")
|
||||||
|
continuation.resume(false)
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
|
||||||
|
initAditumSdkInternal(
|
||||||
|
applicationName = "MyPOS",
|
||||||
|
applicationVersion = BuildConfig.VERSION_NAME,
|
||||||
|
activationCode = codeToUse,
|
||||||
|
resolve = { success -> continuation.resume(success) },
|
||||||
|
reject = { error, message ->
|
||||||
|
setError(error, message)
|
||||||
|
continuation.resume(false)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun initAditumSdk(
|
||||||
|
applicationName: String,
|
||||||
|
applicationVersion: String,
|
||||||
|
activationCode: String?,
|
||||||
|
resolve: (Boolean) -> Unit = {},
|
||||||
|
reject: (String, String?) -> Unit = { _, _ -> }
|
||||||
|
) {
|
||||||
|
if (_sdkState.value == SdkState.INITIALIZING) {
|
||||||
|
reject(AditumError.INIT_ERROR, "SDK já está inicializando")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
initAditumSdkInternal(applicationName, applicationVersion, activationCode, resolve, reject)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initAditumSdkInternal(
|
||||||
|
applicationName: String,
|
||||||
|
applicationVersion: String,
|
||||||
|
activationCode: String?,
|
||||||
|
resolve: (Boolean) -> Unit = {},
|
||||||
|
reject: (String, String?) -> Unit = { _, _ -> }
|
||||||
|
) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
try {
|
||||||
|
_sdkState.value = SdkState.INITIALIZING
|
||||||
|
clearError()
|
||||||
|
|
||||||
|
if (!isServiceConnected.value) {
|
||||||
|
val connected = paymentApplication.startAditumSdkService(context)
|
||||||
|
if (!connected) {
|
||||||
|
_sdkState.value = SdkState.NOT_INITIALIZED
|
||||||
|
reject(AditumError.SERVICE_NOT_AVAILABLE, AditumError.SERVICE_NOT_AVAILABLE_MESSAGE)
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val pinpadMessages = PinpadMessages().apply {
|
||||||
|
mainMessage = applicationName
|
||||||
|
}
|
||||||
|
|
||||||
|
val initRequest = InitRequest().apply {
|
||||||
|
this.pinpadMessages = pinpadMessages
|
||||||
|
this.activationCode = if (BuildConfig.DEBUG) BuildConfig.ACTIVATION_CODE else activationCode
|
||||||
|
this.applicationName = applicationName
|
||||||
|
this.applicationVersion = applicationVersion
|
||||||
|
this.useOnlySdk = true
|
||||||
|
this.applicationToken = BuildConfig.APPLICATION_TOKEN
|
||||||
|
}
|
||||||
|
|
||||||
|
val callback = object : InitResponseCallback.Stub() {
|
||||||
|
override fun onResponse(initResponse: InitResponse?) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
val json = gson.toJson(initResponse)
|
||||||
|
Log.d(TAG, json)
|
||||||
|
if (initResponse != null && initResponse.initialized) {
|
||||||
|
_sdkState.value = SdkState.IDLE
|
||||||
|
if (activationCode != null) {
|
||||||
|
saveActivationCode(activationCode)
|
||||||
|
clearNewActivationCode()
|
||||||
|
}
|
||||||
|
resolve(true)
|
||||||
|
} else {
|
||||||
|
_sdkState.value = SdkState.NOT_INITIALIZED
|
||||||
|
val errorMsg = "Falha na inicialização do SDK"
|
||||||
|
setError(AditumError.INIT_ERROR, errorMsg)
|
||||||
|
reject(AditumError.INIT_ERROR, errorMsg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
paymentApplication.communicationService?.init(initRequest, callback)
|
||||||
|
?: run {
|
||||||
|
_sdkState.value = SdkState.NOT_INITIALIZED
|
||||||
|
reject(AditumError.SERVICE_NULL, AditumError.SERVICE_NULL_MESSAGE)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
_sdkState.value = SdkState.NOT_INITIALIZED
|
||||||
|
setError(AditumError.INIT_ERROR, "${AditumError.INIT_ERROR_MESSAGE}: ${e.message}")
|
||||||
|
reject(AditumError.INIT_ERROR, "${AditumError.INIT_ERROR_MESSAGE}: ${e.message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun pay(
|
||||||
|
amount: Long,
|
||||||
|
installments: Int? = null,
|
||||||
|
paymentType: PaymentType,
|
||||||
|
allowContactless: Boolean = true,
|
||||||
|
amountSeasoning: ((Long) -> Long)? = null,
|
||||||
|
resolve: (PaymentResponse) -> Unit = {},
|
||||||
|
reject: (String, String?) -> Unit = { _, _ -> }
|
||||||
|
) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
try {
|
||||||
|
if (!ensureServiceReady()) {
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
|
||||||
|
_sdkState.value = SdkState.PAYING
|
||||||
|
clearError()
|
||||||
|
|
||||||
|
val adjustedAmount = amountSeasoning?.invoke(amount) ?: amount
|
||||||
|
if (adjustedAmount <= 0) {
|
||||||
|
_sdkState.value = SdkState.IDLE
|
||||||
|
setError(AditumError.INVALID_AMOUNT, AditumError.INVALID_AMOUNT_MESSAGE)
|
||||||
|
reject(AditumError.INVALID_AMOUNT, AditumError.INVALID_AMOUNT_MESSAGE)
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
|
||||||
|
val paymentRequest = PaymentRequest().apply {
|
||||||
|
currency = 986
|
||||||
|
operationType = PayOperationType.Authorization
|
||||||
|
this.paymentType = paymentType
|
||||||
|
this.amount = amount
|
||||||
|
this.allowContactless = allowContactless
|
||||||
|
manualEntry = false
|
||||||
|
installmentType = if (paymentType == PaymentType.Debit) InstallmentType.Undefined else InstallmentType.Merchant
|
||||||
|
this.installmentNumber = when {
|
||||||
|
installments == null -> if (paymentType == PaymentType.Credit) 1 else 0
|
||||||
|
installments > 1 -> installments
|
||||||
|
paymentType == PaymentType.Debit -> 0
|
||||||
|
else -> 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val callback = object : PaymentResponseCallback.Stub() {
|
||||||
|
override fun onResponse(paymentResponse: PaymentResponse?) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
_sdkState.value = SdkState.IDLE
|
||||||
|
if (paymentResponse != null) {
|
||||||
|
val json = gson.toJson(paymentResponse)
|
||||||
|
Log.d(TAG, "Payment response: $json")
|
||||||
|
resolve(paymentResponse)
|
||||||
|
} else {
|
||||||
|
val errorMsg = "Resposta de pagamento nula"
|
||||||
|
setError(AditumError.PAYMENT_RESPONSE_NULL, errorMsg)
|
||||||
|
reject(AditumError.PAYMENT_RESPONSE_NULL, errorMsg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
paymentApplication.communicationService?.pay(paymentRequest, callback)
|
||||||
|
?: run {
|
||||||
|
_sdkState.value = SdkState.IDLE
|
||||||
|
setError(AditumError.SERVICE_NULL, AditumError.SERVICE_NULL_MESSAGE)
|
||||||
|
reject(AditumError.SERVICE_NULL, AditumError.SERVICE_NULL_MESSAGE)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
_sdkState.value = SdkState.IDLE
|
||||||
|
setError(AditumError.PAYMENT_ERROR, "${AditumError.PAYMENT_ERROR_MESSAGE}: ${e.message}")
|
||||||
|
reject(AditumError.PAYMENT_ERROR, "${AditumError.PAYMENT_ERROR_MESSAGE}: ${e.message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun confirm(nsu: String, resolve: (Boolean) -> Unit = {}, reject: (String, String?) -> Unit = { _, _ -> }) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
try {
|
||||||
|
if (!ensureServiceReady()) {
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
|
||||||
|
_sdkState.value = SdkState.PROCESSING
|
||||||
|
clearError()
|
||||||
|
|
||||||
|
val callback = object : ConfirmTransactionCallback.Stub() {
|
||||||
|
override fun onResponse(confirmed: Boolean) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
_sdkState.value = SdkState.IDLE
|
||||||
|
resolve(confirmed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
paymentApplication.communicationService?.confirmTransaction(nsu, callback)
|
||||||
|
?: run {
|
||||||
|
_sdkState.value = SdkState.IDLE
|
||||||
|
setError(AditumError.SERVICE_NULL, AditumError.SERVICE_NULL_MESSAGE)
|
||||||
|
reject(AditumError.SERVICE_NULL, AditumError.SERVICE_NULL_MESSAGE)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
_sdkState.value = SdkState.IDLE
|
||||||
|
setError(AditumError.CONFIRM_ERROR, "${AditumError.CONFIRM_ERROR_MESSAGE}: ${e.message}")
|
||||||
|
reject(AditumError.CONFIRM_ERROR, "${AditumError.CONFIRM_ERROR_MESSAGE}: ${e.message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun cancel(nsu: String, isReversal: Boolean, resolve: (Boolean) -> Unit = {}, reject: (String, String?) -> Unit = { _, _ -> }) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
try {
|
||||||
|
if (!ensureServiceReady()) {
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
|
||||||
|
_sdkState.value = SdkState.CANCELING
|
||||||
|
clearError()
|
||||||
|
|
||||||
|
val cancelationRequest = CancelationRequest(nsu, isReversal)
|
||||||
|
|
||||||
|
val callback = object : CancelationResponseCallback.Stub() {
|
||||||
|
override fun onResponse(cancelationResponse: CancelationResponse?) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
_sdkState.value = SdkState.IDLE
|
||||||
|
if (cancelationResponse != null) {
|
||||||
|
resolve(cancelationResponse.canceled)
|
||||||
|
} else {
|
||||||
|
val errorMsg = "Resposta de cancelamento nula"
|
||||||
|
setError(AditumError.CANCEL_RESPONSE_NULL, errorMsg)
|
||||||
|
reject(AditumError.CANCEL_RESPONSE_NULL, errorMsg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
paymentApplication.communicationService?.cancel(cancelationRequest, callback)
|
||||||
|
?: run {
|
||||||
|
_sdkState.value = SdkState.IDLE
|
||||||
|
setError(AditumError.SERVICE_NULL, AditumError.SERVICE_NULL_MESSAGE)
|
||||||
|
reject(AditumError.SERVICE_NULL, AditumError.SERVICE_NULL_MESSAGE)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
_sdkState.value = SdkState.IDLE
|
||||||
|
setError(AditumError.CANCEL_ERROR, "${AditumError.CANCEL_ERROR_MESSAGE}: ${e.message}")
|
||||||
|
reject(AditumError.CANCEL_ERROR, "${AditumError.CANCEL_ERROR_MESSAGE}: ${e.message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun abortOperation(resolve: (Boolean) -> Unit = {}, reject: (String, String?) -> Unit = { _, _ -> }) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
try {
|
||||||
|
if (!ensureServiceReady()) {
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
|
||||||
|
paymentApplication.communicationService?.abortOperation()
|
||||||
|
_sdkState.value = SdkState.IDLE
|
||||||
|
resolve(true)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
setError(AditumError.SERVICE_NULL, "${AditumError.SERVICE_NULL_MESSAGE}: ${e.message}")
|
||||||
|
reject(AditumError.SERVICE_NULL, "${AditumError.SERVICE_NULL_MESSAGE}: ${e.message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun pendingTransactions(resolve: (List<Charge?>?) -> Unit = {}, reject: (String, String?) -> Unit = { _, _ -> }) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
try {
|
||||||
|
if (!ensureServiceReady()) {
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
|
||||||
|
_sdkState.value = SdkState.PROCESSING
|
||||||
|
|
||||||
|
val callback = object : PendingTransactionsCallback.Stub() {
|
||||||
|
override fun onResponse(results: List<Charge?>?) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
_sdkState.value = SdkState.IDLE
|
||||||
|
val json = gson.toJson(results)
|
||||||
|
Log.d(TAG, "Pending transactions: $json")
|
||||||
|
resolve(results)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
paymentApplication.communicationService?.pendingTransactions(callback)
|
||||||
|
?: run {
|
||||||
|
_sdkState.value = SdkState.IDLE
|
||||||
|
setError(AditumError.SERVICE_NULL, AditumError.SERVICE_NULL_MESSAGE)
|
||||||
|
reject(AditumError.SERVICE_NULL, AditumError.SERVICE_NULL_MESSAGE)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
_sdkState.value = SdkState.IDLE
|
||||||
|
setError(AditumError.CONFIRM_ERROR, "${AditumError.CONFIRM_ERROR_MESSAGE}: ${e.message}")
|
||||||
|
reject(AditumError.CONFIRM_ERROR, "${AditumError.CONFIRM_ERROR_MESSAGE}: ${e.message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun deactivate(resolve: (Boolean) -> Unit = {}, reject: (String, String?) -> Unit = { _, _ -> }) {
|
||||||
|
deactivateInternal(resolve, reject)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun deactivateInternal(resolve: (Boolean) -> Unit = {}, reject: (String, String?) -> Unit = { _, _ -> }) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
try {
|
||||||
|
if (!isServiceConnected.value) {
|
||||||
|
reject(AditumError.SERVICE_NOT_AVAILABLE, AditumError.SERVICE_NOT_AVAILABLE_MESSAGE)
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
|
||||||
|
val callback = object : DeactivationResponseCallback.Stub() {
|
||||||
|
override fun onResponse(status: Boolean) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
_sdkState.value = SdkState.NOT_INITIALIZED
|
||||||
|
Log.d(TAG, "Deactivation response: $status")
|
||||||
|
if (status) {
|
||||||
|
// Limpar os códigos de ativação quando desativar
|
||||||
|
saveActivationCode(null)
|
||||||
|
clearNewActivationCode()
|
||||||
|
}
|
||||||
|
resolve(status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
paymentApplication.communicationService?.deactivate(callback)
|
||||||
|
?: reject(AditumError.SERVICE_NULL, AditumError.SERVICE_NULL_MESSAGE)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
setError(AditumError.DEACTIVATE_ERROR, "${AditumError.DEACTIVATE_ERROR_MESSAGE}: ${e.message}")
|
||||||
|
reject(AditumError.DEACTIVATE_ERROR, "${AditumError.DEACTIVATE_ERROR_MESSAGE}: ${e.message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getMerchantData(resolve: (MerchantData) -> Unit = {}, reject: (String, String?) -> Unit = { _, _ -> }) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
try {
|
||||||
|
if (!ensureServiceReady()) {
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
|
||||||
|
val merchantData: MerchantData? = paymentApplication.communicationService?.merchantData
|
||||||
|
if (merchantData != null) {
|
||||||
|
resolve(merchantData)
|
||||||
|
} else {
|
||||||
|
setError(AditumError.MERCHANT_DATA_NULL, AditumError.MERCHANT_DATA_NULL_MESSAGE)
|
||||||
|
reject(AditumError.MERCHANT_DATA_NULL, AditumError.MERCHANT_DATA_NULL_MESSAGE)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
setError(AditumError.MERCHANT_DATA_ERROR, "${AditumError.MERCHANT_DATA_ERROR_MESSAGE}: ${e.message}")
|
||||||
|
reject(AditumError.MERCHANT_DATA_ERROR, "${AditumError.MERCHANT_DATA_ERROR_MESSAGE}: ${e.message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun provideFactory(
|
||||||
|
paymentApplication: PaymentApplication,
|
||||||
|
context: Context
|
||||||
|
): ViewModelProvider.Factory = object : ViewModelProvider.Factory {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||||
|
if (modelClass.isAssignableFrom(PaymentViewModel::class.java)) {
|
||||||
|
return PaymentViewModel(paymentApplication, context) as T
|
||||||
|
}
|
||||||
|
throw IllegalArgumentException("Unknown ViewModel class")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -11,4 +11,5 @@ val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "my
|
|||||||
object PrefKeys {
|
object PrefKeys {
|
||||||
val SERIAL_NUMBER = stringPreferencesKey("session_serial_number")
|
val SERIAL_NUMBER = stringPreferencesKey("session_serial_number")
|
||||||
val ACTIVATION_CODE = stringPreferencesKey("session_activation_code")
|
val ACTIVATION_CODE = stringPreferencesKey("session_activation_code")
|
||||||
|
val NEW_ACTIVATION_CODE = stringPreferencesKey("new_session_activation_code")
|
||||||
}
|
}
|
||||||
436
app/src/main/java/com/example/mypos/screen/PaymentScreen.kt
Normal file
436
app/src/main/java/com/example/mypos/screen/PaymentScreen.kt
Normal file
@ -0,0 +1,436 @@
|
|||||||
|
package com.example.mypos.screen
|
||||||
|
|
||||||
|
import android.os.Handler
|
||||||
|
import android.os.Looper
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
|
import androidx.compose.foundation.verticalScroll
|
||||||
|
import androidx.compose.material3.*
|
||||||
|
import androidx.compose.runtime.*
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.text.input.KeyboardType
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import br.com.aditum.data.v2.enums.PaymentType
|
||||||
|
import com.example.mypos.BuildConfig
|
||||||
|
import com.example.mypos.models.PaymentViewModel
|
||||||
|
import com.example.mypos.models.SdkState
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun PaymentScreen(viewModel: PaymentViewModel) {
|
||||||
|
val context = LocalContext.current
|
||||||
|
var amount by remember { mutableStateOf("") }
|
||||||
|
var installments by remember { mutableStateOf("1") }
|
||||||
|
var paymentType by remember { mutableStateOf(PaymentType.Credit) }
|
||||||
|
var activationCode by remember { mutableStateOf(if (BuildConfig.DEBUG) BuildConfig.ACTIVATION_CODE else "") }
|
||||||
|
var newActivationCode by remember { mutableStateOf(if (BuildConfig.DEBUG) BuildConfig.ACTIVATION_CODE else "") }
|
||||||
|
|
||||||
|
val isServiceConnected by viewModel.isServiceConnected.collectAsState()
|
||||||
|
val sdkState by viewModel.sdkState.collectAsState()
|
||||||
|
val message by viewModel.messageFlow.collectAsState()
|
||||||
|
val pinLength by viewModel.pinLengthFlow.collectAsState()
|
||||||
|
val error by viewModel.errorFlow.collectAsState()
|
||||||
|
|
||||||
|
val scrollState = rememberScrollState()
|
||||||
|
val handler = remember { Handler(Looper.getMainLooper()) }
|
||||||
|
|
||||||
|
|
||||||
|
LaunchedEffect(error) {
|
||||||
|
error?.let { (errorCode, errorMessage) ->
|
||||||
|
handler.post {
|
||||||
|
Toast.makeText(context, "Erro: $errorMessage", Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(16.dp)
|
||||||
|
.verticalScroll(scrollState),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "Sistema de Pagamento POS",
|
||||||
|
style = MaterialTheme.typography.headlineMedium
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
Card(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
colors = CardDefaults.cardColors(
|
||||||
|
containerColor = when {
|
||||||
|
!isServiceConnected -> MaterialTheme.colorScheme.errorContainer
|
||||||
|
sdkState == SdkState.IDLE -> MaterialTheme.colorScheme.primaryContainer
|
||||||
|
sdkState == SdkState.NOT_INITIALIZED -> MaterialTheme.colorScheme.secondaryContainer
|
||||||
|
else -> MaterialTheme.colorScheme.tertiaryContainer
|
||||||
|
}
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.padding(16.dp),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "Status do Sistema",
|
||||||
|
style = MaterialTheme.typography.titleMedium,
|
||||||
|
fontWeight = FontWeight.Bold
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = if (isServiceConnected) "Serviço: Conectado" else "Serviço: Desconectado",
|
||||||
|
style = MaterialTheme.typography.bodyMedium
|
||||||
|
)
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = "SDK: ${getSdkStateText(sdkState)}",
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
fontWeight = FontWeight.Medium
|
||||||
|
)
|
||||||
|
|
||||||
|
if (message.isNotEmpty()) {
|
||||||
|
Spacer(modifier = Modifier.height(4.dp))
|
||||||
|
Text(
|
||||||
|
text = message,
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pinLength > 0) {
|
||||||
|
Text(
|
||||||
|
text = "PIN: ${"*".repeat(pinLength)}",
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
fontWeight = FontWeight.Bold
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Card(modifier = Modifier.fillMaxWidth()) {
|
||||||
|
Column(modifier = Modifier.padding(16.dp)) {
|
||||||
|
Text(
|
||||||
|
text = "Códigos de Ativação",
|
||||||
|
style = MaterialTheme.typography.titleMedium,
|
||||||
|
fontWeight = FontWeight.Bold
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
|
||||||
|
OutlinedTextField(
|
||||||
|
value = activationCode,
|
||||||
|
onValueChange = { activationCode = it },
|
||||||
|
label = { Text("Código de Ativação Atual") },
|
||||||
|
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.NumberPassword),
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
enabled = sdkState != SdkState.INITIALIZING
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
|
||||||
|
Button(
|
||||||
|
onClick = {
|
||||||
|
if (activationCode.isBlank()) {
|
||||||
|
handler.post {
|
||||||
|
Toast.makeText(context, "Código de ativação não pode estar vazio", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
return@Button
|
||||||
|
}
|
||||||
|
viewModel.saveActivationCode(activationCode)
|
||||||
|
handler.post {
|
||||||
|
Toast.makeText(context, "Código de ativação salvo com sucesso", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
enabled = sdkState != SdkState.INITIALIZING,
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
Text("Salvar Código Atual")
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(12.dp))
|
||||||
|
|
||||||
|
OutlinedTextField(
|
||||||
|
value = newActivationCode,
|
||||||
|
onValueChange = { newActivationCode = it },
|
||||||
|
label = { Text("Novo Código de Ativação") },
|
||||||
|
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.NumberPassword),
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
enabled = sdkState != SdkState.INITIALIZING
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
|
||||||
|
Button(
|
||||||
|
onClick = {
|
||||||
|
if (newActivationCode.isBlank()) {
|
||||||
|
handler.post {
|
||||||
|
Toast.makeText(context, "Novo código de ativação não pode estar vazio", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
return@Button
|
||||||
|
}
|
||||||
|
viewModel.saveNewActivationCode(newActivationCode)
|
||||||
|
handler.post {
|
||||||
|
Toast.makeText(context, "Novo código de ativação salvo com sucesso", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
enabled = sdkState != SdkState.INITIALIZING,
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
Text("Salvar Novo Código")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Button(
|
||||||
|
onClick = {
|
||||||
|
val codeToUse = if (newActivationCode.isNotBlank()) newActivationCode else activationCode
|
||||||
|
if (codeToUse.isBlank()) {
|
||||||
|
handler.post {
|
||||||
|
Toast.makeText(context, "Insira um código de ativação", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
return@Button
|
||||||
|
}
|
||||||
|
|
||||||
|
viewModel.initAditumSdk(
|
||||||
|
applicationName = "MyPOS",
|
||||||
|
applicationVersion = BuildConfig.VERSION_NAME,
|
||||||
|
activationCode = codeToUse,
|
||||||
|
resolve = { success ->
|
||||||
|
handler.post {
|
||||||
|
Toast.makeText(
|
||||||
|
context,
|
||||||
|
if (success) "SDK inicializado com sucesso!" else "Falha na inicialização do SDK",
|
||||||
|
Toast.LENGTH_LONG
|
||||||
|
).show()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
reject = { errorCode, errorMessage ->
|
||||||
|
handler.post {
|
||||||
|
Toast.makeText(context, "Erro: $errorMessage", Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
enabled = sdkState !in listOf(SdkState.INITIALIZING, SdkState.PROCESSING, SdkState.PAYING, SdkState.CANCELING),
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
when (sdkState) {
|
||||||
|
SdkState.INITIALIZING -> "Inicializando SDK..."
|
||||||
|
SdkState.IDLE -> "Reinicializar SDK"
|
||||||
|
else -> "Inicializar SDK"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Card(modifier = Modifier.fillMaxWidth()) {
|
||||||
|
Column(modifier = Modifier.padding(16.dp)) {
|
||||||
|
Text(
|
||||||
|
text = "Realizar Pagamento",
|
||||||
|
style = MaterialTheme.typography.titleMedium,
|
||||||
|
fontWeight = FontWeight.Bold
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
|
||||||
|
OutlinedTextField(
|
||||||
|
value = amount,
|
||||||
|
onValueChange = { amount = it.filter { char -> char.isDigit() || char == '.' } },
|
||||||
|
label = { Text("Valor (R$)") },
|
||||||
|
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Decimal),
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
enabled = sdkState == SdkState.IDLE
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
|
||||||
|
OutlinedTextField(
|
||||||
|
value = installments,
|
||||||
|
onValueChange = { installments = it.filter { char -> char.isDigit() } },
|
||||||
|
label = { Text("Quantidade de Parcelas") },
|
||||||
|
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
enabled = paymentType == PaymentType.Credit && sdkState == SdkState.IDLE
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(12.dp))
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.SpaceEvenly
|
||||||
|
) {
|
||||||
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
|
RadioButton(
|
||||||
|
selected = paymentType == PaymentType.Credit,
|
||||||
|
onClick = { if (sdkState == SdkState.IDLE) paymentType = PaymentType.Credit },
|
||||||
|
enabled = sdkState == SdkState.IDLE
|
||||||
|
)
|
||||||
|
Text("Crédito")
|
||||||
|
}
|
||||||
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
|
RadioButton(
|
||||||
|
selected = paymentType == PaymentType.Debit,
|
||||||
|
onClick = { if (sdkState == SdkState.IDLE) paymentType = PaymentType.Debit },
|
||||||
|
enabled = sdkState == SdkState.IDLE
|
||||||
|
)
|
||||||
|
Text("Débito")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(12.dp))
|
||||||
|
|
||||||
|
Button(
|
||||||
|
onClick = {
|
||||||
|
val amountValue = amount.toDoubleOrNull()?.times(100)?.toLong()
|
||||||
|
val installmentCount = installments.toIntOrNull() ?: 1
|
||||||
|
|
||||||
|
if (amountValue == null || amountValue <= 0) {
|
||||||
|
handler.post {
|
||||||
|
Toast.makeText(context, "Insira um valor válido", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
return@Button
|
||||||
|
}
|
||||||
|
|
||||||
|
viewModel.pay(
|
||||||
|
amount = amountValue,
|
||||||
|
installments = if (paymentType == PaymentType.Credit) installmentCount else null,
|
||||||
|
paymentType = paymentType,
|
||||||
|
allowContactless = true,
|
||||||
|
resolve = { response ->
|
||||||
|
handler.post {
|
||||||
|
Toast.makeText(context, "Pagamento realizado com sucesso!", Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
reject = { errorCode, errorMessage ->
|
||||||
|
handler.post {
|
||||||
|
Toast.makeText(context, "Erro no pagamento: $errorMessage", Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
enabled = sdkState == SdkState.IDLE,
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
colors = ButtonDefaults.buttonColors(
|
||||||
|
containerColor = if (sdkState == SdkState.PAYING)
|
||||||
|
MaterialTheme.colorScheme.secondary
|
||||||
|
else
|
||||||
|
MaterialTheme.colorScheme.primary
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
when (sdkState) {
|
||||||
|
SdkState.PAYING -> "Processando Pagamento..."
|
||||||
|
SdkState.IDLE -> "Realizar Pagamento"
|
||||||
|
else -> "Aguarde..."
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.SpaceEvenly
|
||||||
|
) {
|
||||||
|
Button(
|
||||||
|
onClick = {
|
||||||
|
viewModel.abortOperation(
|
||||||
|
resolve = {
|
||||||
|
handler.post {
|
||||||
|
Toast.makeText(context, "Operação cancelada", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
reject = { errorCode, errorMessage ->
|
||||||
|
handler.post {
|
||||||
|
Toast.makeText(context, "Erro ao cancelar: $errorMessage", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
enabled = sdkState in listOf(SdkState.PAYING, SdkState.PROCESSING, SdkState.CANCELING),
|
||||||
|
colors = ButtonDefaults.buttonColors(containerColor = MaterialTheme.colorScheme.error)
|
||||||
|
) {
|
||||||
|
Text("Cancelar Operação")
|
||||||
|
}
|
||||||
|
|
||||||
|
Button(
|
||||||
|
onClick = {
|
||||||
|
viewModel.deactivate(
|
||||||
|
resolve = { success ->
|
||||||
|
handler.post {
|
||||||
|
Toast.makeText(
|
||||||
|
context,
|
||||||
|
if (success) "SDK desativado com sucesso" else "Falha ao desativar SDK",
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
reject = { errorCode, errorMessage ->
|
||||||
|
handler.post {
|
||||||
|
Toast.makeText(context, "Erro na desativação: $errorMessage", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
enabled = sdkState == SdkState.IDLE,
|
||||||
|
colors = ButtonDefaults.buttonColors(containerColor = MaterialTheme.colorScheme.secondary)
|
||||||
|
) {
|
||||||
|
Text("Desativar SDK")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (sdkState in listOf(SdkState.INITIALIZING, SdkState.PAYING, SdkState.PROCESSING, SdkState.CANCELING)) {
|
||||||
|
Card(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surfaceVariant)
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.padding(16.dp),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
CircularProgressIndicator()
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
Text(
|
||||||
|
text = when (sdkState) {
|
||||||
|
SdkState.INITIALIZING -> "Inicializando SDK..."
|
||||||
|
SdkState.PAYING -> "Processando pagamento..."
|
||||||
|
SdkState.PROCESSING -> "Processando operação..."
|
||||||
|
SdkState.CANCELING -> "Cancelando operação..."
|
||||||
|
else -> "Aguarde..."
|
||||||
|
},
|
||||||
|
style = MaterialTheme.typography.bodyMedium
|
||||||
|
)
|
||||||
|
if (message.isNotEmpty()) {
|
||||||
|
Text(
|
||||||
|
text = message,
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getSdkStateText(state: SdkState): String {
|
||||||
|
return when (state) {
|
||||||
|
SdkState.NOT_INITIALIZED -> "Não Inicializado"
|
||||||
|
SdkState.INITIALIZING -> "Inicializando"
|
||||||
|
SdkState.IDLE -> "Pronto"
|
||||||
|
SdkState.PROCESSING -> "Processando"
|
||||||
|
SdkState.PAYING -> "Pagando"
|
||||||
|
SdkState.CANCELING -> "Cancelando"
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,267 +0,0 @@
|
|||||||
package com.example.mypos.services
|
|
||||||
|
|
||||||
import android.util.Log
|
|
||||||
import br.com.aditum.data.v2.enums.InstallmentType
|
|
||||||
import br.com.aditum.data.v2.enums.PayOperationType
|
|
||||||
import br.com.aditum.data.v2.enums.PaymentType
|
|
||||||
import br.com.aditum.data.v2.model.Charge
|
|
||||||
import br.com.aditum.data.v2.model.MerchantData
|
|
||||||
import br.com.aditum.data.v2.model.PinpadMessages
|
|
||||||
import br.com.aditum.data.v2.model.cancelation.CancelationRequest
|
|
||||||
import br.com.aditum.data.v2.model.cancelation.CancelationResponse
|
|
||||||
import br.com.aditum.data.v2.model.cancelation.CancelationResponseCallback
|
|
||||||
import br.com.aditum.data.v2.model.deactivation.DeactivationResponseCallback
|
|
||||||
import br.com.aditum.data.v2.model.init.InitRequest
|
|
||||||
import br.com.aditum.data.v2.model.init.InitResponse
|
|
||||||
import br.com.aditum.data.v2.model.init.InitResponseCallback
|
|
||||||
import br.com.aditum.data.v2.model.payment.PaymentRequest
|
|
||||||
import br.com.aditum.data.v2.model.payment.PaymentResponse
|
|
||||||
import br.com.aditum.data.v2.model.payment.PaymentResponseCallback
|
|
||||||
import br.com.aditum.data.v2.model.transactions.ConfirmTransactionCallback
|
|
||||||
import br.com.aditum.data.v2.model.transactions.PendingTransactionsCallback
|
|
||||||
import com.example.mypos.BuildConfig
|
|
||||||
import com.example.mypos.data.AditumError
|
|
||||||
import com.google.gson.Gson
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
|
|
||||||
class AditumSdkService(private val paymentApplication: PaymentApplication) {
|
|
||||||
companion object {
|
|
||||||
private const val TAG = "AditumSdkModule"
|
|
||||||
}
|
|
||||||
|
|
||||||
private val gson = Gson()
|
|
||||||
private val coroutineScope = CoroutineScope(Dispatchers.IO)
|
|
||||||
|
|
||||||
fun getName(): String = "AditumSdkModule"
|
|
||||||
|
|
||||||
fun initAditumSdk(
|
|
||||||
applicationName: String,
|
|
||||||
applicationVersion: String,
|
|
||||||
activationCode: String?,
|
|
||||||
resolve: (Boolean) -> Unit = {},
|
|
||||||
reject: (String, String?) -> Unit = { _, _ -> }
|
|
||||||
) {
|
|
||||||
coroutineScope.launch {
|
|
||||||
try {
|
|
||||||
if (!paymentApplication.ensureServiceConnected()) {
|
|
||||||
reject(AditumError.SERVICE_NOT_AVAILABLE, AditumError.SERVICE_NOT_AVAILABLE_MESSAGE)
|
|
||||||
return@launch
|
|
||||||
}
|
|
||||||
|
|
||||||
val pinpadMessages = PinpadMessages().apply {
|
|
||||||
mainMessage = applicationName
|
|
||||||
}
|
|
||||||
|
|
||||||
val initRequest = InitRequest().apply {
|
|
||||||
this.pinpadMessages = pinpadMessages
|
|
||||||
this.activationCode = if (BuildConfig.DEBUG) BuildConfig.ACTIVATION_CODE else activationCode
|
|
||||||
this.applicationName = applicationName
|
|
||||||
this.applicationVersion = applicationVersion
|
|
||||||
this.applicationToken = BuildConfig.APPLICATION_TOKEN
|
|
||||||
}
|
|
||||||
|
|
||||||
val callback = object : InitResponseCallback.Stub() {
|
|
||||||
override fun onResponse(initResponse: InitResponse?) {
|
|
||||||
if (initResponse != null) {
|
|
||||||
resolve(initResponse.initialized)
|
|
||||||
} else {
|
|
||||||
Log.e(TAG, "onResponse - initResponse is null")
|
|
||||||
reject(AditumError.INIT_RESPONSE_NULL, AditumError.INIT_RESPONSE_NULL_MESSAGE)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
paymentApplication.communicationService?.init(initRequest, callback)
|
|
||||||
?: reject(AditumError.SERVICE_NULL, AditumError.SERVICE_NULL_MESSAGE)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
reject(AditumError.INIT_ERROR, "${AditumError.INIT_ERROR_MESSAGE}: ${e.message}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getMerchantData(resolve: (MerchantData) -> Unit = {}, reject: (String, String?) -> Unit = { _, _ -> }) {
|
|
||||||
coroutineScope.launch {
|
|
||||||
try {
|
|
||||||
if (!paymentApplication.ensureServiceConnected()) {
|
|
||||||
reject(AditumError.SERVICE_NOT_AVAILABLE, AditumError.SERVICE_NOT_AVAILABLE_MESSAGE)
|
|
||||||
return@launch
|
|
||||||
}
|
|
||||||
|
|
||||||
val merchantData: MerchantData? = paymentApplication.communicationService?.merchantData
|
|
||||||
if (merchantData != null) {
|
|
||||||
resolve(merchantData)
|
|
||||||
} else {
|
|
||||||
reject(AditumError.MERCHANT_DATA_NULL, AditumError.MERCHANT_DATA_NULL_MESSAGE)
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
reject(AditumError.MERCHANT_DATA_ERROR, "${AditumError.MERCHANT_DATA_ERROR_MESSAGE}: ${e.message}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun pay(
|
|
||||||
amount: Long,
|
|
||||||
installments: Int = 0,
|
|
||||||
paymentType: PaymentType,
|
|
||||||
allowContactless: Boolean = true,
|
|
||||||
amountSeasoning: ((Long) -> Long)? = null,
|
|
||||||
resolve: (PaymentResponse) -> Unit = {},
|
|
||||||
reject: (String, String?) -> Unit = { _, _ -> }
|
|
||||||
) {
|
|
||||||
coroutineScope.launch {
|
|
||||||
try {
|
|
||||||
if (!paymentApplication.ensureServiceConnected()) {
|
|
||||||
reject(AditumError.SERVICE_NOT_AVAILABLE, AditumError.SERVICE_NOT_AVAILABLE_MESSAGE)
|
|
||||||
return@launch
|
|
||||||
}
|
|
||||||
|
|
||||||
val adjustedAmount = amountSeasoning?.invoke(amount) ?: amount
|
|
||||||
if (adjustedAmount <= 0) {
|
|
||||||
reject(AditumError.INVALID_AMOUNT, AditumError.INVALID_AMOUNT_MESSAGE)
|
|
||||||
return@launch
|
|
||||||
}
|
|
||||||
|
|
||||||
val paymentRequest = PaymentRequest().apply {
|
|
||||||
currency = 986
|
|
||||||
operationType = PayOperationType.Authorization
|
|
||||||
this.paymentType = paymentType
|
|
||||||
this.amount = amount
|
|
||||||
this.allowContactless = allowContactless
|
|
||||||
manualEntry = false
|
|
||||||
installmentType = if (paymentType == PaymentType.Debit) InstallmentType.None else InstallmentType.Merchant
|
|
||||||
installmentNumber = installments
|
|
||||||
}
|
|
||||||
|
|
||||||
val callback = object : PaymentResponseCallback.Stub() {
|
|
||||||
override fun onResponse(paymentResponse: PaymentResponse?) {
|
|
||||||
if (paymentResponse != null) {
|
|
||||||
val json = gson.toJson(paymentResponse)
|
|
||||||
Log.e(TAG, "onResponse - $json")
|
|
||||||
resolve(paymentResponse)
|
|
||||||
} else {
|
|
||||||
Log.e(TAG, "onResponse - paymentResponse is null")
|
|
||||||
reject(AditumError.PAYMENT_RESPONSE_NULL, AditumError.PAYMENT_RESPONSE_NULL_MESSAGE)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
paymentApplication.communicationService?.pay(paymentRequest, callback)
|
|
||||||
?: reject(AditumError.SERVICE_NULL, AditumError.SERVICE_NULL_MESSAGE)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
reject(AditumError.PAYMENT_ERROR, "${AditumError.PAYMENT_ERROR_MESSAGE}: ${e.message}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun confirm(nsu: String, resolve: (Boolean) -> Unit = {}, reject: (String, String?) -> Unit = { _, _ -> }) {
|
|
||||||
coroutineScope.launch {
|
|
||||||
try {
|
|
||||||
if (!paymentApplication.ensureServiceConnected()) {
|
|
||||||
reject(AditumError.SERVICE_NOT_AVAILABLE, AditumError.SERVICE_NOT_AVAILABLE_MESSAGE)
|
|
||||||
return@launch
|
|
||||||
}
|
|
||||||
|
|
||||||
val callback = object : ConfirmTransactionCallback.Stub() {
|
|
||||||
override fun onResponse(confirmed: Boolean) {
|
|
||||||
resolve(confirmed)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
paymentApplication.communicationService?.confirmTransaction(nsu, callback)
|
|
||||||
?: reject(AditumError.SERVICE_NULL, AditumError.SERVICE_NULL_MESSAGE)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
reject(AditumError.CONFIRM_ERROR, "${AditumError.CONFIRM_ERROR_MESSAGE}: ${e.message}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun cancel(nsu: String, isReversal: Boolean, resolve: (Boolean) -> Unit = {}, reject: (String, String?) -> Unit = { _, _ -> }) {
|
|
||||||
coroutineScope.launch {
|
|
||||||
try {
|
|
||||||
if (!paymentApplication.ensureServiceConnected()) {
|
|
||||||
reject(AditumError.SERVICE_NOT_AVAILABLE, AditumError.SERVICE_NOT_AVAILABLE_MESSAGE)
|
|
||||||
return@launch
|
|
||||||
}
|
|
||||||
|
|
||||||
val cancelationRequest = CancelationRequest(nsu, isReversal)
|
|
||||||
|
|
||||||
val callback = object : CancelationResponseCallback.Stub() {
|
|
||||||
override fun onResponse(cancelationResponse: CancelationResponse?) {
|
|
||||||
if (cancelationResponse != null) {
|
|
||||||
resolve(cancelationResponse.canceled)
|
|
||||||
} else {
|
|
||||||
Log.e(TAG, "onResponse - cancelationResponse is null")
|
|
||||||
reject(AditumError.CANCEL_RESPONSE_NULL, AditumError.CANCEL_RESPONSE_NULL_MESSAGE)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
paymentApplication.communicationService?.cancel(cancelationRequest, callback)
|
|
||||||
?: reject(AditumError.SERVICE_NULL, AditumError.SERVICE_NULL_MESSAGE)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
reject(AditumError.CANCEL_ERROR, "${AditumError.CANCEL_ERROR_MESSAGE}: ${e.message}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun aboutOperation(resolve: (Boolean) -> Unit = {}, reject: (String, String?) -> Unit = { _, _ -> }) {
|
|
||||||
coroutineScope.launch {
|
|
||||||
if (!paymentApplication.ensureServiceConnected()) {
|
|
||||||
reject(AditumError.SERVICE_NOT_AVAILABLE, AditumError.SERVICE_NOT_AVAILABLE_MESSAGE)
|
|
||||||
return@launch
|
|
||||||
}
|
|
||||||
|
|
||||||
paymentApplication.communicationService?.abortOperation()
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun pendingTransactions(resolve: (List<Charge?>?) -> Unit = {}, reject: (String, String?) -> Unit = { _, _ ->}) {
|
|
||||||
coroutineScope.launch {
|
|
||||||
try {
|
|
||||||
if (!paymentApplication.ensureServiceConnected()) {
|
|
||||||
reject(AditumError.SERVICE_NOT_AVAILABLE, AditumError.SERVICE_NOT_AVAILABLE_MESSAGE)
|
|
||||||
return@launch
|
|
||||||
}
|
|
||||||
|
|
||||||
val callback = object : PendingTransactionsCallback.Stub() {
|
|
||||||
override fun onResponse(results: List<Charge?>?) {
|
|
||||||
val json = gson.toJson(results)
|
|
||||||
Log.e(TAG, "onResponse - $json")
|
|
||||||
resolve(results)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
paymentApplication.communicationService?.pendingTransactions(callback)
|
|
||||||
?: reject(AditumError.SERVICE_NULL, AditumError.SERVICE_NULL_MESSAGE)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
reject(AditumError.CONFIRM_ERROR, "${AditumError.CONFIRM_ERROR_MESSAGE}: ${e.message}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun deactivate(resolve: (Boolean) -> Unit = {}, reject: (String, String?) -> Unit = { _, _ -> }) {
|
|
||||||
coroutineScope.launch {
|
|
||||||
try {
|
|
||||||
if (!paymentApplication.ensureServiceConnected()) {
|
|
||||||
reject(AditumError.SERVICE_NOT_AVAILABLE, AditumError.SERVICE_NOT_AVAILABLE_MESSAGE)
|
|
||||||
return@launch
|
|
||||||
}
|
|
||||||
|
|
||||||
val callback = object : DeactivationResponseCallback.Stub() {
|
|
||||||
override fun onResponse(status: Boolean) {
|
|
||||||
Log.d(TAG, "onDeactivationResponse - deactivationResponse: $status")
|
|
||||||
resolve(status)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
paymentApplication.communicationService?.deactivate(callback)
|
|
||||||
?: reject(AditumError.SERVICE_NULL, AditumError.SERVICE_NULL_MESSAGE)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
reject(AditumError.DEACTIVATE_ERROR, "${AditumError.DEACTIVATE_ERROR_MESSAGE}: ${e.message}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -10,20 +10,20 @@ import android.os.IBinder
|
|||||||
import android.util.Log
|
import android.util.Log
|
||||||
import br.com.aditum.IAditumSdkService
|
import br.com.aditum.IAditumSdkService
|
||||||
import br.com.aditum.data.v2.model.MerchantData
|
import br.com.aditum.data.v2.model.MerchantData
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
|
import kotlin.coroutines.resume
|
||||||
|
import kotlin.coroutines.resumeWithException
|
||||||
|
|
||||||
class PaymentApplication : Application() {
|
class PaymentApplication : Application() {
|
||||||
companion object {
|
companion object {
|
||||||
private const val TAG = "PaymentApplication"
|
|
||||||
private const val PACKAGE_BASE_NAME = "br.com.aditum"
|
private const val PACKAGE_BASE_NAME = "br.com.aditum"
|
||||||
const val PACKAGE_NAME = "$PACKAGE_BASE_NAME.smartpostef"
|
const val PACKAGE_NAME = "$PACKAGE_BASE_NAME.smartpostef"
|
||||||
const val ACTION_COMMUNICATION_SERVICE = "$PACKAGE_BASE_NAME.AditumSdkService"
|
const val ACTION_COMMUNICATION_SERVICE = "$PACKAGE_BASE_NAME.AditumSdkService"
|
||||||
}
|
}
|
||||||
|
private val TAG = "PaymentApplication"
|
||||||
|
|
||||||
private val _isServiceConnectedFlow = MutableStateFlow(false)
|
private val _isServiceConnectedFlow = MutableStateFlow(false)
|
||||||
val isServiceConnectedFlow: StateFlow<Boolean> = _isServiceConnectedFlow.asStateFlow()
|
val isServiceConnectedFlow: StateFlow<Boolean> = _isServiceConnectedFlow.asStateFlow()
|
||||||
@ -55,81 +55,14 @@ class PaymentApplication : Application() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var mServiceConnectionListener: OnServiceConnectionListener? = null
|
|
||||||
var serviceConnectionListener: OnServiceConnectionListener?
|
|
||||||
get() = mServiceConnectionListener
|
|
||||||
set(listener) {
|
|
||||||
mServiceConnectionListener = listener
|
|
||||||
}
|
|
||||||
|
|
||||||
interface OnServiceConnectionListener {
|
interface OnServiceConnectionListener {
|
||||||
fun onServiceConnection(serviceConnected: Boolean)
|
fun onServiceConnection(serviceConnected: Boolean)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreate() {
|
private var serviceConnectionListener: OnServiceConnectionListener? = null
|
||||||
super.onCreate()
|
|
||||||
Log.d(TAG, "onCreate")
|
|
||||||
startAditumSdkService()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onTerminate() {
|
fun setServiceConnectionListener(listener: OnServiceConnectionListener) {
|
||||||
super.onTerminate()
|
this.serviceConnectionListener = listener
|
||||||
Log.d(TAG, "onTerminate")
|
|
||||||
}
|
|
||||||
|
|
||||||
fun startAditumSdkService() {
|
|
||||||
Log.d(TAG, "startAditumSdkService")
|
|
||||||
val intent = Intent(ACTION_COMMUNICATION_SERVICE).apply {
|
|
||||||
setPackage(PACKAGE_NAME)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
||||||
Log.d(TAG, "Android Oreo or higher: ${Build.VERSION.SDK_INT}")
|
|
||||||
startForegroundService(intent)
|
|
||||||
} else {
|
|
||||||
Log.d(TAG, "Android Nougat or lower: ${Build.VERSION.SDK_INT}")
|
|
||||||
startService(intent)
|
|
||||||
}
|
|
||||||
|
|
||||||
bindService(intent, mServiceConnection, BIND_AUTO_CREATE or CONTEXT_IGNORE_SECURITY)
|
|
||||||
Log.d(TAG, "startAditumSdkService - bindService")
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun ensureServiceConnected(): Boolean = withContext(Dispatchers.IO) {
|
|
||||||
if (isServiceConnected && mAditumSdkService?.asBinder()?.isBinderAlive == true) {
|
|
||||||
Log.d(TAG, "Service already connected")
|
|
||||||
return@withContext true
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.d(TAG, "Service not connected, attempting to reconnect...")
|
|
||||||
val intent = Intent(ACTION_COMMUNICATION_SERVICE).apply {
|
|
||||||
setPackage(PACKAGE_NAME)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
||||||
startForegroundService(intent)
|
|
||||||
} else {
|
|
||||||
startService(intent)
|
|
||||||
}
|
|
||||||
|
|
||||||
val bound = bindService(
|
|
||||||
intent,
|
|
||||||
mServiceConnection,
|
|
||||||
BIND_AUTO_CREATE or CONTEXT_IGNORE_SECURITY
|
|
||||||
)
|
|
||||||
|
|
||||||
if (bound) {
|
|
||||||
repeat(30) {
|
|
||||||
delay(100)
|
|
||||||
if (isServiceConnected && mAditumSdkService?.asBinder()?.isBinderAlive == true) {
|
|
||||||
Log.d(TAG, "Service reconnected successfully")
|
|
||||||
return@withContext true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.e(TAG, "Failed to reconnect service")
|
|
||||||
return@withContext false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setServiceConnected(isConnected: Boolean) {
|
private fun setServiceConnected(isConnected: Boolean) {
|
||||||
@ -137,7 +70,49 @@ class PaymentApplication : Application() {
|
|||||||
synchronized(this) {
|
synchronized(this) {
|
||||||
mIsServiceConnected = isConnected
|
mIsServiceConnected = isConnected
|
||||||
_isServiceConnectedFlow.value = isConnected
|
_isServiceConnectedFlow.value = isConnected
|
||||||
mServiceConnectionListener?.onServiceConnection(mIsServiceConnected)
|
serviceConnectionListener?.onServiceConnection(isConnected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
override fun onCreate() {
|
||||||
|
super.onCreate()
|
||||||
|
Log.d(TAG, "onCreate")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onTerminate() {
|
||||||
|
super.onTerminate()
|
||||||
|
Log.d(TAG, "onTerminate")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun startAditumSdkService(context: Context): Boolean {
|
||||||
|
Log.d(TAG, "startAditumSdkService")
|
||||||
|
val intent = Intent(ACTION_COMMUNICATION_SERVICE).apply {
|
||||||
|
setPackage(PACKAGE_NAME)
|
||||||
|
}
|
||||||
|
|
||||||
|
return try {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
context.startForegroundService(intent)
|
||||||
|
} else {
|
||||||
|
context.startService(intent)
|
||||||
|
}
|
||||||
|
context.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Failed to start or bind service: ${e.message}")
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun retryServiceConnection(context: Context, maxAttempts: Int = 3, delayMs: Long = 1000) {
|
||||||
|
var attempts = 0
|
||||||
|
while (attempts < maxAttempts && !isServiceConnected) {
|
||||||
|
if (startAditumSdkService(context)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
Thread.sleep(delayMs)
|
||||||
|
attempts++
|
||||||
|
}
|
||||||
|
if (!isServiceConnected) {
|
||||||
|
Log.e(TAG, "Failed to connect to service after $maxAttempts attempts")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
40
app/src/main/java/com/example/mypos/services/PrintService.kt
Normal file
40
app/src/main/java/com/example/mypos/services/PrintService.kt
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package com.example.mypos.services
|
||||||
|
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.util.Log
|
||||||
|
import br.com.aditum.data.v2.enums.PrintStatus
|
||||||
|
import br.com.aditum.device.callbacks.IPrintStatusCallback
|
||||||
|
import com.google.gson.Gson
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
class PrintService(private val paymentApplication: PaymentApplication) {
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "PrintService"
|
||||||
|
}
|
||||||
|
private val coroutineScope = CoroutineScope(Dispatchers.IO)
|
||||||
|
|
||||||
|
private val _printStatusFlow = MutableStateFlow(null)
|
||||||
|
val printStatusFlow: StateFlow<PrintStatus?> = _printStatusFlow.asStateFlow()
|
||||||
|
|
||||||
|
private val callback = object : IPrintStatusCallback.Stub() {
|
||||||
|
override fun finished(printStatus: PrintStatus?) {
|
||||||
|
coroutineScope.launch {
|
||||||
|
Log.d(TAG, "PrintStatus - $printStatus")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun print(vararg bitmaps: Bitmap) {
|
||||||
|
bitmaps.forEach { bitmap ->
|
||||||
|
paymentApplication.communicationService?.deviceSdk?.printerSdk?.print(
|
||||||
|
bitmap,
|
||||||
|
callback
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user