diff --git a/README.md b/README.md index bd73feb5f..654583031 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,44 @@ +# Teste - Accenture +Projeto de teste para seleção da empresa Accenture para o cargo de Desenvolvedor Mobile Android + +# Procedimentos para execução do projeto +O projeto android encontra-se na pasta "TesteAndroid". +Para execução do projeto é necessário abrí-lo na IDE Android Studio. + +# Procedimentos para execução dos testes unitários +Foi implementada uma classe para realização de todos os testes unitários do sistema +em 'com.accenture.android.app.testeandroid.TestSuite'. + +Basta executar esta classe e todos os testes implementados serão processados. + +# Implementação +Como forma de implementação da solicitação de exibição na tela de login do último usuário salvo, foi utilizado o componente nativo da SDK do Android “SharedPreferences”. +Para exibição do último usuário salvo no sistema, foi definida na tela de Login uma pequena descrição com o nome do usuário, sendo esse buscado do SharedPreferences do smartphone. Segue exemplo: + +![login_image](login_print.png) + +Obs.: Caso o usuário vá para a segunda tela do aplicativo onde ele clique no botão de logout ![logout_image](logout_print.png) , ele retornará para a tela de login e o nome do usuário não será apresentado, pois os dados do usuário são apagados do sistema. Para que essa descrição com o nome seja visualizada, é necessário efetuar o login ao menos uma vez no aplicativo, sair sem se deslogar (Fechar o aplicativo) e entrar novamente. +No meu entendimento, este desafio seria para testar a utilização de autenticação e a forma de guardar dados no smartphone para posterior processamento. Sendo assim, a implementação foi feita pensando em um caso de uso real, onde se o usuário não fizesse o logout, quando ele abrisse o aplicativo novamente e fosse para a tela de login já haveria dados pré-cadastrados no sistema e a autenticação seria feita de forma automática. Porém, para exemplificar, apresento apenas o nome do usuário na tela. + +## Arquitetura +Para confecção do aplicativo foi utilizado a arquitetura MVP (Model View Presenter). +Essa arquitetura foi escolhida por ser um padrão amplamente utilizado em desenvolvimento mobile, com fácil entendimento e uma boa separação de responsabilidades. + +Definição rápida dos diretórios criados: +* data: Contém todo o código referente a dados do sistema (Gerenciamento de consumo de APIs e conversores de dados) +* domain: Contém as classes de domínio. Nossas models, por assim dizer. +* helpers: Contém classes que irão ajudar no desenvolvimento do sistema que podem ser reutilizadas em qualquer local e que não possuem nenhuma dependência com componentes do SDK do Android (Soma, subtração, formatação de strings, etc...). +* presentation: Contém toda a lógica de cada tela, junto com seus respectivos presenters. +* utils: Contém classes que são um pouco mais complexas ou elaboradas que podem ser reutilizadas em qualquer local, porém possuem uma dependência do SDK do Android (SharedPreferences, por exemplo). + +## Teste unitários +Para os testes unitários foi utilizado o JUnit por ser nativo do Java e possuir uma quantidade grande de ferramentas para serem utilizadas. + +## Outras bibliotecas +Para consumo das APIs foi utilizado o framework Retrofit2. Esse framework abstrai uma boa parte da lógica de acessos HTTP e ajuda no gerenciamento das rotas que serão utilizadas. + +Para listagem dos 'statements' foi utilizada a biblioteca RecyclerView. + # Show me the code Esse repositório contem todo o material necessário para realizar o teste: diff --git a/TesteAndroid/.gitignore b/TesteAndroid/.gitignore new file mode 100644 index 000000000..603b14077 --- /dev/null +++ b/TesteAndroid/.gitignore @@ -0,0 +1,14 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx diff --git a/TesteAndroid/.idea/codeStyles/Project.xml b/TesteAndroid/.idea/codeStyles/Project.xml new file mode 100644 index 000000000..fb22c1d7e --- /dev/null +++ b/TesteAndroid/.idea/codeStyles/Project.xml @@ -0,0 +1,164 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + xmlns:android + + ^$ + + + +
+
+ + + + xmlns:.* + + ^$ + + + BY_NAME + +
+
+ + + + .*:id + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + .*:name + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + name + + ^$ + + + +
+
+ + + + style + + ^$ + + + +
+
+ + + + .* + + ^$ + + + BY_NAME + +
+
+ + + + .* + + http://schemas.android.com/apk/res/android + + + ANDROID_ATTRIBUTE_ORDER + +
+
+ + + + .* + + .* + + + BY_NAME + +
+
+
+
+
+
\ No newline at end of file diff --git a/TesteAndroid/.idea/dbnavigator.xml b/TesteAndroid/.idea/dbnavigator.xml new file mode 100644 index 000000000..51325a2a9 --- /dev/null +++ b/TesteAndroid/.idea/dbnavigator.xml @@ -0,0 +1,458 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/TesteAndroid/.idea/gradle.xml b/TesteAndroid/.idea/gradle.xml new file mode 100644 index 000000000..ac6b0aec6 --- /dev/null +++ b/TesteAndroid/.idea/gradle.xml @@ -0,0 +1,21 @@ + + + + + + + \ No newline at end of file diff --git a/TesteAndroid/.idea/jarRepositories.xml b/TesteAndroid/.idea/jarRepositories.xml new file mode 100644 index 000000000..a5f05cd8c --- /dev/null +++ b/TesteAndroid/.idea/jarRepositories.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/TesteAndroid/.idea/misc.xml b/TesteAndroid/.idea/misc.xml new file mode 100644 index 000000000..37a750962 --- /dev/null +++ b/TesteAndroid/.idea/misc.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/TesteAndroid/.idea/runConfigurations.xml b/TesteAndroid/.idea/runConfigurations.xml new file mode 100644 index 000000000..7f68460d8 --- /dev/null +++ b/TesteAndroid/.idea/runConfigurations.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/TesteAndroid/.idea/vcs.xml b/TesteAndroid/.idea/vcs.xml new file mode 100644 index 000000000..6c0b86358 --- /dev/null +++ b/TesteAndroid/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/TesteAndroid/app/.gitignore b/TesteAndroid/app/.gitignore new file mode 100644 index 000000000..42afabfd2 --- /dev/null +++ b/TesteAndroid/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/TesteAndroid/app/build.gradle b/TesteAndroid/app/build.gradle new file mode 100644 index 000000000..0bf5a6132 --- /dev/null +++ b/TesteAndroid/app/build.gradle @@ -0,0 +1,41 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 29 + + defaultConfig { + applicationId "com.accenture.android.app.testeandroid" + minSdkVersion 19 + targetSdkVersion 29 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + + buildFeatures { + dataBinding = true + } +} + +dependencies { + implementation fileTree(dir: "libs", include: ["*.jar"]) + implementation 'androidx.appcompat:appcompat:1.1.0' + implementation 'androidx.constraintlayout:constraintlayout:1.1.3' + implementation 'androidx.recyclerview:recyclerview:1.1.0' + implementation 'com.fasterxml.jackson.core:jackson-databind:2.10.2' + implementation 'com.google.android.material:material:1.3.0-alpha01' + implementation 'com.squareup.retrofit2:retrofit:2.6.4' + implementation 'com.squareup.retrofit2:converter-gson:2.6.4' + testImplementation 'junit:junit:4.13' + androidTestImplementation 'androidx.test.ext:junit:1.1.1' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' + +} \ No newline at end of file diff --git a/TesteAndroid/app/proguard-rules.pro b/TesteAndroid/app/proguard-rules.pro new file mode 100644 index 000000000..481bb4348 --- /dev/null +++ b/TesteAndroid/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/TesteAndroid/app/src/androidTest/java/com/accenture/android/app/testeandroid/ExampleInstrumentedTest.java b/TesteAndroid/app/src/androidTest/java/com/accenture/android/app/testeandroid/ExampleInstrumentedTest.java new file mode 100644 index 000000000..4c4bb9b41 --- /dev/null +++ b/TesteAndroid/app/src/androidTest/java/com/accenture/android/app/testeandroid/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package com.accenture.android.app.testeandroid; + +import android.content.Context; + +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + assertEquals("com.accenture.android.app.testeandroid", appContext.getPackageName()); + } +} \ No newline at end of file diff --git a/TesteAndroid/app/src/main/AndroidManifest.xml b/TesteAndroid/app/src/main/AndroidManifest.xml new file mode 100644 index 000000000..20b63cac6 --- /dev/null +++ b/TesteAndroid/app/src/main/AndroidManifest.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/App.java b/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/App.java new file mode 100644 index 000000000..5e347e809 --- /dev/null +++ b/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/App.java @@ -0,0 +1,19 @@ +package com.accenture.android.app.testeandroid; + +import android.app.Application; +import android.util.Log; + +/** + * Created by Denis Magno on 09/07/2020. + * denis_magno16@hotmail.com + */ +public class App extends Application { + private final static String TAG = "CustomLog - " + App.class.getSimpleName(); + + @Override + public void onCreate() { + super.onCreate(); + + Log.i(TAG, "Application started."); + } +} diff --git a/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/data/converters/StatementConverter.java b/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/data/converters/StatementConverter.java new file mode 100644 index 000000000..70f16f8e7 --- /dev/null +++ b/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/data/converters/StatementConverter.java @@ -0,0 +1,36 @@ +package com.accenture.android.app.testeandroid.data.converters; + +import com.accenture.android.app.testeandroid.data.http.responses.StatementResponse; +import com.accenture.android.app.testeandroid.domain.Statement; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by Denis Magno on 09/07/2020. + * denis_magno16@hotmail.com + */ +public class StatementConverter { + public static Statement toDomain(StatementResponse.StatementData statementDataResponse) { + Statement statement = new Statement(); + + statement.setTitle(statementDataResponse.getTitle() == null ? "" : statementDataResponse.getTitle()); + statement.setDesc(statementDataResponse.getDesc() == null ? "" : statementDataResponse.getDesc()); + statement.setDate(statementDataResponse.getDate() == null ? "" : statementDataResponse.getDate()); + statement.setValue(statementDataResponse.getValue() == null ? 0 : statementDataResponse.getValue()); + + return statement; + } + + public static ArrayList toDomain(List statementDataResponses) { + ArrayList statements = new ArrayList<>(); + + for (StatementResponse.StatementData statementDataResponse : statementDataResponses) { + Statement statement = StatementConverter.toDomain(statementDataResponse); + + statements.add(statement); + } + + return statements; + } +} diff --git a/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/data/converters/UserAccountConverter.java b/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/data/converters/UserAccountConverter.java new file mode 100644 index 000000000..5bc6e1e66 --- /dev/null +++ b/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/data/converters/UserAccountConverter.java @@ -0,0 +1,22 @@ +package com.accenture.android.app.testeandroid.data.converters; + +import com.accenture.android.app.testeandroid.data.http.responses.UserAccountResponse; +import com.accenture.android.app.testeandroid.domain.UserAccount; + +/** + * Created by Denis Magno on 09/07/2020. + * denis_magno16@hotmail.com + */ +public class UserAccountConverter { + public static UserAccount toDomain(UserAccountResponse.UserAccountData userAccountDataResponse) { + UserAccount userAccount = new UserAccount(); + + userAccount.setUserId(userAccountDataResponse.getUserId() == null ? 0L : userAccountDataResponse.getUserId()); + userAccount.setName(userAccountDataResponse.getName() == null ? "" : userAccountDataResponse.getName()); + userAccount.setBankAccount(userAccountDataResponse.getBankAccount() == null ? "" : userAccountDataResponse.getBankAccount()); + userAccount.setAgency(userAccountDataResponse.getAgency() == null ? "" : userAccountDataResponse.getAgency()); + userAccount.setBalance(userAccountDataResponse.getBalance() == null ? 0 : userAccountDataResponse.getBalance()); + + return userAccount; + } +} diff --git a/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/data/http/RetrofitConfig.java b/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/data/http/RetrofitConfig.java new file mode 100644 index 000000000..ffc6a0249 --- /dev/null +++ b/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/data/http/RetrofitConfig.java @@ -0,0 +1,40 @@ +package com.accenture.android.app.testeandroid.data.http; + +import com.accenture.android.app.testeandroid.data.http.resources.AuthResource; +import com.accenture.android.app.testeandroid.data.http.resources.StatementResource; + +import java.util.concurrent.TimeUnit; + +import okhttp3.OkHttpClient; +import retrofit2.Retrofit; +import retrofit2.converter.gson.GsonConverterFactory; + +/** + * Created by Denis Magno on 09/07/2020. + * denis_magno16@hotmail.com + */ +public class RetrofitConfig { + private final Retrofit retrofit; + + public RetrofitConfig(String baseUrl) { + OkHttpClient client = new OkHttpClient.Builder() + .connectTimeout(30, TimeUnit.SECONDS) + .readTimeout(30, TimeUnit.SECONDS) + .writeTimeout(30, TimeUnit.SECONDS) + .build(); + + this.retrofit = new Retrofit.Builder() + .baseUrl(baseUrl) + .client(client) + .addConverterFactory(GsonConverterFactory.create()) + .build(); + } + + public AuthResource getAuthResource() throws NullPointerException { + return this.retrofit.create(AuthResource.class); + } + + public StatementResource getStatementResource() throws NullPointerException { + return this.retrofit.create(StatementResource.class); + } +} \ No newline at end of file diff --git a/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/data/http/helpers/ResponseHelper.java b/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/data/http/helpers/ResponseHelper.java new file mode 100644 index 000000000..dc34c0a1d --- /dev/null +++ b/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/data/http/helpers/ResponseHelper.java @@ -0,0 +1,58 @@ +package com.accenture.android.app.testeandroid.data.http.helpers; + +import com.accenture.android.app.testeandroid.data.http.responses.generics.ErrorResponse; + +import java.io.IOException; +import java.net.ConnectException; +import java.net.UnknownHostException; + +import retrofit2.Response; + +/** + * Created by Denis Magno on 09/07/2020. + * denis_magno16@hotmail.com + */ +public class ResponseHelper { + public static ErrorResponse createResponse(Response response) { + ErrorResponse errorResponse = new ErrorResponse(); + + if (response.errorBody() != null) { + String messageErrorBody = ""; + + try { + messageErrorBody = response.errorBody().string(); + } catch (IOException e) { + e.printStackTrace(); + } + + if (!messageErrorBody.isEmpty()) { + errorResponse = new ErrorResponse(0, messageErrorBody); + } else { + errorResponse.setStatusCode(response.code()); + errorResponse.setMessage(StatusCode.switchStatusCodeToJson(response.code())); + } + + } else { + errorResponse.setStatusCode(response.code()); + errorResponse.setMessage(StatusCode.switchStatusCodeToJson(response.code())); + } + + return errorResponse; + } + + public static ErrorResponse createResponse(Throwable t) { + ErrorResponse errorResponse; + + if (t instanceof UnknownHostException) { + errorResponse = new ErrorResponse(2, "Erro: " + t.getMessage() + "\n\nContate o administrador."); + } else if (t.getCause() instanceof ConnectException) { + errorResponse = new ErrorResponse(1, "Erro: " + t.getMessage() + "\n\nVerifique sua conexão com a internet."); + } else if (t.getMessage() == null) { + errorResponse = new ErrorResponse(0, "Erro: " + t.toString() + "\n\nContate o administrador."); + } else { + errorResponse = new ErrorResponse(0, "Erro: " + t.getMessage() + "\n\nContate o administrador."); + } + + return errorResponse; + } +} diff --git a/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/data/http/helpers/StatusCode.java b/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/data/http/helpers/StatusCode.java new file mode 100644 index 000000000..3b06ff890 --- /dev/null +++ b/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/data/http/helpers/StatusCode.java @@ -0,0 +1,231 @@ +package com.accenture.android.app.testeandroid.data.http.helpers; + +/** + * Created by Denis Magno on 09/07/2020. + * denis_magno16@hotmail.com + */ +public class StatusCode { + public static String switchStatusCodeToJson(int statusCode) { + String message = "Erro: Entre em contato com os administradores do sistema."; + + if (statusCode >= 100 && statusCode < 200) { + String title = "Resposta de informação"; + String description = "Tente novamente mais tarde."; + + if (statusCode == StatusCodeEnum.Continue.value) { + message = title + " (Continue): " + description; + } else if (statusCode == StatusCodeEnum.SwitchingProtocols.value) { + message = title + " (SwitchingProtocols): " + description; + } else if (statusCode == StatusCodeEnum.EarlyHints.value) { + message = title + " (EarlyHints): " + description; + } else { + message = title + ": " + description; + } + } + + if (statusCode >= 200 && statusCode < 300) { + String title = "Sucesso"; + + if (statusCode == StatusCodeEnum.OK.value) { + message = title + " (OK)."; + } else if (statusCode == StatusCodeEnum.Created.value) { + message = title + " (Created)."; + } else if (statusCode == StatusCodeEnum.Accepted.value) { + message = title + " (Accepted)."; + } else if (statusCode == StatusCodeEnum.NonAuthoritativeInformation.value) { + message = title + " (Non Authoritative Information)."; + } else if (statusCode == StatusCodeEnum.NoContent.value) { + message = title + " (No Content)."; + } else if (statusCode == StatusCodeEnum.ResetContent.value) { + message = title + " (Reset Content)."; + } else if (statusCode == StatusCodeEnum.PartialContent.value) { + message = title + " (Partial Content)."; + } else { + message = title + "."; + } + } + + if (statusCode >= 300 && statusCode < 400) { + String title = "Redirecionamento"; + + if (statusCode == StatusCodeEnum.MultipleChoices.value) { + message = title + " (Multiple Choices)."; + } else if (statusCode == StatusCodeEnum.MovedPermanently.value) { + message = title + " (Moved Permanently)."; + } else if (statusCode == StatusCodeEnum.Found.value) { + message = title + " (Found)."; + } else if (statusCode == StatusCodeEnum.SeeOther.value) { + message = title + " (See Other)."; + } else if (statusCode == StatusCodeEnum.NotModified.value) { + message = title + " (Not Modified)."; + } else if (statusCode == StatusCodeEnum.TemporaryRedirect.value) { + message = title + " (Temporary Redirect)."; + } else if (statusCode == StatusCodeEnum.PermanentRedirect.value) { + message = title + " (Permanent Redirect)."; + } else { + message = title + "."; + } + } + + if (statusCode >= 400 && statusCode < 500) { + String title = "Erro do cliente"; + String description = "Entre em contato com os administradores do sistema."; + + if (statusCode == StatusCodeEnum.BadRequest.value) { + message = title + " (Bad Request): " + description; + } else if (statusCode == StatusCodeEnum.Unauthorized.value) { + message = title + " (Unauthorized): " + description; + } else if (statusCode == StatusCodeEnum.PaymentRequired.value) { + message = title + " (Payment Required): " + description; + } else if (statusCode == StatusCodeEnum.Forbidden.value) { + message = title + " (Forbidden): " + description; + } else if (statusCode == StatusCodeEnum.NotFound.value) { + message = title + " (Not Found): " + description; + } else if (statusCode == StatusCodeEnum.MethodNotAllowed.value) { + message = title + " (Method Not Allowed): " + description; + } else if (statusCode == StatusCodeEnum.NotAcceptable.value) { + message = title + " (Not Acceptable): " + description; + } else if (statusCode == StatusCodeEnum.ProxyAuthenticationRequired.value) { + message = title + " (Proxy Authentication Required): " + description; + } else if (statusCode == StatusCodeEnum.RequestTimeout.value) { + message = title + " (Request Timeout): " + description; + } else if (statusCode == StatusCodeEnum.Conflict.value) { + message = title + " (Conflict): " + description; + } else if (statusCode == StatusCodeEnum.Gone.value) { + message = title + " (Gone): " + description; + } else if (statusCode == StatusCodeEnum.LengthRequired.value) { + message = title + " (Length Required): " + description; + } else if (statusCode == StatusCodeEnum.PreconditionFailed.value) { + message = title + " (Precondition Failed): " + description; + } else if (statusCode == StatusCodeEnum.PayloadTooLarge.value) { + message = title + " (Payload Too Large): " + description; + } else if (statusCode == StatusCodeEnum.URITooLong.value) { + message = title + " (URI Too Long): " + description; + } else if (statusCode == StatusCodeEnum.UnsupportedMediaType.value) { + message = title + " (Unsupported Media Type): " + description; + } else if (statusCode == StatusCodeEnum.RangeNotSatisfiable.value) { + message = title + " (Range Not Satisfiable): " + description; + } else if (statusCode == StatusCodeEnum.ExpectationFailed.value) { + message = title + " (Expectation Failed): " + description; + } else if (statusCode == StatusCodeEnum.IAmATeapot.value) { + message = title + " (I'm a Teapot): " + description; + } else if (statusCode == StatusCodeEnum.UnprocessableEntity.value) { + message = title + " (Unprocessable Entity): " + description; + } else if (statusCode == StatusCodeEnum.TooEarly.value) { + message = title + " (Too Early): " + description; + } else if (statusCode == StatusCodeEnum.UpgradeRequired.value) { + message = title + " (Upgrade Required): " + description; + } else if (statusCode == StatusCodeEnum.PreconditionRequired.value) { + message = title + " (Precondition Required): " + description; + } else if (statusCode == StatusCodeEnum.TooManyRequests.value) { + message = title + " (Too Many Requests): " + description; + } else if (statusCode == StatusCodeEnum.RequestHeaderFieldsTooLarge.value) { + message = title + " (Request Header Fields Too Large): " + description; + } else if (statusCode == StatusCodeEnum.UnavailableForLegalReasons.value) { + message = title + " (Unavailable For Legal Reasons): " + description; + } else { + message = title + ": " + description; + } + } + + if (statusCode >= 500) { + String title = "Erro do servidor"; + String description = "Tente novamente mais tarde."; + + if (statusCode == StatusCodeEnum.InternalServerError.value) { + message = title + " (Internal Server Error): " + description; + } else if (statusCode == StatusCodeEnum.NotImplemented.value) { + message = title + " (Not Implemented): " + description; + } else if (statusCode == StatusCodeEnum.BadGateway.value) { + message = title + " (Bad Gateway): " + description; + } else if (statusCode == StatusCodeEnum.ServiceUnavailable.value) { + message = title + " (Service Unavailable): " + description; + } else if (statusCode == StatusCodeEnum.GatewayTimeout.value) { + message = title + " (Gateway Timeout): " + description; + } else if (statusCode == StatusCodeEnum.HTTPVersionNotSupported.value) { + message = title + " (HTTP Version Not Supported): " + description; + } else if (statusCode == StatusCodeEnum.VariantAlsoNegotiates.value) { + message = title + " (Variant Also Negotiates): " + description; + } else if (statusCode == StatusCodeEnum.InsufficientStorage.value) { + message = title + " (Insufficient Storage): " + description; + } else if (statusCode == StatusCodeEnum.LoopDetected.value) { + message = title + " (Loop Detected): " + description; + } else if (statusCode == StatusCodeEnum.NotExtended.value) { + message = title + " (Not Extended): " + description; + } else if (statusCode == StatusCodeEnum.NetworkAuthenticationRequired.value) { + message = title + " (Network Authentication Required): " + description; + } else { + message = title + ": " + description; + } + } + + return message; + } + + public enum StatusCodeEnum { + Continue(100), + SwitchingProtocols(101), + EarlyHints(103), + + OK(200), + Created(201), + Accepted(202), + NonAuthoritativeInformation(203), + NoContent(204), + ResetContent(205), + PartialContent(206), + + MultipleChoices(300), + MovedPermanently(301), + Found(302), + SeeOther(303), + NotModified(304), + TemporaryRedirect(307), + PermanentRedirect(308), + + BadRequest(400), + Unauthorized(401), + PaymentRequired(402), + Forbidden(403), + NotFound(404), + MethodNotAllowed(405), + NotAcceptable(406), + ProxyAuthenticationRequired(407), + RequestTimeout(408), + Conflict(409), + Gone(410), + LengthRequired(411), + PreconditionFailed(412), + PayloadTooLarge(413), + URITooLong(414), + UnsupportedMediaType(415), + RangeNotSatisfiable(416), + ExpectationFailed(417), + IAmATeapot(418), + UnprocessableEntity(422), + TooEarly(425), + UpgradeRequired(426), + PreconditionRequired(428), + TooManyRequests(429), + RequestHeaderFieldsTooLarge(431), + UnavailableForLegalReasons(451), + + InternalServerError(500), + NotImplemented(501), + BadGateway(502), + ServiceUnavailable(503), + GatewayTimeout(504), + HTTPVersionNotSupported(505), + VariantAlsoNegotiates(506), + InsufficientStorage(507), + LoopDetected(508), + NotExtended(510), + NetworkAuthenticationRequired(511); + + public int value; + + StatusCodeEnum(int value) { + this.value = value; + } + } +} diff --git a/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/data/http/providers/StatementProvider.java b/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/data/http/providers/StatementProvider.java new file mode 100644 index 000000000..e12cb668f --- /dev/null +++ b/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/data/http/providers/StatementProvider.java @@ -0,0 +1,78 @@ +package com.accenture.android.app.testeandroid.data.http.providers; + +import android.util.Log; + +import com.accenture.android.app.testeandroid.data.converters.StatementConverter; +import com.accenture.android.app.testeandroid.data.http.helpers.StatusCode; +import com.accenture.android.app.testeandroid.data.http.providers.generics.BaseProvider; +import com.accenture.android.app.testeandroid.data.http.resources.StatementResource; +import com.accenture.android.app.testeandroid.data.http.responses.StatementResponse; +import com.accenture.android.app.testeandroid.data.http.responses.generics.ErrorResponse; +import com.accenture.android.app.testeandroid.domain.Statement; + +import java.util.ArrayList; +import java.util.List; + +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; + +/** + * Created by Denis Magno on 09/07/2020. + * denis_magno16@hotmail.com + */ +public class StatementProvider extends BaseProvider { + private StatementResource resource; + + private Call callBuscarStatements; + + public StatementProvider(String baseUrl) { + super(baseUrl); + + this.resource = this.retrofit.getStatementResource(); + } + + @Override + public void finish() { + if (this.callBuscarStatements != null) { + this.callBuscarStatements.cancel(); + } + } + + public void buscarStatements(final ExpectedResponseStatements callback, Long userId) { + this.callBuscarStatements = this.resource.getStatements(userId); + this.callBuscarStatements.enqueue(new Callback() { + @Override + public void onResponse(Call call, Response response) { + if (response.code() == StatusCode.StatusCodeEnum.OK.value) { + ErrorResponse error = response.body().getError(); + + if (error.getStatusCode() == null) { + ArrayList statements = StatementConverter.toDomain(new ArrayList<>(response.body().getData())); + + callback.onSuccess("Busca efetuada com sucesso.", statements); + } else { + callback.onFailure(error); + } + } else { + callback.onFailure(new ErrorResponse(response.code(), response.message())); + } + } + + @Override + public void onFailure(Call call, Throwable t) { + if (call.isCanceled()) { + Log.e(TAG, "buscarStatements: Requisição cancelada."); + } else { + callback.onFailure(new ErrorResponse(0, t.getMessage())); + } + } + }); + } + + public interface ExpectedResponseStatements { + void onSuccess(String message, List statements); + + void onFailure(ErrorResponse error); + } +} diff --git a/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/data/http/providers/UserAccountProvider.java b/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/data/http/providers/UserAccountProvider.java new file mode 100644 index 000000000..cdebeea17 --- /dev/null +++ b/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/data/http/providers/UserAccountProvider.java @@ -0,0 +1,75 @@ +package com.accenture.android.app.testeandroid.data.http.providers; + +import android.util.Log; + +import com.accenture.android.app.testeandroid.data.converters.UserAccountConverter; +import com.accenture.android.app.testeandroid.data.http.helpers.StatusCode; +import com.accenture.android.app.testeandroid.data.http.providers.generics.BaseProvider; +import com.accenture.android.app.testeandroid.data.http.resources.AuthResource; +import com.accenture.android.app.testeandroid.data.http.responses.UserAccountResponse; +import com.accenture.android.app.testeandroid.data.http.responses.generics.ErrorResponse; +import com.accenture.android.app.testeandroid.domain.UserAccount; + +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; + +/** + * Created by Denis Magno on 09/07/2020. + * denis_magno16@hotmail.com + */ +public class UserAccountProvider extends BaseProvider { + private AuthResource resource; + + private Call callLogin; + + public UserAccountProvider(String baseUrl) { + super(baseUrl); + + this.resource = this.retrofit.getAuthResource(); + } + + public void efetuarLogin(final ExpectedResponseLogin callback, String user, String password) { + this.callLogin = this.resource.login(user, password); + this.callLogin.enqueue(new Callback() { + @Override + public void onResponse(Call call, Response response) { + if (response.code() == StatusCode.StatusCodeEnum.OK.value) { + ErrorResponse error = response.body().getError(); + + if (error.getStatusCode() == null) { + UserAccount statements = UserAccountConverter.toDomain(response.body().getData()); + + callback.onSuccess("Busca efetuada com sucesso.", statements); + } else { + callback.onFailure(error); + } + } else { + callback.onFailure(new ErrorResponse(response.code(), response.message())); + } + } + + @Override + public void onFailure(Call call, Throwable t) { + if (call.isCanceled()) { + Log.e(TAG, "efetuarLogin: Requisição cancelada."); + } else { + callback.onFailure(new ErrorResponse(0, t.getMessage())); + } + } + }); + } + + @Override + public void finish() { + if (this.callLogin != null) { + this.callLogin.cancel(); + } + } + + public interface ExpectedResponseLogin { + void onSuccess(String message, UserAccount userAccount); + + void onFailure(ErrorResponse error); + } +} diff --git a/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/data/http/providers/generics/BaseProvider.java b/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/data/http/providers/generics/BaseProvider.java new file mode 100644 index 000000000..7d515d136 --- /dev/null +++ b/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/data/http/providers/generics/BaseProvider.java @@ -0,0 +1,19 @@ +package com.accenture.android.app.testeandroid.data.http.providers.generics; + +import com.accenture.android.app.testeandroid.data.http.RetrofitConfig; + +/** + * Created by Denis Magno on 09/07/2020. + * denis_magno16@hotmail.com + */ +public abstract class BaseProvider { + protected final String TAG = "CustomLog - " + this.getClass().getSimpleName(); + + protected final RetrofitConfig retrofit; + + public BaseProvider(String baseUrl) { + this.retrofit = new RetrofitConfig(baseUrl); + } + + public abstract void finish(); +} diff --git a/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/data/http/resources/AuthResource.java b/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/data/http/resources/AuthResource.java new file mode 100644 index 000000000..f1123a5dc --- /dev/null +++ b/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/data/http/resources/AuthResource.java @@ -0,0 +1,18 @@ +package com.accenture.android.app.testeandroid.data.http.resources; + +import com.accenture.android.app.testeandroid.data.http.responses.UserAccountResponse; + +import retrofit2.Call; +import retrofit2.http.Field; +import retrofit2.http.FormUrlEncoded; +import retrofit2.http.POST; + +/** + * Created by Denis Magno on 09/07/2020. + * denis_magno16@hotmail.com + */ +public interface AuthResource { + @FormUrlEncoded + @POST("login") + Call login(@Field("user") String user, @Field("password") String password); +} diff --git a/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/data/http/resources/StatementResource.java b/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/data/http/resources/StatementResource.java new file mode 100644 index 000000000..6cf3b3340 --- /dev/null +++ b/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/data/http/resources/StatementResource.java @@ -0,0 +1,16 @@ +package com.accenture.android.app.testeandroid.data.http.resources; + +import com.accenture.android.app.testeandroid.data.http.responses.StatementResponse; + +import retrofit2.Call; +import retrofit2.http.GET; +import retrofit2.http.Path; + +/** + * Created by Denis Magno on 09/07/2020. + * denis_magno16@hotmail.com + */ +public interface StatementResource { + @GET("statements/{userId}") + Call getStatements(@Path("userId") Long userId); +} diff --git a/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/data/http/responses/StatementResponse.java b/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/data/http/responses/StatementResponse.java new file mode 100644 index 000000000..6da8b468e --- /dev/null +++ b/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/data/http/responses/StatementResponse.java @@ -0,0 +1,57 @@ +package com.accenture.android.app.testeandroid.data.http.responses; + +import com.accenture.android.app.testeandroid.data.http.responses.generics.ErrorResponse; +import com.google.gson.annotations.SerializedName; + +import java.util.List; + +/** + * Created by Denis Magno on 09/07/2020. + * denis_magno16@hotmail.com + */ +public class StatementResponse { + @SerializedName("statementList") + private List data; + private ErrorResponse error; + + public static class StatementData { + private String title; + private String desc; + private String date; + private Double value; + + public StatementData() { + } + + public StatementData(String title, String desc, String date, Double value) { + this.title = title; + this.desc = desc; + this.date = date; + this.value = value; + } + + public String getTitle() { + return title; + } + + public String getDesc() { + return desc; + } + + public String getDate() { + return date; + } + + public Double getValue() { + return value; + } + } + + public List getData() { + return data; + } + + public ErrorResponse getError() { + return error; + } +} \ No newline at end of file diff --git a/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/data/http/responses/UserAccountResponse.java b/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/data/http/responses/UserAccountResponse.java new file mode 100644 index 000000000..6e14f5826 --- /dev/null +++ b/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/data/http/responses/UserAccountResponse.java @@ -0,0 +1,61 @@ +package com.accenture.android.app.testeandroid.data.http.responses; + +import com.accenture.android.app.testeandroid.data.http.responses.generics.ErrorResponse; +import com.google.gson.annotations.SerializedName; + +/** + * Created by Denis Magno on 09/07/2020. + * denis_magno16@hotmail.com + */ +public class UserAccountResponse { + @SerializedName("userAccount") + private UserAccountResponse.UserAccountData data; + private ErrorResponse error; + + public static class UserAccountData { + private Long userId; + private String name; + private String bankAccount; + private String agency; + private Double balance; + + public UserAccountData() { + } + + public UserAccountData(Long userId, String name, String bankAccount, String agency, Double balance) { + this.userId = userId; + this.name = name; + this.bankAccount = bankAccount; + this.agency = agency; + this.balance = balance; + } + + public Long getUserId() { + return userId; + } + + public String getName() { + return name; + } + + public String getBankAccount() { + return bankAccount; + } + + public String getAgency() { + return agency; + } + + public Double getBalance() { + return balance; + } + } + + public UserAccountData getData() { + return data; + } + + public ErrorResponse getError() { + return error; + } +} diff --git a/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/data/http/responses/generics/ErrorResponse.java b/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/data/http/responses/generics/ErrorResponse.java new file mode 100644 index 000000000..02e0aa3d1 --- /dev/null +++ b/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/data/http/responses/generics/ErrorResponse.java @@ -0,0 +1,49 @@ +package com.accenture.android.app.testeandroid.data.http.responses.generics; + +import org.json.JSONException; +import org.json.JSONObject; + +/** + * Created by Denis Magno on 09/07/2020. + * denis_magno16@hotmail.com + */ +public class ErrorResponse { + private Integer code; + private String message; + + public ErrorResponse() { + } + + public ErrorResponse(Integer code, String message) { + this.code = code; + this.message = message; + } + + public Integer getStatusCode() { + return code; + } + + public void setStatusCode(Integer code) { + this.code = code; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public String getMessageFormat() { + String error = ""; + try { + JSONObject json = new JSONObject(this.message); + error = json.getString("error"); + } catch (JSONException ex) { + ex.printStackTrace(); + } + + return error; + } +} diff --git a/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/domain/Statement.java b/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/domain/Statement.java new file mode 100644 index 000000000..bfed0e058 --- /dev/null +++ b/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/domain/Statement.java @@ -0,0 +1,119 @@ +package com.accenture.android.app.testeandroid.domain; + +import android.os.Parcel; +import android.os.Parcelable; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.Locale; + +/** + * Created by Denis Magno on 09/07/2020. + * denis_magno16@hotmail.com + */ +public class Statement implements Parcelable { + private String title; + private String desc; + private String date; + private Double value; + + public Statement() { + } + + protected Statement(Parcel in) { + title = in.readString(); + desc = in.readString(); + date = in.readString(); + if (in.readByte() == 0) { + value = null; + } else { + value = in.readDouble(); + } + } + + public static final Creator CREATOR = new Creator() { + @Override + public Statement createFromParcel(Parcel in) { + return new Statement(in); + } + + @Override + public Statement[] newArray(int size) { + return new Statement[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(title); + dest.writeString(desc); + dest.writeString(date); + if (value == null) { + dest.writeByte((byte) 0); + } else { + dest.writeByte((byte) 1); + dest.writeDouble(value); + } + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public String getDate() { + return date; + } + + public void setDate(String date) { + this.date = date; + } + + public Double getValue() { + return value; + } + + public void setValue(Double value) { + this.value = value; + } + + @Override + public String toString() { + return "Statement{" + + "title='" + title + '\'' + + ", desc='" + desc + '\'' + + ", date='" + date + '\'' + + ", value=" + value + + '}'; + } + public Calendar getDateCalendar() throws ParseException { + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()); + + Date date = format.parse(this.date); + + Calendar calendar = Calendar.getInstance(); + + calendar.setTime(date); + + return calendar; + } + +} diff --git a/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/domain/UserAccount.java b/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/domain/UserAccount.java new file mode 100644 index 000000000..9e8826ae9 --- /dev/null +++ b/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/domain/UserAccount.java @@ -0,0 +1,122 @@ +package com.accenture.android.app.testeandroid.domain; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Created by Denis Magno on 09/07/2020. + * denis_magno16@hotmail.com + */ +public class UserAccount implements Parcelable { + private Long userId; + private String name; + private String bankAccount; + private String agency; + private Double balance; + + public UserAccount() { + } + + protected UserAccount(Parcel in) { + if (in.readByte() == 0) { + userId = null; + } else { + userId = in.readLong(); + } + name = in.readString(); + bankAccount = in.readString(); + agency = in.readString(); + if (in.readByte() == 0) { + balance = null; + } else { + balance = in.readDouble(); + } + } + + public static final Creator CREATOR = new Creator() { + @Override + public UserAccount createFromParcel(Parcel in) { + return new UserAccount(in); + } + + @Override + public UserAccount[] newArray(int size) { + return new UserAccount[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + if (userId == null) { + dest.writeByte((byte) 0); + } else { + dest.writeByte((byte) 1); + dest.writeLong(userId); + } + dest.writeString(name); + dest.writeString(bankAccount); + dest.writeString(agency); + if (balance == null) { + dest.writeByte((byte) 0); + } else { + dest.writeByte((byte) 1); + dest.writeDouble(balance); + } + } + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getBankAccount() { + return bankAccount; + } + + public void setBankAccount(String bankAccount) { + this.bankAccount = bankAccount; + } + + public String getAgency() { + return agency; + } + + public void setAgency(String agency) { + this.agency = agency; + } + + public Double getBalance() { + return balance; + } + + public void setBalance(Double balance) { + this.balance = balance; + } + + @Override + public String toString() { + return "UserAccount{" + + "userId=" + userId + + ", name='" + name + '\'' + + ", bankAccount='" + bankAccount + '\'' + + ", agency='" + agency + '\'' + + ", balance=" + balance + + '}'; + } +} diff --git a/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/helpers/FieldHelper.java b/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/helpers/FieldHelper.java new file mode 100644 index 000000000..fff237c94 --- /dev/null +++ b/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/helpers/FieldHelper.java @@ -0,0 +1,76 @@ +package com.accenture.android.app.testeandroid.helpers; + +import java.util.InputMismatchException; + +/** + * Created by Denis Magno on 10/07/2020. + * denis_magno16@hotmail.com + */ +public class FieldHelper { + public static boolean isCPF(String CPF) { + // Retira qualquer caracter que foi digitado pelo usuário é válido em um CPF + CPF = CPF.replaceAll("[.]", "").replaceAll("[-]", "") + .replaceAll("[ ]", ""); + + // considera-se erro CPF's formados por uma sequencia de numeros iguais + if (CPF.equals("00000000000") || + CPF.equals("11111111111") || + CPF.equals("22222222222") || CPF.equals("33333333333") || + CPF.equals("44444444444") || CPF.equals("55555555555") || + CPF.equals("66666666666") || CPF.equals("77777777777") || + CPF.equals("88888888888") || CPF.equals("99999999999") || + (CPF.length() != 11)) + return (false); + + char dig10, dig11; + int sm, i, r, num, peso; + + try { + // Calculo do 1º. Digito Verificador + sm = 0; + peso = 10; + for (i = 0; i < 9; i++) { + // converte o i-esimo caractere do CPF em um numero: + // por exemplo, transforma o caractere '0' no inteiro 0 + // (48 eh a posicao de '0' na tabela ASCII) + num = (int) (CPF.charAt(i) - 48); + sm = sm + (num * peso); + peso = peso - 1; + } + + r = 11 - (sm % 11); + if ((r == 10) || (r == 11)) + dig10 = '0'; + else dig10 = (char) (r + 48); // converte no respectivo caractere numerico + + // Calculo do 2º. Digito Verificador + sm = 0; + peso = 11; + for (i = 0; i < 10; i++) { + num = (int) (CPF.charAt(i) - 48); + sm = sm + (num * peso); + peso = peso - 1; + } + + r = 11 - (sm % 11); + if ((r == 10) || (r == 11)) + dig11 = '0'; + else dig11 = (char) (r + 48); + + // Verifica se os digitos calculados conferem com os digitos informados. + if ((dig10 == CPF.charAt(9)) && (dig11 == CPF.charAt(10))) + return (true); + else return (false); + } catch (InputMismatchException erro) { + return (false); + } + } + + public static boolean isEmail(String email) { + return email.matches("([a-z0-9-._]+)@([a-z]+).([a-z]+).([a-z]+)"); + } + + public static boolean isPassword(String password) { + return password.matches("^(?=.*?[A-Z])(?=(.*[\\d]){1,})(?=(.*[\\W]){1,})(?!.*\\s).{3,}$"); + } +} diff --git a/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/helpers/FormatHelper.java b/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/helpers/FormatHelper.java new file mode 100644 index 000000000..8ffd1a0ed --- /dev/null +++ b/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/helpers/FormatHelper.java @@ -0,0 +1,48 @@ +package com.accenture.android.app.testeandroid.helpers; + +import java.text.NumberFormat; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Locale; + +/** + * Created by Denis Magno on 10/07/2020. + * denis_magno16@hotmail.com + */ +public class FormatHelper { + public static String formatarReal(Double valor) { + String formated = NumberFormat.getCurrencyInstance(new Locale("pt", "BR")).format(valor); + + // Verifica se número formatado está entre parenteses + // Se sim, número é negativo, então formata o número para retirar os parenteses e + // colocar o '-' na frente. + if (formated.contains("(")) { + formated = formated.replaceAll("[()]", ""); + + formated = "- " + formated; + } + + return formated; + } + + public static String formatarAgenciaBanco(String agencia) { + if (agencia.length() < 9) { + int numerosFaltantes = 9 - agencia.length(); + + StringBuilder agenciaBuilder = new StringBuilder(agencia); + for (int i = 0; i < numerosFaltantes; i++) { + agenciaBuilder.insert(0, "0"); + } + agencia = agenciaBuilder.toString(); + } + return agencia.substring(0, 2) + "." + agencia.substring(2, agencia.length() - 1) + "-" + agencia.substring(agencia.length() - 1); + } + + public static String formatarData(Calendar data) { + Integer dia = data.get(Calendar.DAY_OF_MONTH); + String mes = new SimpleDateFormat("MM", Locale.getDefault()).format(data.getTime()); + Integer ano = data.get(Calendar.YEAR); + + return String.format(Locale.getDefault(), "%d/%s/%d", dia, mes, ano); + } +} diff --git a/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/presentation/BasePresenter.java b/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/presentation/BasePresenter.java new file mode 100644 index 000000000..06899cc82 --- /dev/null +++ b/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/presentation/BasePresenter.java @@ -0,0 +1,10 @@ +package com.accenture.android.app.testeandroid.presentation; + +import androidx.lifecycle.LifecycleObserver; + +/** + * Created by Denis Magno on 09/07/2020. + * denis_magno16@hotmail.com + */ +public interface BasePresenter extends LifecycleObserver { +} \ No newline at end of file diff --git a/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/presentation/BaseView.java b/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/presentation/BaseView.java new file mode 100644 index 000000000..13719bc8a --- /dev/null +++ b/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/presentation/BaseView.java @@ -0,0 +1,8 @@ +package com.accenture.android.app.testeandroid.presentation; + +/** + * Created by Denis Magno on 09/07/2020. + * denis_magno16@hotmail.com + */ +public interface BaseView { +} \ No newline at end of file diff --git a/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/presentation/login/LoginActivity.java b/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/presentation/login/LoginActivity.java new file mode 100644 index 000000000..7a61bc289 --- /dev/null +++ b/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/presentation/login/LoginActivity.java @@ -0,0 +1,147 @@ +package com.accenture.android.app.testeandroid.presentation.login; + +import android.content.Intent; +import android.os.Bundle; +import android.view.View; + +import androidx.appcompat.app.AppCompatActivity; + +import com.accenture.android.app.testeandroid.databinding.ActivityLoginBinding; +import com.accenture.android.app.testeandroid.helpers.FieldHelper; +import com.accenture.android.app.testeandroid.presentation.main.MainActivity; +import com.google.android.material.snackbar.Snackbar; + +/** + * Created by Denis Magno on 09/07/2020. + * denis_magno16@hotmail.com + */ +public class LoginActivity extends AppCompatActivity implements LoginContract.View { + private final static String TAG = "CustomLog - " + LoginActivity.class.getSimpleName(); + + private ActivityLoginBinding binding; + + private LoginContract.Presenter presenter; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + this.binding = ActivityLoginBinding.inflate(getLayoutInflater()); + + this.presenter = new LoginPresenter(this, this.getLifecycle(), this); + + setContentView(this.binding.getRoot()); + } + + @Override + protected void onStart() { + super.onStart(); + + this.initEvents(); + } + + private void initEvents() { + this.binding.btnLogar.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (validateFields()) { + String user = binding.edtUser.getText().toString(); + String password = binding.edtPassword.getText().toString(); + + presenter.efetuarLogin(user, password); + } else { + setFeedback("Revise os campos!"); + } + } + }); + } + + @Override + public void setLoading() { + this.binding.pgbLoading.setVisibility(View.VISIBLE); + } + + @Override + public void unsetLoading() { + this.binding.pgbLoading.setVisibility(View.INVISIBLE); + } + + @Override + public void setContent() { + this.binding.btnLogar.setEnabled(true); + } + + @Override + public void unsetContent() { + this.binding.btnLogar.setEnabled(false); + } + + @Override + public void setFeedback(String message) { + Snackbar.make(this.binding.getRoot(), message, Snackbar.LENGTH_LONG) + .show(); + } + + @Override + public void setError(String message) { + this.binding.txtFeedbackError.setVisibility(View.VISIBLE); + this.binding.txtFeedbackError.setText(message); + } + + @Override + public void unsetError() { + this.binding.txtFeedbackError.setVisibility(View.GONE); + this.binding.txtFeedbackError.setText(""); + } + + @Override + public void setLoginRecente(String name) { + this.binding.lltRecentLoginContainer.setVisibility(View.VISIBLE); + this.binding.txtLoginRecente.setText(name); + } + + @Override + public void navigateToMainActivity() { + Intent intent = new Intent(LoginActivity.this, MainActivity.class); + + startActivity(intent); + + this.finish(); + } + + private boolean validateFields() { + boolean filled = true; + + // Valida campo "User" + this.binding.tilUser.setErrorEnabled(false); + if (this.binding.edtUser.getText().toString().isEmpty()) { + this.binding.tilUser.setError("Campo obrigatório!"); + filled = false; + } else if (this.binding.edtUser.getText().toString().contains("@")) { + if (!FieldHelper.isEmail(this.binding.edtUser.getText().toString())) { + this.binding.tilUser.setError("CPF ou e-mail inválido!"); + filled = false; + } + } else if (!FieldHelper.isCPF(this.binding.edtUser.getText().toString())) { + this.binding.tilUser.setError("CPF ou e-mail inválido!"); + filled = false; + } + + this.unsetError(); + // Valida campo "Password" + this.binding.tilPassword.setErrorEnabled(false); + if (this.binding.edtPassword.getText().toString().isEmpty()) { + this.binding.tilPassword.setError("Campo obrigatório!"); + filled = false; + } else if (this.binding.edtPassword.getText().length() < 3) { + this.binding.tilPassword.setError("Campo inválido!"); + filled = false; + } else if (!FieldHelper.isPassword(this.binding.edtPassword.getText().toString())) { + this.binding.tilPassword.setError("Campo inválido!"); + this.setError("A senha deve conter pelo menos uma letra maiúscula, um caracter especial e um caracter alfanumérico."); + + filled = false; + } + + return filled; + } +} diff --git a/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/presentation/login/LoginContract.java b/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/presentation/login/LoginContract.java new file mode 100644 index 000000000..65bfa05ad --- /dev/null +++ b/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/presentation/login/LoginContract.java @@ -0,0 +1,34 @@ +package com.accenture.android.app.testeandroid.presentation.login; + +import com.accenture.android.app.testeandroid.presentation.BasePresenter; +import com.accenture.android.app.testeandroid.presentation.BaseView; + +/** + * Created by Denis Magno on 09/07/2020. + * denis_magno16@hotmail.com + */ +interface LoginContract { + interface Presenter extends BasePresenter { + void efetuarLogin(String user, String password); + } + + interface View extends BaseView { + void setLoading(); + + void unsetLoading(); + + void setContent(); + + void unsetContent(); + + void setFeedback(String message); + + void setError(String message); + + void unsetError(); + + void setLoginRecente(String name); + + void navigateToMainActivity(); + } +} diff --git a/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/presentation/login/LoginPresenter.java b/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/presentation/login/LoginPresenter.java new file mode 100644 index 000000000..481a23118 --- /dev/null +++ b/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/presentation/login/LoginPresenter.java @@ -0,0 +1,104 @@ +package com.accenture.android.app.testeandroid.presentation.login; + +import android.content.Context; +import android.util.Log; + +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.OnLifecycleEvent; + +import com.accenture.android.app.testeandroid.data.http.providers.UserAccountProvider; +import com.accenture.android.app.testeandroid.data.http.responses.generics.ErrorResponse; +import com.accenture.android.app.testeandroid.domain.UserAccount; +import com.accenture.android.app.testeandroid.utils.AuthManager; + +import java.util.Locale; + +/** + * Created by Denis Magno on 09/07/2020. + * denis_magno16@hotmail.com + */ +class LoginPresenter implements LoginContract.Presenter { + private final static String TAG = "CustomLog - " + LoginPresenter.class.getSimpleName(); + + private LoginContract.View view; + private Context context; + + private AuthManager authManager; + + private UserAccountProvider userAccountProvider; + private UserAccountProvider.ExpectedResponseLogin userAccountCallback; + + LoginPresenter(Context context, Lifecycle lifecycle, LoginContract.View view) { + this.context = context; + this.view = view; + lifecycle.addObserver(this); + } + + @OnLifecycleEvent(Lifecycle.Event.ON_START) + private void onStart() { + this.initEvents(); + + this.initAuthManager(); + this.initProviders(); + } + + @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) + private void onResume() { + this.buscarUsuarioLogadoRecentemente(); + } + + @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) + private void onPause() { + this.userAccountProvider.finish(); + } + + private void initEvents() { + this.userAccountCallback = new UserAccountProvider.ExpectedResponseLogin() { + @Override + public void onSuccess(String message, UserAccount userAccount) { + view.unsetLoading(); + view.setContent(); + + authManager.setUserAccount(userAccount); + + view.navigateToMainActivity(); + } + + @Override + public void onFailure(ErrorResponse error) { + view.unsetLoading(); + view.setContent(); + + authManager.clearUserAccount(); + + view.setFeedback(String.format(Locale.getDefault(), "%d: %s", error.getStatusCode(), error.getMessage())); + } + }; + } + + private void initAuthManager() { + this.authManager = new AuthManager(this.context); + } + + private void initProviders() { + String url = "https://bank-app-test.herokuapp.com/api/"; + + this.userAccountProvider = new UserAccountProvider(url); + } + + @Override + public void efetuarLogin(String user, String password) { + this.view.unsetContent(); + this.view.setLoading(); + + this.userAccountProvider.efetuarLogin(this.userAccountCallback, user, password); + } + + private void buscarUsuarioLogadoRecentemente() { + UserAccount userAccount = this.authManager.getUserAccount(); + + if (userAccount.getUserId() != null) { + this.view.setLoginRecente(userAccount.getName()); + } + } +} diff --git a/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/presentation/main/MainActivity.java b/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/presentation/main/MainActivity.java new file mode 100644 index 000000000..c657b9e38 --- /dev/null +++ b/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/presentation/main/MainActivity.java @@ -0,0 +1,145 @@ +package com.accenture.android.app.testeandroid.presentation.main; + +import androidx.appcompat.app.AppCompatActivity; +import androidx.recyclerview.widget.DividerItemDecoration; +import androidx.recyclerview.widget.LinearLayoutManager; + +import android.content.Intent; +import android.os.Bundle; +import android.view.View; + +import com.accenture.android.app.testeandroid.R; +import com.accenture.android.app.testeandroid.databinding.ActivityMainBinding; +import com.accenture.android.app.testeandroid.domain.Statement; +import com.accenture.android.app.testeandroid.domain.UserAccount; +import com.accenture.android.app.testeandroid.helpers.FormatHelper; +import com.accenture.android.app.testeandroid.presentation.login.LoginActivity; +import com.accenture.android.app.testeandroid.presentation.main.adapters.StatementsRecyclerAdapter; +import com.google.android.material.snackbar.Snackbar; + +import java.util.ArrayList; + +/** + * Created by Denis Magno on 09/07/2020. + * denis_magno16@hotmail.com + */ +public class MainActivity extends AppCompatActivity implements MainContract.View { + private final static String TAG = "CustomLog - " + MainActivity.class.getSimpleName(); + + private ActivityMainBinding binding; + + private MainContract.Presenter presenter; + + private StatementsRecyclerAdapter statementsRecyclerAdapter; + private ArrayList statements = new ArrayList<>(); + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + this.binding = ActivityMainBinding.inflate(getLayoutInflater()); + + this.presenter = new MainPresenter(this, this.getLifecycle(), this); + + setContentView(this.binding.getRoot()); + } + + @Override + protected void onStart() { + super.onStart(); + + this.initEvents(); + this.configRecyclerView(); + } + + @Override + protected void onResume() { + super.onResume(); + + this.presenter.buscarStatements(1L); + } + + private void initEvents() { + this.binding.iclAppBar.btnLogout.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + presenter.efetuarLogout(); + } + }); + } + + private void configRecyclerView() { + LinearLayoutManager lltManager = new LinearLayoutManager(this); + this.binding.rcvStatements.setLayoutManager(lltManager); + + DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(this, lltManager.getOrientation()); + dividerItemDecoration.setDrawable(this.getResources().getDrawable(R.drawable.shape_divider_transparent)); + this.binding.rcvStatements.addItemDecoration(dividerItemDecoration); + + this.statementsRecyclerAdapter = new StatementsRecyclerAdapter(this, this.statements); + this.binding.rcvStatements.setAdapter(this.statementsRecyclerAdapter); + } + + @Override + public void setLoading() { + this.binding.pgbLoading.setVisibility(View.VISIBLE); + } + + @Override + public void unsetLoading() { + this.binding.pgbLoading.setVisibility(View.INVISIBLE); + } + + @Override + public void setContent() { + this.binding.txtTitulo.setVisibility(View.VISIBLE); + this.binding.rcvStatements.setVisibility(View.VISIBLE); + } + + @Override + public void unsetContent() { + this.binding.txtTitulo.setVisibility(View.INVISIBLE); + this.binding.rcvStatements.setVisibility(View.INVISIBLE); + } + + @Override + public void setFeedback(String message) { + Snackbar.make(this.binding.getRoot(), message, Snackbar.LENGTH_LONG) + .show(); + } + + @Override + public void setUserAccount(UserAccount userAccount) { + this.binding.iclAppBar.txtUserName.setText(userAccount.getName()); + this.binding.iclAppBar.txtConta.setText(getResources().getString(R.string.app_main_txt_text_conta, userAccount.getBankAccount(), FormatHelper.formatarAgenciaBanco(userAccount.getAgency()))); + } + + @Override + public void setStatements(ArrayList statements) { + this.statements.clear(); + + this.statements.addAll(statements); + + this.statementsRecyclerAdapter.notifyDataSetChanged(); + + this.binding.iclAppBar.txtSaldo.setText(FormatHelper.formatarReal(this.contarSaldo(statements))); + } + + @Override + public void navigateToLoginActivity() { + Intent intent = new Intent(MainActivity.this, LoginActivity.class); + + startActivity(intent); + + this.finish(); + } + + private Double contarSaldo(ArrayList statements) { + Double saldo = 0.0; + + for (Statement statement : statements) { + saldo = saldo + statement.getValue(); + } + + return saldo; + } +} \ No newline at end of file diff --git a/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/presentation/main/MainContract.java b/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/presentation/main/MainContract.java new file mode 100644 index 000000000..df93ef52c --- /dev/null +++ b/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/presentation/main/MainContract.java @@ -0,0 +1,38 @@ +package com.accenture.android.app.testeandroid.presentation.main; + +import com.accenture.android.app.testeandroid.domain.Statement; +import com.accenture.android.app.testeandroid.domain.UserAccount; +import com.accenture.android.app.testeandroid.presentation.BasePresenter; +import com.accenture.android.app.testeandroid.presentation.BaseView; + +import java.util.ArrayList; + +/** + * Created by Denis Magno on 09/07/2020. + * denis_magno16@hotmail.com + */ +interface MainContract { + interface Presenter extends BasePresenter { + void buscarStatements(Long userId); + + void efetuarLogout(); + } + + interface View extends BaseView { + void setLoading(); + + void unsetLoading(); + + void setContent(); + + void unsetContent(); + + void setFeedback(String message); + + void setUserAccount(UserAccount userAccount); + + void setStatements(ArrayList statements); + + void navigateToLoginActivity(); + } +} \ No newline at end of file diff --git a/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/presentation/main/MainPresenter.java b/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/presentation/main/MainPresenter.java new file mode 100644 index 000000000..351fe3dd8 --- /dev/null +++ b/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/presentation/main/MainPresenter.java @@ -0,0 +1,100 @@ +package com.accenture.android.app.testeandroid.presentation.main; + +import android.content.Context; + +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.OnLifecycleEvent; + +import com.accenture.android.app.testeandroid.data.http.providers.StatementProvider; +import com.accenture.android.app.testeandroid.data.http.responses.generics.ErrorResponse; +import com.accenture.android.app.testeandroid.domain.Statement; +import com.accenture.android.app.testeandroid.utils.AuthManager; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +/** + * Created by Denis Magno on 09/07/2020. + * denis_magno16@hotmail.com + */ +class MainPresenter implements MainContract.Presenter { + private final static String TAG = "CustomLog - " + MainPresenter.class.getSimpleName(); + + private MainContract.View view; + private Context context; + + private AuthManager authManager; + + private StatementProvider statementProvider; + private StatementProvider.ExpectedResponseStatements statementsCallback; + + MainPresenter(Context context, Lifecycle lifecycle, MainContract.View view) { + this.context = context; + this.view = view; + lifecycle.addObserver(this); + } + + @OnLifecycleEvent(Lifecycle.Event.ON_START) + private void onStart() { + this.initEvents(); + + this.initAuthManager(); + this.initProviders(); + } + + @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) + private void onResume() { + this.view.setUserAccount(this.authManager.getUserAccount()); + } + + @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) + private void onPause() { + this.statementProvider.finish(); + } + + private void initEvents() { + this.statementsCallback = new StatementProvider.ExpectedResponseStatements() { + @Override + public void onSuccess(String message, List statements) { + view.unsetLoading(); + view.setContent(); + + view.setStatements(new ArrayList<>(statements)); + } + + @Override + public void onFailure(ErrorResponse error) { + view.unsetLoading(); + view.setContent(); + + view.setFeedback(String.format(Locale.getDefault(), "%d: %s", error.getStatusCode(), error.getMessage())); + } + }; + } + + private void initAuthManager() { + this.authManager = new AuthManager(this.context); + } + + private void initProviders() { + String url = "https://bank-app-test.herokuapp.com/api/"; + + this.statementProvider = new StatementProvider(url); + } + + @Override + public void buscarStatements(Long userId) { + this.view.setLoading(); + this.view.unsetContent(); + + this.statementProvider.buscarStatements(this.statementsCallback, userId); + } + + @Override + public void efetuarLogout() { + this.authManager.clearUserAccount(); + + this.view.navigateToLoginActivity(); + } +} \ No newline at end of file diff --git a/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/presentation/main/adapters/StatementsRecyclerAdapter.java b/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/presentation/main/adapters/StatementsRecyclerAdapter.java new file mode 100644 index 000000000..82fc06b2a --- /dev/null +++ b/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/presentation/main/adapters/StatementsRecyclerAdapter.java @@ -0,0 +1,83 @@ +package com.accenture.android.app.testeandroid.presentation.main.adapters; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +import com.accenture.android.app.testeandroid.databinding.ItemRecyclerStatementBinding; +import com.accenture.android.app.testeandroid.domain.Statement; +import com.accenture.android.app.testeandroid.helpers.FormatHelper; + +import java.text.ParseException; +import java.util.ArrayList; + +/** + * Created by Denis Magno on 09/07/2020. + * denis_magno16@hotmail.com + */ +public class StatementsRecyclerAdapter extends RecyclerView.Adapter { + private final static String TAG = "CustomLog - " + StatementsRecyclerAdapter.class.getSimpleName(); + + private final Context context; + + private ArrayList statements; + + public StatementsRecyclerAdapter(Context context, ArrayList statements) { + this.context = context; + this.statements = statements; + } + + @NonNull + @Override + public StatementsRecyclerAdapter.StatementViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + LayoutInflater layoutInflater = (LayoutInflater) this.context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + ItemRecyclerStatementBinding binding = ItemRecyclerStatementBinding.inflate(layoutInflater, parent, false); + + return new StatementViewHolder(this.context, binding); + } + + @Override + public void onBindViewHolder(@NonNull StatementsRecyclerAdapter.StatementViewHolder holder, int position) { + holder.bindStatement(this.statements.get(position)); + } + + @Override + public int getItemCount() { + return this.statements.size(); + } + + @Override + public long getItemId(int position) { + return 0; + } + + public static class StatementViewHolder extends RecyclerView.ViewHolder { + private final static String TAG = "CustomLog - " + StatementViewHolder.class.getSimpleName(); + + private Context context; + private ItemRecyclerStatementBinding binding; + + public StatementViewHolder(Context context, ItemRecyclerStatementBinding binding) { + super(binding.getRoot()); + + this.context = context; + this.binding = binding; + } + + public void bindStatement(Statement statement) { + this.binding.txtTitulo.setText(statement.getTitle()); + this.binding.txtDescricao.setText(statement.getDesc()); + + try { + this.binding.txtData.setText(FormatHelper.formatarData(statement.getDateCalendar())); + } catch (ParseException e) { + e.printStackTrace(); + } + + this.binding.txtValor.setText(FormatHelper.formatarReal(statement.getValue())); + } + } +} diff --git a/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/utils/AuthManager.java b/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/utils/AuthManager.java new file mode 100644 index 000000000..84fa92270 --- /dev/null +++ b/TesteAndroid/app/src/main/java/com/accenture/android/app/testeandroid/utils/AuthManager.java @@ -0,0 +1,54 @@ +package com.accenture.android.app.testeandroid.utils; + +import android.content.Context; +import android.content.SharedPreferences; + +import com.accenture.android.app.testeandroid.domain.UserAccount; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * Created by Denis Magno on 09/07/2020. + * denis_magno16@hotmail.com + */ +public class AuthManager { + private static final String USER_AUTH_KEY = "USER_AUTH_KEY_JWT"; + private static final String USER_AUTH_OBJECT = "USER_AUTH_OBJECT"; + private final SharedPreferences sharedPreferences; + private final SharedPreferences.Editor editor; + + public AuthManager(Context context) { + this.sharedPreferences = context.getSharedPreferences(USER_AUTH_KEY, 0); + this.editor = this.sharedPreferences.edit(); + } + + public void setUserAccount(UserAccount userAccount) { + try { + ObjectMapper mapper = new ObjectMapper(); + String json = mapper.writeValueAsString(userAccount); + this.editor.putString(USER_AUTH_OBJECT, json); + this.editor.apply(); + } catch (JsonProcessingException var5) { + var5.printStackTrace(); + } + } + + public UserAccount getUserAccount() { + UserAccount userAccount = new UserAccount(); + + try { + ObjectMapper mapper = new ObjectMapper(); + String json = this.sharedPreferences.getString(USER_AUTH_OBJECT, new UserAccount().toString()); + userAccount = (UserAccount) mapper.readValue(json, UserAccount.class); + } catch (JsonProcessingException var4) { + var4.printStackTrace(); + } + + return userAccount; + } + + public void clearUserAccount() { + this.editor.remove(USER_AUTH_OBJECT); + this.editor.commit(); + } +} diff --git a/TesteAndroid/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/TesteAndroid/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 000000000..2b068d114 --- /dev/null +++ b/TesteAndroid/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/TesteAndroid/app/src/main/res/drawable/ic_launcher_background.xml b/TesteAndroid/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 000000000..07d5da9cb --- /dev/null +++ b/TesteAndroid/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/TesteAndroid/app/src/main/res/drawable/ic_logout.xml b/TesteAndroid/app/src/main/res/drawable/ic_logout.xml new file mode 100644 index 000000000..33ae72f7d --- /dev/null +++ b/TesteAndroid/app/src/main/res/drawable/ic_logout.xml @@ -0,0 +1,12 @@ + + + + diff --git a/TesteAndroid/app/src/main/res/drawable/logo.xml b/TesteAndroid/app/src/main/res/drawable/logo.xml new file mode 100644 index 000000000..f5711f8a4 --- /dev/null +++ b/TesteAndroid/app/src/main/res/drawable/logo.xml @@ -0,0 +1,9 @@ + + + diff --git a/TesteAndroid/app/src/main/res/drawable/shape_button_blue.xml b/TesteAndroid/app/src/main/res/drawable/shape_button_blue.xml new file mode 100644 index 000000000..96ad27bcc --- /dev/null +++ b/TesteAndroid/app/src/main/res/drawable/shape_button_blue.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/TesteAndroid/app/src/main/res/drawable/shape_button_transparent.xml b/TesteAndroid/app/src/main/res/drawable/shape_button_transparent.xml new file mode 100644 index 000000000..c451f3a55 --- /dev/null +++ b/TesteAndroid/app/src/main/res/drawable/shape_button_transparent.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/TesteAndroid/app/src/main/res/drawable/shape_divider_transparent.xml b/TesteAndroid/app/src/main/res/drawable/shape_divider_transparent.xml new file mode 100644 index 000000000..d3ea7d7ca --- /dev/null +++ b/TesteAndroid/app/src/main/res/drawable/shape_divider_transparent.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/TesteAndroid/app/src/main/res/drawable/shape_edit_text_gray_outline.xml b/TesteAndroid/app/src/main/res/drawable/shape_edit_text_gray_outline.xml new file mode 100644 index 000000000..4e1808693 --- /dev/null +++ b/TesteAndroid/app/src/main/res/drawable/shape_edit_text_gray_outline.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/TesteAndroid/app/src/main/res/font/helvetica_neue.ttf b/TesteAndroid/app/src/main/res/font/helvetica_neue.ttf new file mode 100644 index 000000000..cac3b75d8 Binary files /dev/null and b/TesteAndroid/app/src/main/res/font/helvetica_neue.ttf differ diff --git a/TesteAndroid/app/src/main/res/font/helvetica_neue_light.ttf b/TesteAndroid/app/src/main/res/font/helvetica_neue_light.ttf new file mode 100644 index 000000000..0bf4e61cc Binary files /dev/null and b/TesteAndroid/app/src/main/res/font/helvetica_neue_light.ttf differ diff --git a/TesteAndroid/app/src/main/res/layout/activity_login.xml b/TesteAndroid/app/src/main/res/layout/activity_login.xml new file mode 100644 index 000000000..0b5b1dbc0 --- /dev/null +++ b/TesteAndroid/app/src/main/res/layout/activity_login.xml @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +