melhorando aditmSdkService

This commit is contained in:
Ighor Moura 2025-07-23 16:23:44 -04:00
parent 31ab97af95
commit 427c75b93c
7 changed files with 330 additions and 16 deletions

View 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>

View File

@ -21,6 +21,7 @@ android {
buildTypes {
debug {
buildConfigField ("String", "ACTIVATION_CODE", "\"436596309\"")
buildConfigField ("String", "APPLICATION_TOKEN", "\"mk_MbQ92RRHEOcGFRIf9/R1A\"")
}
release {

View File

@ -1,25 +1,35 @@
package com.example.mypos
import android.content.Context
import android.os.Bundle
import androidx.activity.ComponentActivity
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.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewmodel.viewModelFactory
import com.example.mypos.models.PaymentViewModel
import com.example.mypos.screen.PaymentScreen
import com.example.mypos.services.AditumSdkService
import com.example.mypos.services.PaymentApplication
import com.example.mypos.ui.theme.POSTheme
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
val context = this
var paymentApplication = application as PaymentApplication
var aditumSdkService = AditumSdkService(paymentApplication)
var paymentViewModel = ViewModelProvider(
this,
PaymentViewModel.provideFactory(paymentApplication, context, aditumSdkService)
)[PaymentViewModel::class.java]
setContent {
POSTheme {
PaymentScreen(paymentViewModel, aditumSdkService)
}
}
}

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

View File

@ -2,7 +2,12 @@ package com.example.mypos.models
import android.content.Context
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import br.com.aditum.data.v2.enums.AbecsCommands
import br.com.aditum.data.v2.enums.TransactionStatus
import com.example.mypos.data.PaymentRegister
import com.example.mypos.services.AditumSdkService
import com.example.mypos.services.PaymentApplication
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@ -11,9 +16,10 @@ import kotlinx.coroutines.launch
class PaymentViewModel(
private val application: PaymentApplication,
private val context: Context
private val context: Context,
private val aditumSdkService: AditumSdkService
) : ViewModel() {
private val paymentApplication = application as PaymentApplication
private val paymentApplication = application
private val _isServiceConnected = MutableStateFlow(false)
val isServiceConnected: StateFlow<Boolean> = _isServiceConnected.asStateFlow()
@ -22,10 +28,37 @@ class PaymentViewModel(
paymentApplication.isServiceConnectedFlow.collect { isConnected ->
_isServiceConnected.value = isConnected
}
aditumSdkService.register(
listener = object : PaymentRegister {
override fun notification(
message: String?,
transactionStatus: TransactionStatus?,
command: AbecsCommands?
) {
TODO("Not yet implemented")
}
}
)
}
}
fun startService() {
paymentApplication.startAditumSdkService()
}
companion object {
fun provideFactory(
paymentApplication: PaymentApplication,
context: Context,
aditumSdkService: AditumSdkService
): 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, aditumSdkService) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}
}
}

View File

@ -0,0 +1,186 @@
package com.example.mypos.screen
import androidx.compose.foundation.text.KeyboardOptions
import android.widget.Toast
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
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.services.AditumSdkService
import com.example.mypos.services.PaymentApplication
import kotlinx.coroutines.flow.collectLatest
@Composable
fun PaymentScreen(viewModel: PaymentViewModel, aditumSdkService: AditumSdkService) {
val context = LocalContext.current
var amount by remember { mutableStateOf("") }
var paymentType by remember { mutableStateOf(PaymentType.Credit) }
var isServiceConnected by remember { mutableStateOf(false) }
var message by remember { mutableStateOf("") }
var pinLength by remember { mutableStateOf(0) }
var isInitializing by remember { mutableStateOf(false) }
var isPaying by remember { mutableStateOf(false) }
LaunchedEffect(Unit) {
viewModel.isServiceConnected.collectLatest { connected ->
isServiceConnected = connected
}
}
LaunchedEffect(Unit) {
aditumSdkService.messageFlow.collectLatest { msg ->
message = msg
}
}
LaunchedEffect(Unit) {
aditumSdkService.pinLengthFlow.collectLatest { length ->
pinLength = length
}
}
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
Text(
text = "Pagamento",
style = MaterialTheme.typography.headlineMedium
)
Text(
text = if (isServiceConnected) "Serviço Conectado" else "Serviço Desconectado",
color = if (isServiceConnected) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.error
)
Button(
onClick = {
if (!isServiceConnected) {
viewModel.startService()
Toast.makeText(context, "Tentando conectar ao serviço...", Toast.LENGTH_SHORT).show()
return@Button
}
if (!isInitializing) {
isInitializing = true
aditumSdkService.initAditumSdk(
applicationName = "MyPOS",
applicationVersion = "1.0.0",
activationCode = BuildConfig.ACTIVATION_CODE,
resolve = { success ->
isInitializing = false
Toast.makeText(
context,
if (success) "SDK inicializado com sucesso!" else "Falha na inicialização.",
Toast.LENGTH_LONG
).show()
},
reject = { errorCode, errorMessage ->
isInitializing = false
Toast.makeText(context, "Erro: $errorMessage", Toast.LENGTH_LONG).show()
}
)
}
},
enabled = !isInitializing,
modifier = Modifier.fillMaxWidth()
) {
Text(if (isInitializing) "Inicializando..." else "Inicializar SDK")
}
OutlinedTextField(
value = amount,
onValueChange = { amount = it.filter { char -> char.isDigit() || char == '.' } },
label = { Text("Valor (R$)") },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Decimal),
modifier = Modifier.fillMaxWidth()
)
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceEvenly
) {
Row(verticalAlignment = Alignment.CenterVertically) {
RadioButton(
selected = paymentType == PaymentType.Credit,
onClick = { paymentType = PaymentType.Credit }
)
Text("Crédito")
}
Row(verticalAlignment = Alignment.CenterVertically) {
RadioButton(
selected = paymentType == PaymentType.Debit,
onClick = { paymentType = PaymentType.Debit }
)
Text("Débito")
}
}
Button(
onClick = {
val amountValue = amount.toDoubleOrNull()?.times(100)?.toLong()
if (amountValue == null || amountValue <= 0) {
Toast.makeText(context, "Insira um valor válido", Toast.LENGTH_SHORT).show()
return@Button
}
if (!isServiceConnected) {
Toast.makeText(context, "Serviço não conectado", Toast.LENGTH_SHORT).show()
return@Button
}
if (!isPaying) {
isPaying = true
aditumSdkService.pay(
amount = amountValue,
paymentType = paymentType,
allowContactless = true,
resolve = { response ->
isPaying = false
Toast.makeText(context, "Pagamento realizado com sucesso!", Toast.LENGTH_LONG).show()
},
reject = { errorCode, errorMessage ->
isPaying = false
Toast.makeText(context, "Erro no pagamento: $errorMessage", Toast.LENGTH_LONG).show()
}
)
}
},
enabled = !isPaying,
modifier = Modifier.fillMaxWidth()
) {
Text(if (isPaying) "Processando Pagamento..." else "Realizar Pagamento")
}
if (message.isNotEmpty()) {
Text(
text = "Mensagem do PIN pad: $message",
style = MaterialTheme.typography.bodyMedium
)
}
if (pinLength > 0) {
Text(
text = "Comprimento do PIN: $pinLength",
style = MaterialTheme.typography.bodyMedium
)
}
}
}

View File

@ -28,6 +28,7 @@ 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.data.PaymentRegister
import com.google.gson.Gson
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@ -52,7 +53,7 @@ class AditumSdkService(private val paymentApplication: PaymentApplication) {
private val _pinLengthFlow = MutableStateFlow(0)
val pinLengthFlow: StateFlow<Int> = _pinLengthFlow.asStateFlow()
fun register() {
fun register(listener: PaymentRegister) {
coroutineScope.launch {
val callback = object : IPaymentCallback.Stub() {
override fun notification(
@ -60,9 +61,15 @@ class AditumSdkService(private val paymentApplication: PaymentApplication) {
transactionStatus: TransactionStatus?,
command: AbecsCommands?
) {
Log.d(TAG, "\nnotification - ${ message ?: "" }")
if (message != null) {
_messageFlow.value = message.replace("\\s+".toRegex(), " ").trim()
}
listener.notification(
message?.replace("\\s+".toRegex(), " ")?.trim(),
transactionStatus,
command
)
}
override fun pinNotification(message: String?, length: Int) {
@ -88,9 +95,7 @@ class AditumSdkService(private val paymentApplication: PaymentApplication) {
}
}
paymentApplication.communicationService?.registerPaymentCallback(callback) ?: run {
}
paymentApplication.communicationService?.registerPaymentCallback(callback)
}
}
@ -117,6 +122,7 @@ class AditumSdkService(private val paymentApplication: PaymentApplication) {
this.activationCode = if (BuildConfig.DEBUG) BuildConfig.ACTIVATION_CODE else activationCode
this.applicationName = applicationName
this.applicationVersion = applicationVersion
this.useOnlySdk = true
this.applicationToken = BuildConfig.APPLICATION_TOKEN
}
@ -161,7 +167,7 @@ class AditumSdkService(private val paymentApplication: PaymentApplication) {
fun pay(
amount: Long,
installments: Int = 0,
installments: Int? = null,
paymentType: PaymentType,
allowContactless: Boolean = true,
amountSeasoning: ((Long) -> Long)? = null,
@ -188,8 +194,13 @@ class AditumSdkService(private val paymentApplication: PaymentApplication) {
this.amount = amount
this.allowContactless = allowContactless
manualEntry = false
installmentType = if (paymentType == PaymentType.Debit) InstallmentType.None else InstallmentType.Merchant
installmentNumber = installments
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() {