From f22ffbf7a750ad0b854912a3380d1a4a74f95d21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?La=C3=ADs=20Santos?= Date: Wed, 4 Sep 2024 21:54:41 +0200 Subject: [PATCH] Refactoring architecture Manage Stone Code Activity (#52) * refactor Manage Stone Code Activity * compose layout managestonecode screen * update gitignore * update layout * add unit tests to ManageStoneCodeViewModel * remove DevicesViewModel * remove exclude slf4j --- .gitignore | 1 + app/build.gradle | 36 +++- app/src/main/AndroidManifest.xml | 2 +- .../main/java/br/com/stonesdk/sdkdemo/DI.kt | 24 +++ .../br/com/stonesdk/sdkdemo/FeatureFlag.kt | 5 + .../sdkdemo/activities/DemoApplication.kt | 8 +- .../sdkdemo/activities/MainActivity.kt | 1 + .../ActivationProviderWrapper.kt | 51 +++++ .../ManageStoneCodeActivity.kt | 22 +- .../manageStoneCode/ManageStoneCodeLayout.kt | 197 ++++++++++++++++++ .../ManageStoneCodeViewModel.kt | 87 ++++++++ app/src/main/res/layout/activity_devices.xml | 5 +- .../res/layout/activity_manage_stone_code.xml | 2 +- .../ManageStoneCodeViewModelTest.kt | 129 ++++++++++++ build.gradle | 39 ++-- gradle/libs.versions.toml | 10 + settings.gradle | 1 - settings.gradle.kts | 1 + 18 files changed, 586 insertions(+), 35 deletions(-) create mode 100644 app/src/main/java/br/com/stonesdk/sdkdemo/DI.kt create mode 100644 app/src/main/java/br/com/stonesdk/sdkdemo/FeatureFlag.kt create mode 100644 app/src/main/java/br/com/stonesdk/sdkdemo/activities/manageStoneCode/ActivationProviderWrapper.kt rename app/src/main/java/br/com/stonesdk/sdkdemo/activities/{ => manageStoneCode}/ManageStoneCodeActivity.kt (90%) create mode 100644 app/src/main/java/br/com/stonesdk/sdkdemo/activities/manageStoneCode/ManageStoneCodeLayout.kt create mode 100644 app/src/main/java/br/com/stonesdk/sdkdemo/activities/manageStoneCode/ManageStoneCodeViewModel.kt create mode 100644 app/src/test/java/br/com/stonesdk/sdkdemo/activities/manageStoneCode/ManageStoneCodeViewModelTest.kt create mode 100644 gradle/libs.versions.toml delete mode 100644 settings.gradle create mode 100644 settings.gradle.kts diff --git a/.gitignore b/.gitignore index ccc367d..06bf4ad 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ gertec/gertec-keystore.properties positivo/positivo-keystore.properties gertec/*.jks positivo/*.jks +.kotlin \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index ab66ae8..40268ce 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,5 +1,8 @@ -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' +plugins { + id 'com.android.application' + id 'org.jetbrains.kotlin.android' + alias(libs.plugins.compose.compiler) +} apply from: '../gertec/gertec-signing-config.gradle' apply from: '../positivo/positivo-signing-config.gradle' @@ -23,6 +26,14 @@ android { versionName "1.0" } + buildFeatures { + compose true + } + + testOptions { + unitTests.returnDefaultValues = true + } + buildTypes { //Necessary for the functioning of Positivo's signature in debug mode. @@ -49,6 +60,7 @@ android { exclude 'META-INF/client_release.kotlin_module' } + buildFeatures { viewBinding true } @@ -56,6 +68,23 @@ android { } dependencies { + + def composeBom = platform('androidx.compose:compose-bom:2024.06.00') + implementation composeBom + implementation 'androidx.compose.material3:material3' + implementation 'androidx.compose.foundation:foundation' + implementation 'androidx.compose.ui:ui' + implementation 'androidx.compose.ui:ui-tooling-preview' + implementation 'androidx.activity:activity-compose:1.9.1' + implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.8.3' + + implementation("io.insert-koin:koin-core:3.1.6") + implementation("io.insert-koin:koin-android:3.1.6") + implementation("io.insert-koin:koin-androidx-compose:3.1.6") + + debugImplementation 'androidx.compose.ui:ui-tooling' + androidTestImplementation composeBom + implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'androidx.appcompat:appcompat:1.7.0' implementation 'com.github.permissions-dispatcher:permissionsdispatcher:4.8.0' @@ -75,10 +104,9 @@ dependencies { implementation "androidx.activity:activity-ktx:1.9.1" implementation "androidx.fragment:fragment-ktx:1.8.2" - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - testImplementation "io.mockk:mockk:1.13.12" testImplementation "org.junit.platform:junit-platform-launcher:1.10.0" testImplementation "org.junit.jupiter:junit-jupiter-engine:5.10.0" testImplementation "org.junit.vintage:junit-vintage-engine:5.10.0" + testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:1.9.0-RC" } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 80de3ac..483185a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -52,7 +52,7 @@ android:name=".activities.PosTransactionActivity" android:label="@string/title_activity_transaction" /> diff --git a/app/src/main/java/br/com/stonesdk/sdkdemo/DI.kt b/app/src/main/java/br/com/stonesdk/sdkdemo/DI.kt new file mode 100644 index 0000000..3097c9c --- /dev/null +++ b/app/src/main/java/br/com/stonesdk/sdkdemo/DI.kt @@ -0,0 +1,24 @@ +package br.com.stonesdk.sdkdemo + +import br.com.stonesdk.sdkdemo.activities.manageStoneCode.ActivationProviderWrapper +import br.com.stonesdk.sdkdemo.activities.manageStoneCode.ManageStoneCodeViewModel +import org.koin.androidx.viewmodel.dsl.viewModel +import org.koin.dsl.module +import stone.application.SessionApplication +import stone.providers.ActiveApplicationProvider +import stone.utils.Stone + +val demoApplicationModule = module { + + factory { + Stone.sessionApplication + } + + factory { + ActivationProviderWrapper(get()) + } + + viewModel { + ManageStoneCodeViewModel(get(), get()) + } +} \ No newline at end of file diff --git a/app/src/main/java/br/com/stonesdk/sdkdemo/FeatureFlag.kt b/app/src/main/java/br/com/stonesdk/sdkdemo/FeatureFlag.kt new file mode 100644 index 0000000..29bb1a9 --- /dev/null +++ b/app/src/main/java/br/com/stonesdk/sdkdemo/FeatureFlag.kt @@ -0,0 +1,5 @@ +package br.com.stonesdk.sdkdemo + +object FeatureFlag { + const val composeRefactorEnabled = true +} \ No newline at end of file diff --git a/app/src/main/java/br/com/stonesdk/sdkdemo/activities/DemoApplication.kt b/app/src/main/java/br/com/stonesdk/sdkdemo/activities/DemoApplication.kt index 5c57895..c3bbcb3 100644 --- a/app/src/main/java/br/com/stonesdk/sdkdemo/activities/DemoApplication.kt +++ b/app/src/main/java/br/com/stonesdk/sdkdemo/activities/DemoApplication.kt @@ -1,13 +1,19 @@ package br.com.stonesdk.sdkdemo.activities import android.app.Application +import br.com.stonesdk.sdkdemo.demoApplicationModule +import org.koin.android.ext.koin.androidContext +import org.koin.core.context.GlobalContext.startKoin import stone.application.StoneStart - class DemoApplication : Application() { override fun onCreate() { super.onCreate() StoneStart.init(this) + startKoin { + androidContext(this@DemoApplication) + modules(demoApplicationModule) + } } } \ No newline at end of file diff --git a/app/src/main/java/br/com/stonesdk/sdkdemo/activities/MainActivity.kt b/app/src/main/java/br/com/stonesdk/sdkdemo/activities/MainActivity.kt index 751e35c..4b2aaa2 100644 --- a/app/src/main/java/br/com/stonesdk/sdkdemo/activities/MainActivity.kt +++ b/app/src/main/java/br/com/stonesdk/sdkdemo/activities/MainActivity.kt @@ -12,6 +12,7 @@ import androidx.appcompat.app.AppCompatActivity import br.com.stone.posandroid.providers.PosPrintProvider import br.com.stone.posandroid.providers.PosValidateTransactionByCardProvider import br.com.stonesdk.sdkdemo.R +import br.com.stonesdk.sdkdemo.activities.manageStoneCode.ManageStoneCodeActivity import br.com.stonesdk.sdkdemo.databinding.ActivityMainBinding import stone.application.enums.Action import stone.application.interfaces.StoneActionCallback diff --git a/app/src/main/java/br/com/stonesdk/sdkdemo/activities/manageStoneCode/ActivationProviderWrapper.kt b/app/src/main/java/br/com/stonesdk/sdkdemo/activities/manageStoneCode/ActivationProviderWrapper.kt new file mode 100644 index 0000000..deb0701 --- /dev/null +++ b/app/src/main/java/br/com/stonesdk/sdkdemo/activities/manageStoneCode/ActivationProviderWrapper.kt @@ -0,0 +1,51 @@ +package br.com.stonesdk.sdkdemo.activities.manageStoneCode + +import android.content.Context +import kotlinx.coroutines.suspendCancellableCoroutine +import stone.application.interfaces.StoneCallbackInterface +import stone.providers.ActiveApplicationProvider +import kotlin.coroutines.resume + +class ActivationProviderWrapper( + private val context: Context +) { + + suspend fun activate(stoneCode: String): Boolean = suspendCancellableCoroutine { continuation -> + val provider = newProvider() + + provider.connectionCallback = object : StoneCallbackInterface { + override fun onSuccess() { + continuation.resume(true) + } + + override fun onError() { + continuation.resume(false) + } + } + + provider.activate(stoneCode) + + continuation.invokeOnCancellation {} + } + + suspend fun deactivate(stoneCode: String): Boolean = + suspendCancellableCoroutine { continuation -> + val provider = newProvider() + + provider.connectionCallback = object : StoneCallbackInterface { + override fun onSuccess() { + continuation.resume(true) + } + + override fun onError() { + continuation.resume(false) + } + } + + provider.deactivate(stoneCode) + + continuation.invokeOnCancellation {} + } + + fun newProvider() = ActiveApplicationProvider(context) +} \ No newline at end of file diff --git a/app/src/main/java/br/com/stonesdk/sdkdemo/activities/ManageStoneCodeActivity.kt b/app/src/main/java/br/com/stonesdk/sdkdemo/activities/manageStoneCode/ManageStoneCodeActivity.kt similarity index 90% rename from app/src/main/java/br/com/stonesdk/sdkdemo/activities/ManageStoneCodeActivity.kt rename to app/src/main/java/br/com/stonesdk/sdkdemo/activities/manageStoneCode/ManageStoneCodeActivity.kt index 56bb763..3602a36 100644 --- a/app/src/main/java/br/com/stonesdk/sdkdemo/activities/ManageStoneCodeActivity.kt +++ b/app/src/main/java/br/com/stonesdk/sdkdemo/activities/manageStoneCode/ManageStoneCodeActivity.kt @@ -1,26 +1,23 @@ -package br.com.stonesdk.sdkdemo.activities +package br.com.stonesdk.sdkdemo.activities.manageStoneCode import android.os.Bundle import android.util.Log -import android.view.View import android.widget.AdapterView.OnItemClickListener import android.widget.ArrayAdapter import android.widget.EditText import android.widget.ListView import android.widget.Toast +import androidx.activity.compose.setContent import androidx.appcompat.app.AppCompatActivity +import androidx.compose.material3.MaterialTheme +import br.com.stonesdk.sdkdemo.FeatureFlag import br.com.stonesdk.sdkdemo.R -import br.com.stonesdk.sdkdemo.databinding.ActivityMainBinding import br.com.stonesdk.sdkdemo.databinding.ActivityManageStoneCodeBinding import stone.application.interfaces.StoneCallbackInterface import stone.providers.ActiveApplicationProvider import stone.user.UserModel import stone.utils.Stone -/** - * @author tiago.barbosa - * @since 10/04/2018 - */ class ManageStoneCodeActivity : AppCompatActivity() { private lateinit var binding: ActivityManageStoneCodeBinding @@ -30,7 +27,18 @@ class ManageStoneCodeActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + if (FeatureFlag.composeRefactorEnabled) { + setContent { + MaterialTheme { + ManageStoneCodeScreen() + } + } + } else { + onCreateConfig() + } + } + private fun onCreateConfig() { binding = ActivityManageStoneCodeBinding.inflate(layoutInflater) setContentView(binding.root) diff --git a/app/src/main/java/br/com/stonesdk/sdkdemo/activities/manageStoneCode/ManageStoneCodeLayout.kt b/app/src/main/java/br/com/stonesdk/sdkdemo/activities/manageStoneCode/ManageStoneCodeLayout.kt new file mode 100644 index 0000000..8b0593a --- /dev/null +++ b/app/src/main/java/br/com/stonesdk/sdkdemo/activities/manageStoneCode/ManageStoneCodeLayout.kt @@ -0,0 +1,197 @@ +package br.com.stonesdk.sdkdemo.activities.manageStoneCode + +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.heightIn +import androidx.compose.foundation.layout.navigationBarsPadding +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.itemsIndexed +import androidx.compose.foundation.text.KeyboardActions +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight +import androidx.compose.material3.Button +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Icon +import androidx.compose.material3.ModalBottomSheet +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import br.com.stonesdk.sdkdemo.activities.manageStoneCode.ManageStoneCodeEvent.ActivateStoneCode +import br.com.stonesdk.sdkdemo.activities.manageStoneCode.ManageStoneCodeEvent.AddStoneCode +import br.com.stonesdk.sdkdemo.activities.manageStoneCode.ManageStoneCodeEvent.OnDismiss +import br.com.stonesdk.sdkdemo.activities.manageStoneCode.ManageStoneCodeEvent.StoneCodeItemClick +import br.com.stonesdk.sdkdemo.activities.manageStoneCode.ManageStoneCodeEvent.UserInput +import org.koin.androidx.compose.getViewModel + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +internal fun ManageStoneCodeScreen( + viewModel: ManageStoneCodeViewModel = getViewModel() +) { + + if (viewModel.viewState.showBottomSheet) { + ModalBottomSheet( + onDismissRequest = { viewModel.onEvent(OnDismiss) }, + content = { + BottomSheetContent( + model = viewModel.viewState, + onEvent = viewModel::onEvent + ) + } + ) + } + + ManageStoneCodeContent( + model = viewModel.viewState, + onEvent = viewModel::onEvent, + ) +} + +@OptIn(ExperimentalFoundationApi::class) +@Composable +private fun ManageStoneCodeContent( + model: ManageStoneCodeUiModel, + onEvent: (ManageStoneCodeEvent) -> Unit, +) { + Column( + modifier = Modifier + .padding(horizontal = 16.dp, vertical = 8.dp) + .fillMaxSize() + ) { + + Spacer(modifier = Modifier.height(16.dp)) + + if (model.stoneCodesActivated.isEmpty()) { + Text( + modifier = Modifier + .padding(top = 40.dp) + .fillMaxWidth(), + fontSize = 16.sp, + textAlign = TextAlign.Center, + text = "Seus Stone codes ativados aparecerão aqui" + ) + } else { + Text( + modifier = Modifier.padding(bottom = 8.dp), + text = "Stone codes ativados", + fontSize = 20.sp, + fontWeight = FontWeight.W500 + ) + } + LazyColumn(modifier = Modifier.weight(1f)) { + itemsIndexed( + items = model.stoneCodesActivated, + key = { index, stoneCode -> "$index$stoneCode" }, + itemContent = { index, stoneCode -> + Column { + StoneCodeActivatedItem( + modifier = Modifier + .animateItemPlacement() + .clickable { + onEvent(StoneCodeItemClick(position = index)) + }, + stoneCode = stoneCode, + ) + } + if (model.stoneCodesActivated.lastIndex > index) { + HorizontalDivider() + } + } + ) + } + Button(modifier = Modifier.align(Alignment.CenterHorizontally), + onClick = { onEvent(AddStoneCode) }, + content = { + Text(text = "Adicionar Stone Code") + } + ) + } +} + +@Composable +private fun StoneCodeActivatedItem( + modifier: Modifier = Modifier, + stoneCode: String, +) { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = modifier.heightIn(min = 44.dp) + ) { + Text( + modifier = Modifier.weight(1f), + textAlign = TextAlign.Left, + text = stoneCode, + fontSize = 18.sp + ) + Icon( + modifier = Modifier + .size(18.dp) + .padding(start = 4.dp), + imageVector = Icons.AutoMirrored.Filled.KeyboardArrowRight, + contentDescription = null + ) + } +} + +@Composable +private fun BottomSheetContent( + model: ManageStoneCodeUiModel, onEvent: (ManageStoneCodeEvent) -> Unit +) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp) + ) { + OutlinedTextField( + modifier = Modifier.fillMaxWidth(), + placeholder = { + Text(text = "Digite o stone code") + }, + value = model.stoneCodeToBeActivated, + onValueChange = { stoneCode -> onEvent(UserInput(stoneCode)) }, + singleLine = true, + keyboardOptions = KeyboardOptions.Default.copy(keyboardType = KeyboardType.Number), + keyboardActions = KeyboardActions( + onDone = { onEvent(ActivateStoneCode) } + ) + ) + + Spacer(modifier = Modifier.height(8.dp)) + + Button( + modifier = Modifier + .align(Alignment.CenterHorizontally) + .navigationBarsPadding(), + onClick = { onEvent(ActivateStoneCode) }, + enabled = model.stoneCodeToBeActivated.isNotEmpty() && !model.activationInProgress, + content = { + if (model.activationInProgress) { + CircularProgressIndicator( + modifier = Modifier + .size(20.dp) + .padding(end = 4.dp) + ) + } + Text(text = "Ativar") + } + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/br/com/stonesdk/sdkdemo/activities/manageStoneCode/ManageStoneCodeViewModel.kt b/app/src/main/java/br/com/stonesdk/sdkdemo/activities/manageStoneCode/ManageStoneCodeViewModel.kt new file mode 100644 index 0000000..de8e937 --- /dev/null +++ b/app/src/main/java/br/com/stonesdk/sdkdemo/activities/manageStoneCode/ManageStoneCodeViewModel.kt @@ -0,0 +1,87 @@ +package br.com.stonesdk.sdkdemo.activities.manageStoneCode + +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import br.com.stonesdk.sdkdemo.activities.manageStoneCode.ManageStoneCodeEvent.ActivateStoneCode +import br.com.stonesdk.sdkdemo.activities.manageStoneCode.ManageStoneCodeEvent.StoneCodeItemClick +import br.com.stonesdk.sdkdemo.activities.manageStoneCode.ManageStoneCodeEvent.UserInput +import kotlinx.coroutines.launch +import stone.application.SessionApplication + +class ManageStoneCodeViewModel( + private val sessionApplication: SessionApplication, + private val providerWrapper: ActivationProviderWrapper, +) : ViewModel() { + + var viewState by mutableStateOf(ManageStoneCodeUiModel()) + private set + + init { + listStoneCodesActivated() + } + + fun onEvent(event: ManageStoneCodeEvent) { + when (event) { + is ActivateStoneCode -> activateStoneCode() + is UserInput -> viewState = viewState.copy(stoneCodeToBeActivated = event.stoneCode) + ManageStoneCodeEvent.AddStoneCode -> viewState = viewState.copy(showBottomSheet = true) + ManageStoneCodeEvent.OnDismiss -> viewState = viewState.copy(showBottomSheet = false) + is StoneCodeItemClick -> deactivateStoneCode(position = event.position) + } + } + + private fun activateStoneCode() { + viewModelScope.launch { + viewState = viewState.copy(activationInProgress = true) + val isSuccess = providerWrapper.activate(viewState.stoneCodeToBeActivated) + + if (isSuccess) { + listStoneCodesActivated() + } + + viewState = viewState.copy( + error = !isSuccess, + activationInProgress = false, + showBottomSheet = !isSuccess, + stoneCodeToBeActivated = if (isSuccess) "" else viewState.stoneCodeToBeActivated + ) + } + } + + private fun deactivateStoneCode(position: Int) { + viewModelScope.launch { + val isSuccess = providerWrapper.deactivate(viewState.stoneCodesActivated[position]) + + if (isSuccess) { + listStoneCodesActivated() + } + } + } + + private fun listStoneCodesActivated() { + val stoneCodes = sessionApplication.userModelList + .map { userModel -> userModel.stoneCode } + .toList() + + viewState = viewState.copy(stoneCodesActivated = stoneCodes) + } +} + +data class ManageStoneCodeUiModel( + val error: Boolean = false, + val stoneCodeToBeActivated: String = "", + val stoneCodesActivated: List = emptyList(), + val showBottomSheet: Boolean = false, + val activationInProgress: Boolean = false +) + +sealed interface ManageStoneCodeEvent { + data class UserInput(val stoneCode: String) : ManageStoneCodeEvent + data object AddStoneCode : ManageStoneCodeEvent + data object OnDismiss : ManageStoneCodeEvent + data object ActivateStoneCode : ManageStoneCodeEvent + data class StoneCodeItemClick(val position: Int) : ManageStoneCodeEvent +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_devices.xml b/app/src/main/res/layout/activity_devices.xml index bcc8879..a5643f5 100644 --- a/app/src/main/res/layout/activity_devices.xml +++ b/app/src/main/res/layout/activity_devices.xml @@ -5,8 +5,7 @@ - + android:layout_width="match_parent" + android:layout_height="wrap_content"> diff --git a/app/src/main/res/layout/activity_manage_stone_code.xml b/app/src/main/res/layout/activity_manage_stone_code.xml index cc1947c..3b16672 100644 --- a/app/src/main/res/layout/activity_manage_stone_code.xml +++ b/app/src/main/res/layout/activity_manage_stone_code.xml @@ -4,7 +4,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - tools:context="br.com.stonesdk.sdkdemo.activities.ManageStoneCodeActivity"> + tools:context="br.com.stonesdk.sdkdemo.activities.manageStoneCode.ManageStoneCodeActivity">