diff --git a/.gitignore b/.gitignore
new file mode 100644
index 000000000..603b14077
--- /dev/null
+++ b/.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/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 000000000..208c0a5c4
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,50 @@
+image: openjdk:8-jdk
+
+variables:
+ ANDROID_COMPILE_SDK: "29"
+ ANDROID_BUILD_TOOLS: "29.3.0"
+ ANDROID_SDK_TOOLS: "6514223"
+
+before_script:
+
+ # installing packages
+ - apt-get --quiet update --yes
+ - apt-get --quiet install --yes wget tar unzip lib32stdc++6 lib32z1
+
+ # install android sdk
+ - wget --quiet --output-document=android-sdk.zip https://dl.google.com/android/repository/sdk-tools-linux-${ANDROID_SDK_TOOLS}.zip
+ - unzip -d android-sdk-linux android-sdk.zip
+ - echo y | android-sdk-linux/tools/bin/sdkmanager "platforms;android-${ANDROID_COMPILE_SDK}" >/dev/null
+ - echo y | android-sdk-linux/tools/bin/sdkmanager "platform-tools" >/dev/null
+ - echo y | android-sdk-linux/tools/bin/sdkmanager "build-tools;${ANDROID_BUILD_TOOLS}" >/dev/null
+
+ # setup environment
+ - export ANDROID_HOME=$PWD/android-sdk-linux
+ - export PATH=$PATH:$PWD/android-sdk-linux/platform-tools/
+ - chmod +x ./gradlew
+ # temporarily disable checking for EPIPE error and use yes to accept all licenses
+ - set +o pipefail
+ - yes | android-sdk-linux/tools/bin/sdkmanager --licenses
+ - set -o pipefail
+
+stages:
+ - build
+ - test
+
+lintDebug:
+ stage: build
+ script:
+ - ./gradlew -Pci --console=plain :app:lintDebug -PbuildDir=lint
+
+assembleDebug:
+ stage: build
+ script:
+ - ./gradlew assembleDebug
+ artifacts:
+ paths:
+ - app/build/outputs/
+
+debugTests:
+ stage: test
+ script:
+ - ./gradlew -Pci --console=plain :app:testDebug
diff --git a/.idea/$CACHE_FILE$ b/.idea/$CACHE_FILE$
new file mode 100644
index 000000000..ebf0f060e
--- /dev/null
+++ b/.idea/$CACHE_FILE$
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+ Groovy
+
+
+ Java
+
+
+ Potentially confusing code constructsGroovy
+
+
+ Threading issuesJava
+
+
+
+
+ Android
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/.name b/.idea/.name
new file mode 100644
index 000000000..866f33f0a
--- /dev/null
+++ b/.idea/.name
@@ -0,0 +1 @@
+Teste Santander
\ No newline at end of file
diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
new file mode 100644
index 000000000..88ea3aa1e
--- /dev/null
+++ b/.idea/codeStyles/Project.xml
@@ -0,0 +1,122 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 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/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml
new file mode 100644
index 000000000..79ee123c2
--- /dev/null
+++ b/.idea/codeStyles/codeStyleConfig.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
new file mode 100644
index 000000000..40ed93785
--- /dev/null
+++ b/.idea/compiler.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
new file mode 100644
index 000000000..97626ba45
--- /dev/null
+++ b/.idea/encodings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
new file mode 100644
index 000000000..8ae288524
--- /dev/null
+++ b/.idea/gradle.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml
new file mode 100644
index 000000000..a5f05cd8c
--- /dev/null
+++ b/.idea/jarRepositories.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 000000000..8a8f75bfe
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml
new file mode 100644
index 000000000..7f68460d8
--- /dev/null
+++ b/.idea/runConfigurations.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 000000000..6c0b86358
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Bank_App.postman_collection.json b/Bank_App.postman_collection.json
deleted file mode 100644
index 53882a1c6..000000000
--- a/Bank_App.postman_collection.json
+++ /dev/null
@@ -1,96 +0,0 @@
-{
- "info": {
- "_postman_id": "b90c97e1-4261-4a34-a348-a0604f0264a7",
- "name": "Bank App",
- "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
- },
- "item": [
- {
- "name": "Login",
- "request": {
- "method": "POST",
- "header": [
- {
- "key": "Content-Type",
- "name": "Content-Type",
- "value": "application/x-www-form-urlencoded",
- "type": "text"
- }
- ],
- "body": {
- "mode": "urlencoded",
- "urlencoded": [
- {
- "key": "user",
- "value": "test_user",
- "type": "text"
- },
- {
- "key": "password",
- "value": "Test@1",
- "type": "text"
- }
- ]
- },
- "url": {
- "raw": "https://bank-app-test.herokuapp.com/api/login",
- "protocol": "https",
- "host": [
- "bank-app-test",
- "herokuapp",
- "com"
- ],
- "path": [
- "api",
- "login"
- ]
- }
- },
- "response": []
- },
- {
- "name": "Statements",
- "request": {
- "method": "GET",
- "header": [
- {
- "key": "Content-Type",
- "name": "Content-Type",
- "value": "application/x-www-form-urlencoded",
- "type": "text"
- }
- ],
- "body": {
- "mode": "urlencoded",
- "urlencoded": [
- {
- "key": "user",
- "value": "4",
- "type": "text"
- },
- {
- "key": "password",
- "value": "asdfa",
- "type": "text"
- }
- ]
- },
- "url": {
- "raw": "https://bank-app-test.herokuapp.com/api/statements/1",
- "protocol": "https",
- "host": [
- "bank-app-test",
- "herokuapp",
- "com"
- ],
- "path": [
- "api",
- "statements",
- "1"
- ]
- }
- },
- "response": []
- }
- ]
-}
\ No newline at end of file
diff --git a/README.md b/README.md
deleted file mode 100644
index bd73feb5f..000000000
--- a/README.md
+++ /dev/null
@@ -1,41 +0,0 @@
-# Show me the code
-
-Esse repositório contem todo o material necessário para realizar o teste:
-- A especificação do layout está na pasta 'bank_app_layout' abrindo o index.html, utilizar os Styles do Android
-
-- Os dados da Api estão mockados, os exemplos e a especificação dos serviços (login e statements) se encontram no arquivo BankApp.postman_collection.json ( é necessário instalar o postman e importar a colection https://www.getpostman.com/apps)
-
-
-
-### # DESAFIO:
-
-Na primeira tela teremos um formulario de login, o campo user deve aceitar email ou cpf,
-o campo password deve validar se a senha tem pelo menos uma letra maiuscula, um caracter especial e um caracter alfanumérico.
-Apos a validação, realizar o login no endpoint https://bank-app-test.herokuapp.com/api/login e exibir os dados de retorno na próxima tela.
-O ultimo usuário logado deve ser salvo de forma segura localmente, e exibido na tela de login se houver algum salvo.
-
-Na segunda tela será exibido os dados formatados do retorno do login e será necessário fazer um segundo request para obter os lançamentos do usuário, no endpoint https://bank-app-test.herokuapp.com/api/statements/{idUser} que retornará uma lista de lançamentos
-
-### # Avaliação
-
-Você será avaliado pela usabilidade, por respeitar o design e pela arquitetura do app. É esperado que você consiga explicar as decisões que tomou durante o desenvolvimento através de commits.
-
-Obrigatórios:
-
-* Java ou Kotlin
-* Material Design
-* O app deve funcionar a partir do android 4.4
-* Testes unitários, pode usar a ferramenta que você tem mais experiência, só nos explique o que ele tem de bom.
-* Arquitetura a ser utilizada: Android Clean Code (https://github.com/kmmraj/android-clean-code && https://medium.com/@kmmraj/android-clean-code-part-1-c66da6551d1)
-* Uso do git.
-
-### # Observações gerais
-
-Adicione um arquivo [README.md](http://README.md) com os procedimentos para executar o projeto.
-Pedimos que trabalhe sozinho e não divulgue o resultado na internet.
-
-Faça um fork desse desse repositório em seu Github e ao finalizar nos envie um Pull Request com o resultado, por favor informe por qual empresa você esta se candidatando.
-
-# Importante: não há prazo de entrega, faça com qualidade!
-
-# BOA SORTE!
diff --git a/_config.yml b/_config.yml
deleted file mode 100644
index 2f7efbeab..000000000
--- a/_config.yml
+++ /dev/null
@@ -1 +0,0 @@
-theme: jekyll-theme-minimal
\ No newline at end of file
diff --git a/app/.gitignore b/app/.gitignore
new file mode 100644
index 000000000..42afabfd2
--- /dev/null
+++ b/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
new file mode 100644
index 000000000..0c6479d9b
--- /dev/null
+++ b/app/build.gradle
@@ -0,0 +1,52 @@
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
+
+android {
+ compileSdkVersion 29
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ defaultConfig {
+ applicationId "br.com.cauejannini.testesantander"
+ 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'
+ }
+ }
+
+ testOptions {
+ unitTests {
+ includeAndroidResources = true
+ }
+ }
+}
+
+dependencies {
+ implementation fileTree(dir: "libs", include: ["*.jar"])
+ implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
+ implementation 'androidx.core:core-ktx:1.3.0'
+ implementation 'androidx.appcompat:appcompat:1.1.0'
+ implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
+ implementation 'com.google.android.material:material:1.1.0'
+ testImplementation 'junit:junit:4.12'
+ androidTestImplementation 'androidx.test.ext:junit:1.1.1'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
+ implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.0.0'
+ testImplementation 'org.robolectric:robolectric:4.3'
+
+ implementation 'com.squareup.retrofit2:retrofit:2.8.1'
+ implementation 'com.squareup.retrofit2:converter-gson:2.8.1'
+}
\ No newline at end of file
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
new file mode 100644
index 000000000..481bb4348
--- /dev/null
+++ b/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/app/src/androidTest/java/br/com/cauejannini/testesantander/ExampleInstrumentedTest.kt b/app/src/androidTest/java/br/com/cauejannini/testesantander/ExampleInstrumentedTest.kt
new file mode 100644
index 000000000..cbb0b0bc0
--- /dev/null
+++ b/app/src/androidTest/java/br/com/cauejannini/testesantander/ExampleInstrumentedTest.kt
@@ -0,0 +1,24 @@
+package br.com.cauejannini.testesantander
+
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.ext.junit.runners.AndroidJUnit4
+
+import org.junit.Test
+import org.junit.runner.RunWith
+
+import org.junit.Assert.*
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+@RunWith(AndroidJUnit4::class)
+class ExampleInstrumentedTest {
+ @Test
+ fun useAppContext() {
+ // Context of the app under test.
+ val appContext = InstrumentationRegistry.getInstrumentation().targetContext
+ assertEquals("br.com.cauejannini.testesantander", appContext.packageName)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..4ab7c3c18
--- /dev/null
+++ b/app/src/main/AndroidManifest.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/br/com/cauejannini/testesantander/commons/SharedPreferencesHelper.kt b/app/src/main/java/br/com/cauejannini/testesantander/commons/SharedPreferencesHelper.kt
new file mode 100644
index 000000000..affb58b9a
--- /dev/null
+++ b/app/src/main/java/br/com/cauejannini/testesantander/commons/SharedPreferencesHelper.kt
@@ -0,0 +1,38 @@
+package br.com.cauejannini.testesantander.commons
+
+import android.content.Context
+import android.content.SharedPreferences
+import android.preference.PreferenceManager
+import br.com.cauejannini.testesantander.login.UserAccount
+import com.google.gson.Gson
+
+object SharedPreferencesHelper {
+
+ private val SP_KEY_USER_ACCOUNT = "SP_KEY_USER_ACCOUNT"
+ private val SP_KEY_USER_NAME = "SP_KEY_USER_NAME"
+
+ fun getSP(context: Context): SharedPreferences? {
+ return PreferenceManager.getDefaultSharedPreferences(context)
+ }
+
+ fun saveLoggedUser(context: Context, username: String) {
+ getSP(context)?.edit()?.putString(SP_KEY_USER_NAME, username)?.apply()
+ }
+
+ fun getLastLoggedUser(context: Context): String? {
+ return getSP(context)?.getString(SP_KEY_USER_NAME, null)
+ }
+
+// fun saveUserAccount(context: Context, userAccount: UserAccount) {
+// val json = Gson().toJson(userAccount)
+// getSP(context)?.edit()?.putString(SP_KEY_USER_ACCOUNT, json)?.apply()
+// }
+//
+// fun getUserAccount(context: Context): UserAccount? {
+// getSP(context)?.getString(SP_KEY_USER_ACCOUNT, null)?.let {
+// return Gson().fromJson(it, UserAccount::class.java)
+// }
+// return null
+// }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/br/com/cauejannini/testesantander/commons/Utils.kt b/app/src/main/java/br/com/cauejannini/testesantander/commons/Utils.kt
new file mode 100644
index 000000000..5c0498a12
--- /dev/null
+++ b/app/src/main/java/br/com/cauejannini/testesantander/commons/Utils.kt
@@ -0,0 +1,71 @@
+package br.com.cauejannini.testesantander.commons
+
+import android.content.Context
+import android.widget.Toast
+import java.text.DecimalFormat
+import java.text.DecimalFormatSymbols
+import java.text.NumberFormat
+import java.text.ParseException
+import java.util.*
+
+object Utils {
+
+ fun showToast(context: Context?, text: String?) {
+
+ if (context != null && text != null) {
+ Toast.makeText(context, text, Toast.LENGTH_SHORT).show()
+ }
+ }
+
+ fun valorMonetarioParaDouble(valor: String?): Double? {
+ if (valor != null) {
+
+ val df = DecimalFormat("#,##0.00")
+ try {
+ val number = df.parse(valor)
+ number?.let{
+ return it.toDouble()
+ }
+ } catch (e: ParseException) {
+ e.printStackTrace()
+ }
+ }
+ return null
+ }
+
+ fun doubleParaValorMonetario(valor: Double?): String {
+ valor?.let{
+ val df = DecimalFormat("#,##0.00")
+ return df.format(it)
+ }
+ return "-"
+ }
+
+ fun dataApiParaApp(data: String?): String {
+ data?.let{
+ val array = data.split("-")
+ if (array.size == 3) {
+ return String.format("%s/%s/%s", array.get(2), array.get(1),array.get(0))
+ }
+ }
+ return "-"
+ }
+
+ fun doisDecimais(valor: Double?): String? {
+ if (valor != null) {
+ val decimalFormat = DecimalFormat(
+ "#,##0.00",
+ DecimalFormatSymbols(Locale("pt", "BR"))
+ )
+ return decimalFormat.format(valor)
+ }
+ return "-"
+ }
+
+ fun currencyBrazil(valor: Double?): String {
+ val doisDecimais = doisDecimais(valor)
+
+ return "R$ $doisDecimais"
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/br/com/cauejannini/testesantander/commons/form/AppButton.kt b/app/src/main/java/br/com/cauejannini/testesantander/commons/form/AppButton.kt
new file mode 100644
index 000000000..11997608a
--- /dev/null
+++ b/app/src/main/java/br/com/cauejannini/testesantander/commons/form/AppButton.kt
@@ -0,0 +1,65 @@
+package br.com.cauejannini.testesantander.commons.form
+
+import android.content.Context
+import android.graphics.PorterDuff
+import android.util.AttributeSet
+import android.view.LayoutInflater
+import android.view.View
+import android.widget.LinearLayout
+import android.widget.ProgressBar
+import android.widget.TextView
+import androidx.core.content.ContextCompat
+import br.com.cauejannini.testesantander.R
+
+class AppButton(context: Context?, attrs: AttributeSet?) : LinearLayout(context, attrs) {
+
+ var textView: TextView
+ var progressBar: ProgressBar
+ var text: String? = null
+
+ init {
+ initializeAttrs(attrs)
+ orientation = VERTICAL
+ background = resources.getDrawable(R.drawable.app_button)
+
+ // Adicionar view
+ val child = LayoutInflater.from(context).inflate(R.layout.app_button, this, true)
+
+ // Configurar estado inicial
+ textView = child.findViewWithTag("textView")
+ progressBar = child.findViewWithTag("progressBar")
+ progressBar.indeterminateDrawable.setColorFilter(ContextCompat.getColor(context!!, R.color.white), PorterDuff.Mode.SRC_IN)
+
+ text?.let { textView.text = it }
+ }
+
+ fun updateText(text: String) {
+ this.text = text
+ textView.text = text
+ }
+
+ private fun initializeAttrs(attrs: AttributeSet?) {
+
+ val typedArray = context.obtainStyledAttributes(attrs, R.styleable.AppButton)
+
+ text = typedArray.getString(R.styleable.AppButton_text)
+ typedArray.recycle()
+ }
+
+ override fun setEnabled(enabled: Boolean) {
+ super.setEnabled(enabled)
+ isClickable = enabled
+ }
+
+ fun setLoading(loading: Boolean) {
+ if (loading) {
+ isClickable = false
+ textView.visibility = View.INVISIBLE
+ progressBar.visibility = View.VISIBLE
+ } else {
+ progressBar.visibility = View.INVISIBLE
+ textView.visibility = View.VISIBLE
+ isClickable = true
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/br/com/cauejannini/testesantander/commons/form/Form.kt b/app/src/main/java/br/com/cauejannini/testesantander/commons/form/Form.kt
new file mode 100644
index 000000000..4e9c16746
--- /dev/null
+++ b/app/src/main/java/br/com/cauejannini/testesantander/commons/form/Form.kt
@@ -0,0 +1,64 @@
+package br.com.cauejannini.testesantander.commons.form
+
+import android.view.View
+import java.util.*
+
+/**
+ * Created by cauejannini on 16/01/2018.
+ */
+class Form (private val btAction: View?) {
+
+ private val fields = ArrayList()
+
+ init {
+ checkButtonStatus()
+ }
+
+ fun addValidatable(field: Validatable) {
+ fields.add(field)
+ field.form = this
+ checkButtonStatus()
+ }
+
+ fun removeValidatable(fieldToRemove: Validatable?) {
+ if (fieldToRemove != null) {
+ val iter = fields.iterator()
+ while (iter.hasNext()) {
+ val field = iter.next()
+ if (field == fieldToRemove) {
+ field.form = null
+ iter.remove()
+ }
+ }
+ checkButtonStatus()
+ }
+ }
+
+ private fun checkButtonStatus() {
+ if (btAction != null) btAction.isEnabled = isValid
+ }
+
+ val isValid: Boolean
+ get() {
+ for (item in fields) {
+ if (!item.isValid) {
+ return false
+ }
+ }
+ return true
+ }
+
+ fun validadeReceiver(valido: Boolean) {
+ if (valido) {
+ checkButtonStatus()
+ } else {
+ if (btAction != null) btAction.isEnabled = false
+ }
+ }
+
+ interface Validatable {
+ var form: Form?
+ val isValid: Boolean
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/br/com/cauejannini/testesantander/commons/form/InputTextField.kt b/app/src/main/java/br/com/cauejannini/testesantander/commons/form/InputTextField.kt
new file mode 100644
index 000000000..8578efd0c
--- /dev/null
+++ b/app/src/main/java/br/com/cauejannini/testesantander/commons/form/InputTextField.kt
@@ -0,0 +1,204 @@
+package br.com.cauejannini.testesantander.commons.form
+
+import android.content.Context
+import android.text.Editable
+import android.text.InputType
+import android.text.TextWatcher
+import android.text.method.PasswordTransformationMethod
+import android.util.AttributeSet
+import android.util.TypedValue
+import android.view.Gravity
+import android.view.LayoutInflater
+import android.view.View.OnFocusChangeListener
+import android.view.inputmethod.EditorInfo
+import android.view.inputmethod.InputMethodManager
+import android.widget.EditText
+import android.widget.LinearLayout
+import android.widget.TextView.OnEditorActionListener
+import br.com.cauejannini.testesantander.R
+import br.com.cauejannini.testesantander.commons.form.textwatchers.ValidatorTextWatcher
+import br.com.cauejannini.testesantander.commons.form.validators.ValidationResult
+import br.com.cauejannini.testesantander.commons.form.validators.Validator
+
+class InputTextField(context: Context?, attrs: AttributeSet?) : LinearLayout(context, attrs),
+ ValidatorTextWatcher.WatchableField,
+ Form.Validatable {
+
+ override lateinit var editText: EditText
+
+ var text: String
+ get() = editText.text.toString()
+ set(text) {
+ editText.setText(text)
+ }
+
+ var hint: String
+ get() = editText.hint.toString()
+ set(hint) {
+ editText.hint = hint
+ }
+
+ var inputType = 0
+ var imeOptions = 0
+ var customTextAlignment = 0
+ var maxLines = 0
+ var textSize = 0f
+ var textWatcher: TextWatcher? = null
+ var myValidator: Validator? = null
+ var afterTextChangedListener: AfterTextChangedListener? = null
+
+ override var form: Form? = null
+
+ var onEditTextFocusChangeValidator = OnFocusChangeListener { view, isFocused ->
+ if (isFocused) {
+ onFocused()
+ } else {
+ onUnfocused()
+ val imm = getContext().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
+ imm.hideSoftInputFromWindow(view.windowToken, 0)
+ }
+ }
+
+ init {
+
+ val child = LayoutInflater.from(context).inflate(R.layout.field_input_text, this, true)
+ editText = child.findViewWithTag("et")
+
+ background = resources.getDrawable(R.drawable.input_text_bg)
+
+ initAttrs(attrs)
+
+ if (inputType != -1) {
+ editText.inputType = inputType or InputType.TYPE_TEXT_FLAG_MULTI_LINE or InputType.TYPE_TEXT_VARIATION_LONG_MESSAGE
+ if (inputType == InputType.TYPE_TEXT_VARIATION_PASSWORD) {
+ editText.transformationMethod = PasswordTransformationMethod.getInstance()
+ }
+ }
+
+ editText.imeOptions = imeOptions
+
+ editText.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize)
+
+ if (maxLines > 0) editText.maxLines = maxLines
+
+ editText.gravity = Gravity.TOP or customTextAlignment
+
+ editText.onFocusChangeListener = onEditTextFocusChangeValidator
+
+ editText.setHintTextColor(resources.getColor(R.color.hintColor))
+
+ if (textWatcher == null) {
+ editText.addTextChangedListener(object : TextWatcher {
+ override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
+
+ override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
+
+ override fun afterTextChanged(editable: Editable) {
+ this@InputTextField.afterTextChanged(true, editable.toString())
+ }
+ })
+ }
+ }
+
+ private fun initAttrs(attrs: AttributeSet?) {
+
+ val typedArray = context.obtainStyledAttributes(attrs, R.styleable.InputTextField)
+
+ var hint = typedArray.getString(R.styleable.InputTextField_hint)
+ if (hint == null) hint = ""
+ this.hint = hint
+
+ inputType = typedArray.getInt(R.styleable.InputTextField_inputType, -1)
+ textSize = typedArray.getDimension(R.styleable.InputTextField_textSize, resources.getDimension(R.dimen.ts))
+ customTextAlignment = typedArray.getInt(R.styleable.InputTextField_textAlignment, Gravity.START)
+ maxLines = typedArray.getInt(R.styleable.InputTextField_maxLines, -1)
+ imeOptions = typedArray.getInt(R.styleable.InputTextField_imeOptions, EditorInfo.IME_ACTION_NEXT)
+
+ typedArray.recycle()
+ }
+
+ override fun setEnabled(enabled: Boolean) {
+ if (enabled) {
+ editText.isEnabled = true
+ editText.alpha = 1f
+ } else {
+ editText.isEnabled = false
+ editText.alpha = .5f
+ }
+ }
+
+ fun setOnEditorActionListener(onEditorActionListener: OnEditorActionListener) {
+ editText.setOnEditorActionListener(onEditorActionListener)
+ }
+
+ override val isValid: Boolean
+ get() = myValidator == null || myValidator!!.validate(text).isValid
+
+ override fun afterTextChanged(valid: Boolean, text: String) {
+ runValidation(text, false)
+ if (afterTextChangedListener != null) afterTextChangedListener!!.afterTextChanged(text)
+ }
+
+ fun runValidation(text: String?, reflectLayout: Boolean) {
+ val validationResult = if (myValidator != null) myValidator!!.validate(text) else ValidationResult(true, "")
+
+ if (text!!.trim { it <= ' ' }.isEmpty() || !reflectLayout) {
+ layoutNeutral()
+ } else {
+ if (validationResult.isValid) {
+ layoutValid()
+ } else {
+ layoutError(validationResult.getMessage())
+ }
+ }
+ form?.validadeReceiver(validationResult.isValid)
+ }
+
+ private fun layoutNeutral() {
+ editText.setTextColor(resources.getColor(R.color.textColor))
+ }
+
+ private fun layoutError(message: String) {
+ editText.setTextColor(resources.getColor(R.color.red))
+ editText.error = message
+ }
+
+ private fun layoutValid() {
+ editText.setTextColor(resources.getColor(R.color.textColor))
+ }
+
+ fun setValidator(validationInterface: Validator?) {
+ myValidator = validationInterface
+ runValidation(text, true)
+ }
+
+ fun showErrorOnFocusLost(shouldShow: Boolean) {
+ if (shouldShow) {
+ editText.onFocusChangeListener = onEditTextFocusChangeValidator
+ } else {
+ editText.onFocusChangeListener = null
+ }
+ }
+
+ fun setCustomTextWatcher(textWatcher: TextWatcher?) {
+ if (textWatcher != null) {
+ this.textWatcher = textWatcher
+ editText.addTextChangedListener(this.textWatcher)
+ } else {
+ editText.removeTextChangedListener(this.textWatcher)
+ this.textWatcher = null
+ }
+ }
+
+ private fun onFocused() {
+ layoutNeutral()
+ }
+
+ private fun onUnfocused() {
+ runValidation(text, true)
+ }
+
+ interface AfterTextChangedListener {
+ fun afterTextChanged(text: String)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/br/com/cauejannini/testesantander/commons/form/textwatchers/ValidatorTextWatcher.kt b/app/src/main/java/br/com/cauejannini/testesantander/commons/form/textwatchers/ValidatorTextWatcher.kt
new file mode 100644
index 000000000..72a65f080
--- /dev/null
+++ b/app/src/main/java/br/com/cauejannini/testesantander/commons/form/textwatchers/ValidatorTextWatcher.kt
@@ -0,0 +1,51 @@
+package br.com.cauejannini.testesantander.commons.form.textwatchers
+
+import android.text.Editable
+import android.text.TextWatcher
+import android.widget.EditText
+
+/**
+ * Created by user on 16/01/18.
+ */
+abstract class ValidatorTextWatcher : TextWatcher {
+
+ private var formInterface: WatchableField? = null
+ protected var editText: EditText
+ private var lastValidated: String? = null
+
+ constructor(editText: EditText) {
+ this.editText = editText
+ }
+
+ constructor(formInterface: WatchableField) {
+ this.formInterface = formInterface
+ editText = this.formInterface!!.editText
+
+ val valor = editText.text.toString()
+ val valido = valida(valor)
+ this.formInterface!!.afterTextChanged(valido, valor)
+ }
+
+ override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
+
+ override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
+
+ override fun afterTextChanged(s: Editable) {
+ if (formInterface != null) {
+ val current = s.toString()
+ if (current != lastValidated) {
+ lastValidated = current
+ val valido = valida(current)
+ formInterface!!.afterTextChanged(valido, current)
+ }
+ }
+ }
+
+
+ protected abstract fun valida(valor: String): Boolean
+
+ interface WatchableField {
+ var editText: EditText
+ fun afterTextChanged(valid: Boolean, text: String)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/br/com/cauejannini/testesantander/commons/form/textwatchers/ValorMonetarioTextWatcher.kt b/app/src/main/java/br/com/cauejannini/testesantander/commons/form/textwatchers/ValorMonetarioTextWatcher.kt
new file mode 100644
index 000000000..7c2fd1201
--- /dev/null
+++ b/app/src/main/java/br/com/cauejannini/testesantander/commons/form/textwatchers/ValorMonetarioTextWatcher.kt
@@ -0,0 +1,86 @@
+package br.com.cauejannini.testesantander.commons.form.textwatchers
+
+import android.text.Editable
+import android.widget.EditText
+import br.com.cauejannini.testesantander.commons.form.validators.Validations
+
+/**
+ * Created by cauejannini on 24/04/17.
+ */
+class ValorMonetarioTextWatcher : ValidatorTextWatcher {
+
+ var cifrao: String? = null
+
+ constructor (editText: EditText) : super(editText) {}
+
+ constructor (formInterface: WatchableField) : super(formInterface) {}
+
+ constructor (formInterface: WatchableField, cifrao: String?) : super(formInterface) {
+ this.cifrao = cifrao
+ }
+
+ override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
+
+ override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
+
+ override fun afterTextChanged(editable: Editable) {
+
+ var str = editable.toString()
+
+ if (cifrao != null) {
+ str = str.replace(cifrao!!, "").trim { it <= ' ' }
+ str = str.replace(cifrao!!.substring(0, cifrao!!.length - 1), "").trim { it <= ' ' }
+ }
+
+ if (str != "") {
+
+ var clean = str.replace(".", "").replace(",", "")
+
+ val stringBuilder = StringBuilder(clean)
+
+ while (stringBuilder.length > 0 && stringBuilder[0] == '0') {
+ stringBuilder.deleteCharAt(0)
+ }
+
+ clean = stringBuilder.toString()
+ val countChar = clean.length
+
+ str = if (countChar > 19) {
+ clean.substring(countChar - 19, countChar - 17) + "." + clean.substring(countChar - 17, countChar - 14) + "." + clean.substring(countChar - 14, countChar - 11) + "." + clean.substring(countChar - 11, countChar - 8) + "." + clean.substring(countChar - 8, countChar - 5) + "." + clean.substring(countChar - 5, countChar - 2) + "," + clean.substring(countChar - 2)
+ } else if (countChar > 17) {
+ clean.substring(0, countChar - 17) + "." + clean.substring(countChar - 17, countChar - 14) + "." + clean.substring(countChar - 14, countChar - 11) + "." + clean.substring(countChar - 11, countChar - 8) + "." + clean.substring(countChar - 8, countChar - 5) + "." + clean.substring(countChar - 5, countChar - 2) + "," + clean.substring(countChar - 2)
+ } else if (countChar > 14) {
+ clean.substring(0, countChar - 14) + "." + clean.substring(countChar - 14, countChar - 11) + "." + clean.substring(countChar - 11, countChar - 8) + "." + clean.substring(countChar - 8, countChar - 5) + "." + clean.substring(countChar - 5, countChar - 2) + "," + clean.substring(countChar - 2)
+ } else if (countChar > 11) {
+ clean.substring(0, countChar - 11) + "." + clean.substring(countChar - 11, countChar - 8) + "." + clean.substring(countChar - 8, countChar - 5) + "." + clean.substring(countChar - 5, countChar - 2) + "," + clean.substring(countChar - 2)
+ } else if (countChar > 8) {
+ clean.substring(0, countChar - 8) + "." + clean.substring(countChar - 8, countChar - 5) + "." + clean.substring(countChar - 5, countChar - 2) + "," + clean.substring(countChar - 2)
+ } else if (countChar > 5) {
+ clean.substring(0, countChar - 5) + "." + clean.substring(countChar - 5, countChar - 2) + "," + clean.substring(countChar - 2)
+ } else if (countChar > 2) {
+ clean.substring(0, countChar - 2) + "," + clean.substring(countChar - 2)
+ } else if (countChar == 2) {
+ "0,$clean"
+ } else if (countChar == 1) {
+ "0,0$clean"
+ } else {
+ "0,00"
+ }
+ }
+
+ editText.removeTextChangedListener(this)
+ val filters = editable.filters
+ editable.filters = arrayOf()
+ val newString = if (cifrao != null) "$cifrao $str" else str
+ editable.replace(0, editable.length, newString)
+ editable.filters = filters
+ editText.setSelection(editText.length())
+ editText.addTextChangedListener(this)
+
+ super.afterTextChanged(editable)
+ }
+
+ override fun valida(valor: String): Boolean {
+ return Validations.validarValorMonetario(valor).isValid
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/br/com/cauejannini/testesantander/commons/form/validators/PasswordValidator.kt b/app/src/main/java/br/com/cauejannini/testesantander/commons/form/validators/PasswordValidator.kt
new file mode 100644
index 000000000..2f1120d66
--- /dev/null
+++ b/app/src/main/java/br/com/cauejannini/testesantander/commons/form/validators/PasswordValidator.kt
@@ -0,0 +1,14 @@
+package br.com.cauejannini.testesantander.commons.form.validators
+
+import br.com.cauejannini.testesantander.commons.Utils
+
+/**
+ * Created by cauejannini on 14/03/2018.
+ */
+class PasswordValidator : Validator {
+
+ override fun validate(text: String?): ValidationResult {
+
+ return Validations.validarSenha(text)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/br/com/cauejannini/testesantander/commons/form/validators/UsernameValidator.kt b/app/src/main/java/br/com/cauejannini/testesantander/commons/form/validators/UsernameValidator.kt
new file mode 100644
index 000000000..92280d30c
--- /dev/null
+++ b/app/src/main/java/br/com/cauejannini/testesantander/commons/form/validators/UsernameValidator.kt
@@ -0,0 +1,14 @@
+package br.com.cauejannini.testesantander.commons.form.validators
+
+import br.com.cauejannini.testesantander.commons.Utils
+
+/**
+ * Created by cauejannini on 14/03/2018.
+ */
+class UsernameValidator : Validator {
+
+ override fun validate(text: String?): ValidationResult {
+
+ return Validations.validarUsuario(text)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/br/com/cauejannini/testesantander/commons/form/validators/ValidationResult.kt b/app/src/main/java/br/com/cauejannini/testesantander/commons/form/validators/ValidationResult.kt
new file mode 100644
index 000000000..190c95dbc
--- /dev/null
+++ b/app/src/main/java/br/com/cauejannini/testesantander/commons/form/validators/ValidationResult.kt
@@ -0,0 +1,12 @@
+package br.com.cauejannini.testesantander.commons.form.validators
+
+/**
+ * Created by cauejannini on 14/03/2018.
+ */
+class ValidationResult(val isValid: Boolean, private val message: String?) {
+
+ fun getMessage(): String {
+ return message ?: if (isValid) "Campo válido" else "Campo inválido"
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/br/com/cauejannini/testesantander/commons/form/validators/Validations.kt b/app/src/main/java/br/com/cauejannini/testesantander/commons/form/validators/Validations.kt
new file mode 100644
index 000000000..ddf65d3ba
--- /dev/null
+++ b/app/src/main/java/br/com/cauejannini/testesantander/commons/form/validators/Validations.kt
@@ -0,0 +1,100 @@
+package br.com.cauejannini.testesantander.commons.form.validators
+
+/**
+ * Created by cauejannini on 23/02/17.
+ */
+object Validations {
+
+
+ fun validarSenha(senha: String?): ValidationResult {
+ return if (senha != null &&
+ senha.isNotEmpty() &&
+ // Checar se tem pelo menos um dígito
+ senha.matches("(?=.*[0-9]).*".toRegex()) &&
+
+ // Checar se tem pelo menos uma letra maiúscula
+ senha.matches("(?=.*[A-Z]).*".toRegex()) &&
+
+ // Checar se tem pelo menos um caractere especial
+ senha.matches("(?=.*[~!@#$%^&*()_-]).*".toRegex())) {
+ ValidationResult(true, "Senha OK")
+ } else {
+ ValidationResult(false, "Deve conter um número, uma maiúscula e um caractere especial")
+ }
+ }
+
+ fun validarUsuario(usuario: String?): ValidationResult {
+
+ return if (validarCpf(usuario) || validarEmail(usuario)) {
+ ValidationResult(true, "Usuário OK")
+ } else {
+ ValidationResult(false, "Usuário deve ser email ou CPF")
+ }
+ }
+
+ fun validarEmail(email: String?): Boolean {
+ return (email != null && email.matches("^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,6}$".toRegex()))
+ }
+
+ fun validarCpf(cpfString: String?): Boolean {
+ try {
+ val cpf = cpfString!!.replace("-", "").replace(".", "")
+ if (cpf == "00000000000" || cpf == "11111111111" || cpf == "22222222222" || cpf == "33333333333" || cpf == "44444444444" || cpf == "55555555555" || cpf == "66666666666" || cpf == "77777777777" || cpf == "88888888888" || cpf == "99999999999") {
+ return false
+ }
+ if (cpf.length < 11) {
+ return false
+ } else if (cpf.length == 11) {
+
+ // VALIDAR PRIMEIRO DIGITO
+ val d1 = cpf[0].toInt() - 48
+ val d2 = cpf[1].toInt() - 48
+ val d3 = cpf[2].toInt() - 48
+ val d4 = cpf[3].toInt() - 48
+ val d5 = cpf[4].toInt() - 48
+ val d6 = cpf[5].toInt() - 48
+ val d7 = cpf[6].toInt() - 48
+ val d8 = cpf[7].toInt() - 48
+ val d9 = cpf[8].toInt() - 48
+ val d10 = cpf[9].toInt() - 48
+ val d11 = cpf[10].toInt() - 48
+ val totalSum1 = d1 * 10 + d2 * 9 + d3 * 8 + d4 * 7 + d5 * 6 + d6 * 5 + d7 * 4 + d8 * 3 + d9 * 2
+
+ val remainder1 = totalSum1 % 11
+ var firstDigitCorret = 0
+ if (remainder1 >= 2) {
+ firstDigitCorret = 11 - remainder1
+ }
+ if (d10 == firstDigitCorret) {
+
+ // VALIDAR SEGUNDO DÍGITO
+ val totalSum2 =
+ totalSum1 + d1 + d2 + d3 + d4 + d5 + d6 + d7 + d8 + d9 + d10 * 2
+ val remainder2 = totalSum2 % 11
+ var secondDigitCorrect = 0
+ if (remainder2 >= 2) {
+ secondDigitCorrect = 11 - remainder2
+ }
+ if (secondDigitCorrect == d11) {
+ return true
+ }
+ }
+ }
+ } catch (ignored: Exception) {
+ }
+ return false
+ }
+
+ fun validarValorMonetario(renda: String): ValidationResult {
+
+ val rendaJustNumbers = renda.replace(".", "").replace("R$", "").trim { it <= ' ' }
+ val arrayRenda = rendaJustNumbers.split(",".toRegex()).toTypedArray()
+ val rendaSemDecimal = arrayRenda[0]
+
+ return if (rendaSemDecimal.matches("[0-9]+".toRegex())) {
+ ValidationResult(true, "Valor ok")
+ } else {
+ ValidationResult(false, "Valor inválido")
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/br/com/cauejannini/testesantander/commons/form/validators/Validator.kt b/app/src/main/java/br/com/cauejannini/testesantander/commons/form/validators/Validator.kt
new file mode 100644
index 000000000..6d5aa47b0
--- /dev/null
+++ b/app/src/main/java/br/com/cauejannini/testesantander/commons/form/validators/Validator.kt
@@ -0,0 +1,8 @@
+package br.com.cauejannini.testesantander.commons.form.validators
+
+/**
+ * Created by cauejannini on 14/03/2018.
+ */
+interface Validator {
+ fun validate(text: String?): ValidationResult
+}
\ No newline at end of file
diff --git a/app/src/main/java/br/com/cauejannini/testesantander/commons/form/validators/ValorMonetarioValidator.kt b/app/src/main/java/br/com/cauejannini/testesantander/commons/form/validators/ValorMonetarioValidator.kt
new file mode 100644
index 000000000..2d784afd8
--- /dev/null
+++ b/app/src/main/java/br/com/cauejannini/testesantander/commons/form/validators/ValorMonetarioValidator.kt
@@ -0,0 +1,45 @@
+package br.com.cauejannini.testesantander.commons.form.validators
+
+import br.com.cauejannini.testesantander.commons.Utils
+
+/**
+ * Created by cauejannini on 14/03/2018.
+ */
+class ValorMonetarioValidator : Validator {
+
+ var min: Double? = null
+ var max: Double? = null
+
+ constructor() {}
+
+ constructor(min: Double?, max: Double?) {
+ this.min = min
+ this.max = max
+ }
+
+ override fun validate(text: String?): ValidationResult {
+
+ val validationResult = Validations.validarValorMonetario(text!!)
+ if (validationResult.isValid) {
+ if (min != null || max != null) {
+
+ if (min != null && max != null && min!! > max!!) {
+ return ValidationResult(false, "Valor inválido")
+ }
+
+ val valorDouble = Utils.valorMonetarioParaDouble(text)
+
+ if (valorDouble != null) {
+ if (min != null && valorDouble < min!!) {
+ return ValidationResult(false, "O valor mínimo é de " + Utils.doisDecimais(min))
+ }
+
+ return if (max != null && valorDouble > max!!) {
+ ValidationResult(false, "O valor máximo é de " + Utils.doisDecimais(max))
+ } else ValidationResult(true, "Válido")
+ }
+ }
+ }
+ return validationResult
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/br/com/cauejannini/testesantander/commons/integracao/Api.kt b/app/src/main/java/br/com/cauejannini/testesantander/commons/integracao/Api.kt
new file mode 100644
index 000000000..2adf2f4ae
--- /dev/null
+++ b/app/src/main/java/br/com/cauejannini/testesantander/commons/integracao/Api.kt
@@ -0,0 +1,16 @@
+package br.com.cauejannini.testesantander.commons.integracao
+
+import br.com.cauejannini.testesantander.login.LoginResponseModel
+import br.com.cauejannini.testesantander.statements.StatementsResponseModel
+import retrofit2.Call
+import retrofit2.http.*
+
+interface Api {
+
+ @FormUrlEncoded
+ @POST("login")
+ fun login(@Field("user") user: String, @Field("password") password: String): Call
+
+ @GET("statements/{userId}")
+ fun getStatements(@Path("userId") userId: Int): Call
+}
\ No newline at end of file
diff --git a/app/src/main/java/br/com/cauejannini/testesantander/commons/integracao/ApiRepository.kt b/app/src/main/java/br/com/cauejannini/testesantander/commons/integracao/ApiRepository.kt
new file mode 100644
index 000000000..5fb940f76
--- /dev/null
+++ b/app/src/main/java/br/com/cauejannini/testesantander/commons/integracao/ApiRepository.kt
@@ -0,0 +1,11 @@
+package br.com.cauejannini.testesantander.commons.integracao
+
+import android.content.Context
+
+class ApiRepository() {
+
+ fun get(): Api {
+ return RetrofitService().createService(Api::class.java)
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/br/com/cauejannini/testesantander/commons/integracao/NetworkUtils.kt b/app/src/main/java/br/com/cauejannini/testesantander/commons/integracao/NetworkUtils.kt
new file mode 100644
index 000000000..dfadded66
--- /dev/null
+++ b/app/src/main/java/br/com/cauejannini/testesantander/commons/integracao/NetworkUtils.kt
@@ -0,0 +1,20 @@
+package br.com.cauejannini.testesantander.commons.integracao
+
+import android.content.Context
+import android.net.ConnectivityManager
+import android.net.NetworkInfo
+
+object NetworkUtils {
+
+ fun isConnected(context: Context?): Boolean {
+ context?.let { contextNonNull ->
+ val connectivityManager = contextNonNull.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
+ connectivityManager.activeNetworkInfo?.let { networkInfo ->
+ return networkInfo.isConnected
+ }
+ return false
+ }
+ return true
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/br/com/cauejannini/testesantander/commons/integracao/RetrofitService.kt b/app/src/main/java/br/com/cauejannini/testesantander/commons/integracao/RetrofitService.kt
new file mode 100644
index 000000000..610ef1339
--- /dev/null
+++ b/app/src/main/java/br/com/cauejannini/testesantander/commons/integracao/RetrofitService.kt
@@ -0,0 +1,58 @@
+package br.com.cauejannini.testesantander.commons.integracao
+
+import android.content.Context
+import br.com.cauejannini.testesantander.commons.integracao.NetworkUtils
+import okhttp3.HttpUrl
+import okhttp3.Interceptor
+import okhttp3.OkHttpClient
+import retrofit2.Retrofit
+import retrofit2.converter.gson.GsonConverterFactory
+import java.io.IOException
+
+class RetrofitService() {
+
+ val baseUrl = "https://bank-app-test.herokuapp.com/api/"
+
+ val retrofit: Retrofit = Retrofit.Builder()
+ .baseUrl(baseUrl)
+ .addConverterFactory(GsonConverterFactory.create())
+ .client(buildClient())
+ .build()
+
+ fun createService(serviceClass: Class): T {
+
+ return retrofit.create(serviceClass)
+ }
+
+ fun buildClient() : OkHttpClient {
+
+// val connectionCheckerInterface = Interceptor { chain ->
+//
+// if (!NetworkUtils.isConnected(context)) {
+// throw IOException("Você parece estar offline")
+// }
+// chain.proceed(chain.request().newBuilder().build())
+//
+// }
+
+// val authInterceptor = Interceptor { chain ->
+//
+// val apiKey = BuildConfig.API_KEY
+//
+// var request = chain.request()
+// val url: HttpUrl = request.url().newBuilder()
+// .addQueryParameter("access_key", apiKey)
+// .build()
+// request = request.newBuilder().url(url).build()
+//
+// chain.proceed(request)
+//
+// }
+
+ return OkHttpClient.Builder()
+// .addInterceptor(connectionCheckerInterface)
+// .addInterceptor(authInterceptor)
+ .build()
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/br/com/cauejannini/testesantander/login/LoginActivity.kt b/app/src/main/java/br/com/cauejannini/testesantander/login/LoginActivity.kt
new file mode 100644
index 000000000..4ab8d729c
--- /dev/null
+++ b/app/src/main/java/br/com/cauejannini/testesantander/login/LoginActivity.kt
@@ -0,0 +1,87 @@
+package br.com.cauejannini.testesantander.login
+
+import android.app.Activity
+import android.content.Context
+import android.content.Intent
+import androidx.appcompat.app.AppCompatActivity
+import android.os.Bundle
+import android.util.Log
+import android.view.View
+import android.view.inputmethod.InputMethodManager
+import android.widget.TextView
+import br.com.cauejannini.testesantander.R
+import br.com.cauejannini.testesantander.commons.SharedPreferencesHelper
+import br.com.cauejannini.testesantander.commons.Utils
+import br.com.cauejannini.testesantander.commons.form.Form
+import br.com.cauejannini.testesantander.commons.form.validators.PasswordValidator
+import br.com.cauejannini.testesantander.commons.form.validators.UsernameValidator
+import br.com.cauejannini.testesantander.statements.StatementsActivity
+import com.google.gson.Gson
+import kotlinx.android.synthetic.main.activity_login.*
+
+interface LoginActivityInput {
+ fun onLoggedIn(userAccount: UserAccount)
+ fun onLoginError(message: String)
+}
+
+class LoginActivity : AppCompatActivity(), LoginActivityInput {
+
+ lateinit var output: LoginInteractorInput
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_login)
+
+ LoginConfigurator.configure(this)
+
+ inputUser.setValidator(UsernameValidator())
+ inputPassword.setValidator(PasswordValidator())
+
+ val form = Form(btLogin)
+ form.addValidatable(inputUser)
+ form.addValidatable(inputPassword)
+
+ btLogin.setOnClickListener {
+ login()
+ }
+
+ inputPassword.setOnEditorActionListener(TextView.OnEditorActionListener { textView, i, keyEvent ->
+ hideKeyboard()
+ login()
+ true
+ })
+
+ SharedPreferencesHelper.getLastLoggedUser(this)?.let {
+ inputUser.text = it
+ }
+ }
+
+ fun login() {
+ val user = inputUser.text
+ val password = inputPassword.text
+ output.login(LoginRequest(user, password))
+ btLogin.setLoading(true)
+ }
+
+ override fun onLoggedIn(userAccount: UserAccount) {
+ Log.i("userAccount", Gson().toJson(userAccount))
+ btLogin.setLoading(false)
+
+ SharedPreferencesHelper.saveLoggedUser(this, inputUser.text)
+
+ val intent = Intent(this, StatementsActivity::class.java)
+ intent.putExtra(StatementsActivity.EXTRA_KEY_USER_ACCOUNT, userAccount)
+ startActivity(intent)
+ }
+
+ override fun onLoginError(message: String) {
+ Utils.showToast(this, message)
+ btLogin.setLoading(false)
+ }
+
+ fun AppCompatActivity.hideKeyboard() {
+ val view = currentFocus ?: View(this)
+ val inputMethodManager = getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager
+ inputMethodManager.hideSoftInputFromWindow(view.windowToken, 0)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/br/com/cauejannini/testesantander/login/LoginConfigurator.kt b/app/src/main/java/br/com/cauejannini/testesantander/login/LoginConfigurator.kt
new file mode 100644
index 000000000..e4f079385
--- /dev/null
+++ b/app/src/main/java/br/com/cauejannini/testesantander/login/LoginConfigurator.kt
@@ -0,0 +1,19 @@
+package br.com.cauejannini.testesantander.login
+
+import java.lang.ref.WeakReference
+
+object LoginConfigurator {
+
+ fun configure(loginActivity: LoginActivity) {
+
+ val presenter = LoginPresenter()
+ presenter.output = WeakReference(loginActivity)
+
+ val interactor = LoginInteractor()
+ interactor.output = presenter
+
+ loginActivity.output = interactor
+
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/br/com/cauejannini/testesantander/login/LoginInteractor.kt b/app/src/main/java/br/com/cauejannini/testesantander/login/LoginInteractor.kt
new file mode 100644
index 000000000..10f839c3c
--- /dev/null
+++ b/app/src/main/java/br/com/cauejannini/testesantander/login/LoginInteractor.kt
@@ -0,0 +1,43 @@
+package br.com.cauejannini.testesantander.login
+
+import android.content.Context
+import br.com.cauejannini.testesantander.commons.integracao.ApiRepository
+import retrofit2.Call
+import retrofit2.Callback
+import retrofit2.Response
+
+interface LoginInteractorInput {
+ fun login(request: LoginRequest)
+}
+
+class LoginInteractor: LoginInteractorInput {
+
+ var output: LoginPresenterInput? = null
+ val api = ApiRepository().get()
+
+ override fun login(request: LoginRequest) {
+
+ val user = request.user
+ val password = request.password
+
+ api.login(user, password).enqueue(object: Callback {
+
+ override fun onFailure(call: Call, t: Throwable) {
+ output?.onLoginFailed(if (t.message != null) t.message!! else "Erro")
+ }
+
+ override fun onResponse(call: Call, response: Response) {
+ val loginResponse = response.body()
+ if (loginResponse != null) {
+ output?.onLoginResponse(loginResponse)
+ } else {
+ output?.onLoginFailed(response.message())
+ }
+ }
+
+ })
+
+ }
+
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/br/com/cauejannini/testesantander/login/LoginModels.kt b/app/src/main/java/br/com/cauejannini/testesantander/login/LoginModels.kt
new file mode 100644
index 000000000..6b38835e2
--- /dev/null
+++ b/app/src/main/java/br/com/cauejannini/testesantander/login/LoginModels.kt
@@ -0,0 +1,20 @@
+package br.com.cauejannini.testesantander.login
+
+import java.io.Serializable
+
+data class LoginRequest(val user: String, val password: String) {}
+
+class LoginResponseModel {
+
+ var userAccount: UserAccount? = null
+ var error: Any? = null
+}
+
+class UserAccount: Serializable {
+
+ var userId: Int? = null
+ var name: String? = null
+ var bankAccount: String? = null
+ var agency: String? = null
+ var balance: Double? = null
+}
\ No newline at end of file
diff --git a/app/src/main/java/br/com/cauejannini/testesantander/login/LoginPresenter.kt b/app/src/main/java/br/com/cauejannini/testesantander/login/LoginPresenter.kt
new file mode 100644
index 000000000..e260c0492
--- /dev/null
+++ b/app/src/main/java/br/com/cauejannini/testesantander/login/LoginPresenter.kt
@@ -0,0 +1,27 @@
+package br.com.cauejannini.testesantander.login
+
+import java.lang.ref.WeakReference
+
+interface LoginPresenterInput {
+ fun onLoginResponse(loginResponse: LoginResponseModel)
+ fun onLoginFailed(message: String)
+}
+
+class LoginPresenter: LoginPresenterInput {
+
+ var output: WeakReference? = null
+
+ override fun onLoginResponse(loginResponse: LoginResponseModel) {
+ val userAccount = loginResponse.userAccount
+ if (userAccount != null) {
+ output?.get()?.onLoggedIn(userAccount)
+ } else {
+ output?.get()?.onLoginError(loginResponse.error.toString())
+ }
+ }
+
+ override fun onLoginFailed(message: String) {
+ output?.get()?.onLoginError(message)
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/br/com/cauejannini/testesantander/statements/StatementsActivity.kt b/app/src/main/java/br/com/cauejannini/testesantander/statements/StatementsActivity.kt
new file mode 100644
index 000000000..fc3bb20cd
--- /dev/null
+++ b/app/src/main/java/br/com/cauejannini/testesantander/statements/StatementsActivity.kt
@@ -0,0 +1,64 @@
+package br.com.cauejannini.testesantander.statements
+
+import androidx.appcompat.app.AppCompatActivity
+import android.os.Bundle
+import androidx.recyclerview.widget.LinearLayoutManager
+import br.com.cauejannini.testesantander.R
+import br.com.cauejannini.testesantander.commons.Utils
+import br.com.cauejannini.testesantander.login.UserAccount
+import kotlinx.android.synthetic.main.activity_statements.*
+
+interface StatementsActivityInput {
+ fun displayStatements(statements: List)
+ fun displayStatementsError(message: String)
+ fun displayUserData(userDataModel: UserDataModel)
+}
+
+class StatementsActivity : AppCompatActivity(), StatementsActivityInput {
+
+ companion object {
+ val EXTRA_KEY_USER_ACCOUNT = "EXTRA_KEY_USER_ACCOUNT"
+ }
+
+ lateinit var output: StatementsInteractorInput
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_statements)
+
+ StatementsConfigurator.configure(this)
+
+ val userAccount = intent.getSerializableExtra(EXTRA_KEY_USER_ACCOUNT) as UserAccount?
+
+ fetchStatements(userAccount)
+
+ swipeRefreshLayout.setOnRefreshListener {
+ fetchStatements(userAccount)
+ }
+
+ }
+
+ fun fetchStatements(userAccount: UserAccount?) {
+
+ output.fetchStatements(userAccount)
+ swipeRefreshLayout.isRefreshing = true
+ }
+
+ override fun displayStatements(statements: List) {
+ rvStatements.layoutManager = LinearLayoutManager(this)
+ rvStatements.adapter = StatementsRecyclerViewAdapter(statements)
+ swipeRefreshLayout.isRefreshing = false
+ }
+
+ override fun displayStatementsError(message: String) {
+ Utils.showToast(this, message)
+ swipeRefreshLayout.isRefreshing = false
+ }
+
+ override fun displayUserData(userDataModel: UserDataModel) {
+ tvUserName.text = userDataModel.userName
+ tvContaNumero.text = userDataModel.agenciaConta
+ tvSaldo.text = userDataModel.saldo
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/br/com/cauejannini/testesantander/statements/StatementsConfigurator.kt b/app/src/main/java/br/com/cauejannini/testesantander/statements/StatementsConfigurator.kt
new file mode 100644
index 000000000..d8fb59f64
--- /dev/null
+++ b/app/src/main/java/br/com/cauejannini/testesantander/statements/StatementsConfigurator.kt
@@ -0,0 +1,19 @@
+package br.com.cauejannini.testesantander.statements
+
+import java.lang.ref.WeakReference
+
+object StatementsConfigurator {
+
+ fun configure(statementsActivity: StatementsActivity) {
+
+ val presenter = StatementsPresenter()
+ presenter.output = WeakReference(statementsActivity)
+
+ val interactor = StatementsInteractor()
+ interactor.output = presenter
+
+ statementsActivity.output = interactor
+
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/br/com/cauejannini/testesantander/statements/StatementsInteractor.kt b/app/src/main/java/br/com/cauejannini/testesantander/statements/StatementsInteractor.kt
new file mode 100644
index 000000000..851c18024
--- /dev/null
+++ b/app/src/main/java/br/com/cauejannini/testesantander/statements/StatementsInteractor.kt
@@ -0,0 +1,48 @@
+package br.com.cauejannini.testesantander.statements
+
+import android.content.Context
+import br.com.cauejannini.testesantander.commons.integracao.ApiRepository
+import br.com.cauejannini.testesantander.login.UserAccount
+import retrofit2.Call
+import retrofit2.Callback
+import retrofit2.Response
+
+interface StatementsInteractorInput {
+ fun fetchStatements(userAccount: UserAccount?)
+}
+
+class StatementsInteractor: StatementsInteractorInput{
+
+ var output: StatementsPresenterInput? = null
+ val api = ApiRepository().get()
+
+ override fun fetchStatements(userAccount: UserAccount?) {
+
+ output?.presentUserAccount(userAccount)
+
+ val userId = userAccount?.userId
+ if (userId != null) {
+
+ api.getStatements(userId).enqueue(object: Callback {
+
+ override fun onFailure(call: Call, t: Throwable) {
+ output?.presentStatementsFailed(if (t.message != null) t.message!! else "Erro")
+ }
+
+ override fun onResponse(call: Call, response: Response) {
+ val statementsResponse = response.body()
+ if (statementsResponse != null) {
+ output?.presentStatementsResponse(statementsResponse)
+ } else {
+ output?.presentStatementsFailed(response.message())
+ }
+ }
+ })
+
+ } else {
+ output?.presentStatementsFailed("User id nulo")
+ }
+
+
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/br/com/cauejannini/testesantander/statements/StatementsModels.kt b/app/src/main/java/br/com/cauejannini/testesantander/statements/StatementsModels.kt
new file mode 100644
index 000000000..7a53508c4
--- /dev/null
+++ b/app/src/main/java/br/com/cauejannini/testesantander/statements/StatementsModels.kt
@@ -0,0 +1,34 @@
+package br.com.cauejannini.testesantander.statements
+
+data class StatementsRequestModel(val userId: String) {}
+
+class StatementsResponseModel {
+
+ var statementList: List? = null
+ var error: Any? = null
+}
+
+
+class UserDataModel {
+
+ var userName: String? = null
+ var agenciaConta: String? = null
+ var saldo: String? = null
+}
+
+class Statement {
+
+ constructor() {}
+
+ constructor(title: String?, desc: String?, date: String?, value: Double?) {
+ this.title = title
+ this.desc = desc
+ this.date = date
+ this.value = value
+ }
+
+ var title: String? = null
+ var desc: String? = null
+ var date: String? = null
+ var value: Double? = null
+}
\ No newline at end of file
diff --git a/app/src/main/java/br/com/cauejannini/testesantander/statements/StatementsPresenter.kt b/app/src/main/java/br/com/cauejannini/testesantander/statements/StatementsPresenter.kt
new file mode 100644
index 000000000..09dc4e5db
--- /dev/null
+++ b/app/src/main/java/br/com/cauejannini/testesantander/statements/StatementsPresenter.kt
@@ -0,0 +1,53 @@
+package br.com.cauejannini.testesantander.statements
+
+import br.com.cauejannini.testesantander.commons.Utils
+import br.com.cauejannini.testesantander.login.UserAccount
+import java.lang.ref.WeakReference
+
+interface StatementsPresenterInput {
+ fun presentStatementsResponse(statementsResponse: StatementsResponseModel)
+ fun presentStatementsFailed(message: String)
+ fun presentUserAccount (userAccount: UserAccount?)
+}
+
+class StatementsPresenter: StatementsPresenterInput {
+
+ var output: WeakReference? = null
+
+ override fun presentStatementsResponse(statementsResponse: StatementsResponseModel) {
+ val statementList = statementsResponse.statementList
+ if (statementList != null) {
+ output?.get()?.displayStatements(statementList)
+ } else {
+ output?.get()?.displayStatementsError(statementsResponse.error.toString())
+ }
+ }
+
+ override fun presentStatementsFailed(message: String) {
+ output?.get()?.displayStatementsError(message)
+ }
+
+ override fun presentUserAccount(userAccount: UserAccount?) {
+
+ val userDataModel = UserDataModel()
+
+ if (userAccount != null) {
+
+ userDataModel.userName = if (userAccount.name != null) userAccount.name else "-"
+ val agencia = if (userAccount.agency != null) userAccount.agency else "-"
+ val conta = if (userAccount.bankAccount != null) userAccount.bankAccount else "-"
+ userDataModel.agenciaConta = "$conta / $agencia"
+
+ val saldo = if (userAccount.balance != null) Utils.currencyBrazil(userAccount.balance) else "-"
+ userDataModel.saldo = saldo
+
+ } else {
+ userDataModel.userName = "-"
+ userDataModel.agenciaConta = "- / -"
+ userDataModel.saldo = "-"
+ }
+
+ output?.get()?.displayUserData(userDataModel)
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/br/com/cauejannini/testesantander/statements/StatementsRecyclerViewAdapter.kt b/app/src/main/java/br/com/cauejannini/testesantander/statements/StatementsRecyclerViewAdapter.kt
new file mode 100644
index 000000000..110e109d2
--- /dev/null
+++ b/app/src/main/java/br/com/cauejannini/testesantander/statements/StatementsRecyclerViewAdapter.kt
@@ -0,0 +1,42 @@
+package br.com.cauejannini.testesantander.statements
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.recyclerview.widget.RecyclerView
+import br.com.cauejannini.testesantander.R
+import br.com.cauejannini.testesantander.commons.Utils
+import kotlinx.android.synthetic.main.list_item_statement.view.*
+
+class StatementsRecyclerViewAdapter(val statementList: List):
+ RecyclerView.Adapter() {
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): StatementItemViewHolder {
+ val view = LayoutInflater.from(parent.context).inflate(R.layout.list_item_statement, parent, false)
+ return StatementItemViewHolder(view)
+ }
+
+ override fun getItemCount(): Int {
+ return statementList.size
+ }
+
+ override fun onBindViewHolder(holder: StatementItemViewHolder, position: Int) {
+ val statement = statementList.get(position)
+ holder.tvTitle.text = statement.title
+ holder.tvDate.text = Utils.dataApiParaApp(statement.date)
+ holder.tvDesc.text = statement.desc
+
+ val value = Utils.doisDecimais(statement.value)
+ holder.tvValue.text = "R$ $value"
+ }
+
+ class StatementItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
+
+ val tvTitle = itemView.tvTitle
+ val tvDesc = itemView.tvDesc
+ val tvDate = itemView.tvDate
+ val tvValue = itemView.tvValue
+
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/res/drawable/app_button.xml b/app/src/main/res/drawable/app_button.xml
new file mode 100644
index 000000000..3ed3d526c
--- /dev/null
+++ b/app/src/main/res/drawable/app_button.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/app_button_disabled.xml b/app/src/main/res/drawable/app_button_disabled.xml
new file mode 100644
index 000000000..dfa1270df
--- /dev/null
+++ b/app/src/main/res/drawable/app_button_disabled.xml
@@ -0,0 +1,11 @@
+
+
+
+ -
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/app_button_np.xml b/app/src/main/res/drawable/app_button_np.xml
new file mode 100644
index 000000000..2c6f71bca
--- /dev/null
+++ b/app/src/main/res/drawable/app_button_np.xml
@@ -0,0 +1,11 @@
+
+
+
+ -
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/app_button_p.xml b/app/src/main/res/drawable/app_button_p.xml
new file mode 100644
index 000000000..7fc07bd3c
--- /dev/null
+++ b/app/src/main/res/drawable/app_button_p.xml
@@ -0,0 +1,11 @@
+
+
+
+ -
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 000000000..07d5da9cb
--- /dev/null
+++ b/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml
new file mode 100644
index 000000000..2b068d114
--- /dev/null
+++ b/app/src/main/res/drawable/ic_launcher_foreground.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/bank_app_layout/assets/logout 2.png b/app/src/main/res/drawable/ic_logout.png
similarity index 100%
rename from bank_app_layout/assets/logout 2.png
rename to app/src/main/res/drawable/ic_logout.png
diff --git a/app/src/main/res/drawable/input_text_bg.xml b/app/src/main/res/drawable/input_text_bg.xml
new file mode 100644
index 000000000..4579d8b2b
--- /dev/null
+++ b/app/src/main/res/drawable/input_text_bg.xml
@@ -0,0 +1,12 @@
+
+
+
+ -
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/item_list_bg.xml b/app/src/main/res/drawable/item_list_bg.xml
new file mode 100644
index 000000000..f26b435a5
--- /dev/null
+++ b/app/src/main/res/drawable/item_list_bg.xml
@@ -0,0 +1,11 @@
+
+
+
+ -
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/bank_app_layout/assets/Logo.png b/app/src/main/res/drawable/logo.png
similarity index 100%
rename from bank_app_layout/assets/Logo.png
rename to app/src/main/res/drawable/logo.png
diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml
new file mode 100644
index 000000000..3f21c564f
--- /dev/null
+++ b/app/src/main/res/layout/activity_login.xml
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_statements.xml b/app/src/main/res/layout/activity_statements.xml
new file mode 100644
index 000000000..aa5006224
--- /dev/null
+++ b/app/src/main/res/layout/activity_statements.xml
@@ -0,0 +1,101 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/app_button.xml b/app/src/main/res/layout/app_button.xml
new file mode 100644
index 000000000..a2a4beec2
--- /dev/null
+++ b/app/src/main/res/layout/app_button.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/field_input_text.xml b/app/src/main/res/layout/field_input_text.xml
new file mode 100644
index 000000000..1b890f94c
--- /dev/null
+++ b/app/src/main/res/layout/field_input_text.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/list_item_statement.xml b/app/src/main/res/layout/list_item_statement.xml
new file mode 100644
index 000000000..ddfd0fe9a
--- /dev/null
+++ b/app/src/main/res/layout/list_item_statement.xml
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 000000000..eca70cfe5
--- /dev/null
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 000000000..eca70cfe5
--- /dev/null
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 000000000..a571e6009
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 000000000..61da551c5
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 000000000..c41dd2853
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 000000000..db5080a75
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..6dba46dab
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 000000000..da31a871c
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 000000000..15ac68172
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 000000000..b216f2d31
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 000000000..f25a41974
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 000000000..e96783ccc
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml
new file mode 100644
index 000000000..fc4dadfd6
--- /dev/null
+++ b/app/src/main/res/values/attrs.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
new file mode 100644
index 000000000..ddfec5f79
--- /dev/null
+++ b/app/src/main/res/values/colors.xml
@@ -0,0 +1,17 @@
+
+
+ #111
+ #000
+ #3434ec
+ #5454ff
+ #333
+
+ #bbb
+ #555
+ #fff
+ #f9f9f9
+ #7fff
+ #777
+ #ddd
+ #f66
+
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
new file mode 100644
index 000000000..37b5b24bd
--- /dev/null
+++ b/app/src/main/res/values/dimens.xml
@@ -0,0 +1,15 @@
+
+
+ 8dp
+ 16dp
+ 32dp
+ 48dp
+
+ 12sp
+ 16sp
+ 24sp
+
+ 4dp
+ 1dp
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
new file mode 100644
index 000000000..52ff143dc
--- /dev/null
+++ b/app/src/main/res/values/strings.xml
@@ -0,0 +1,9 @@
+
+ Teste Santander
+ Login
+ User
+ Password
+ Conta
+ Saldo
+ Recentes
+
\ No newline at end of file
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
new file mode 100644
index 000000000..eec57e753
--- /dev/null
+++ b/app/src/main/res/values/styles.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/test/java/br/com/cauejannini/testesantander/LoginActivityUnitTest.kt b/app/src/test/java/br/com/cauejannini/testesantander/LoginActivityUnitTest.kt
new file mode 100644
index 000000000..03dbad7e7
--- /dev/null
+++ b/app/src/test/java/br/com/cauejannini/testesantander/LoginActivityUnitTest.kt
@@ -0,0 +1,177 @@
+package br.com.cauejannini.testesantander
+
+import android.content.Context
+import android.content.Intent
+import android.os.Build
+import br.com.cauejannini.testesantander.commons.form.AppButton
+import br.com.cauejannini.testesantander.commons.form.InputTextField
+import br.com.cauejannini.testesantander.commons.form.validators.PasswordValidator
+import br.com.cauejannini.testesantander.commons.form.validators.UsernameValidator
+import br.com.cauejannini.testesantander.login.LoginActivity
+import br.com.cauejannini.testesantander.login.LoginInteractorInput
+import br.com.cauejannini.testesantander.login.LoginRequest
+import br.com.cauejannini.testesantander.login.UserAccount
+import br.com.cauejannini.testesantander.statements.StatementsActivity
+import org.junit.Assert
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.Robolectric
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.RuntimeEnvironment
+import org.robolectric.Shadows
+import org.robolectric.annotation.Config
+
+@Config(sdk = [Build.VERSION_CODES.O_MR1])
+@RunWith(RobolectricTestRunner::class)
+class LoginActivityUnitTest {
+
+ @Test
+ fun loginActivity_shouldNot_beNull() {
+ val activity = Robolectric.setupActivity(LoginActivity::class.java)
+
+ Assert.assertNotNull(activity)
+ }
+
+ @Test
+ fun onCreate_shouldAdd_inputValidation() {
+
+ val activity = Robolectric.setupActivity(LoginActivity::class.java)
+
+ val inputUser = activity.findViewById(R.id.inputUser)
+ val inputPassword = activity.findViewById(R.id.inputPassword)
+
+ Assert.assertTrue(inputUser.myValidator is UsernameValidator)
+ Assert.assertTrue(inputPassword.myValidator is PasswordValidator)
+ }
+
+ @Test
+ fun inputUser_shouldOnly_acceptValidUsers() {
+ val activity = Robolectric.setupActivity(LoginActivity::class.java)
+
+ val inputUser = activity.findViewById(R.id.inputUser)
+
+ // Emails inválidos
+ inputUser.text = ""
+ Assert.assertFalse(inputUser.isValid)
+
+ inputUser.text = "a"
+ Assert.assertFalse(inputUser.isValid)
+
+ inputUser.text = "abcdefg123"
+ Assert.assertFalse(inputUser.isValid)
+
+ inputUser.text = "abcdefg123@domain"
+ Assert.assertFalse(inputUser.isValid)
+
+ inputUser.text = "abcdefg123@domain."
+ Assert.assertFalse(inputUser.isValid)
+
+ // Email válido
+ inputUser.text = "abcdefg123@domain.com"
+ Assert.assertTrue(inputUser.isValid)
+
+ // CPFs inválidos
+ inputUser.text = "406361858"
+ Assert.assertFalse(inputUser.isValid)
+
+ inputUser.text = "40636185875"
+ Assert.assertFalse(inputUser.isValid)
+
+ // CPFs válido
+ inputUser.text = "40636185877"
+ Assert.assertTrue(inputUser.isValid)
+
+ }
+
+ @Test
+ fun inputPassword_shouldOnly_acceptValidPasswords() {
+ val activity = Robolectric.setupActivity(LoginActivity::class.java)
+
+ val inputPassword = activity.findViewById(R.id.inputPassword)
+
+ // Passwords inválidos
+ inputPassword.text = ""
+ Assert.assertFalse(inputPassword.isValid)
+
+ inputPassword.text = "a"
+ Assert.assertFalse(inputPassword.isValid)
+
+ inputPassword.text = "a1"
+ Assert.assertFalse(inputPassword.isValid)
+
+ inputPassword.text = "a1@"
+ Assert.assertFalse(inputPassword.isValid)
+
+ // Password válido
+ inputPassword.text = "a1@B"
+ Assert.assertTrue(inputPassword.isValid)
+
+ }
+
+ @Test
+ fun btLoginClick_callsLogin_withCorrectData() {
+
+ val activity = Robolectric.setupActivity(LoginActivity::class.java)
+ val outputSpy = LoginActivityOutputSpy()
+ activity.output = outputSpy
+
+ val inputUser = activity.findViewById(R.id.inputUser)
+ val inputPassword = activity.findViewById(R.id.inputPassword)
+ val btLogin = activity.findViewById(R.id.btLogin)
+
+ inputUser.text = "user@domain.com"
+ inputPassword.text = "b2#C"
+
+ btLogin.performClick()
+
+ // Assert login is called in spy
+ Assert.assertTrue(outputSpy.loginCalled)
+
+ // Assert request passed is not null and correct
+ Assert.assertNotNull(outputSpy.loginRequestCopy)
+ Assert.assertTrue(outputSpy.loginRequestCopy.user == inputUser.text)
+ Assert.assertTrue(outputSpy.loginRequestCopy.password == inputPassword.text)
+ }
+
+ @Test
+ fun successfullLogin_shouldCall_statementsActivityWithIntent() {
+ val activity = Robolectric.setupActivity(LoginActivity::class.java)
+
+ val userAccount = UserAccount()
+ userAccount.userId = 1
+ userAccount.name = "Lucio dos Santos Teste"
+ userAccount.agency = "1234"
+ userAccount.bankAccount = "567890"
+ userAccount.balance = 990.34
+
+ activity.onLoggedIn(userAccount)
+
+ val intentEsperado = Intent(activity, StatementsActivity::class.java)
+ intentEsperado.putExtra(StatementsActivity.EXTRA_KEY_USER_ACCOUNT, userAccount)
+
+ val intentEnviado = Shadows.shadowOf(RuntimeEnvironment.application).nextStartedActivity
+
+ // Assert redirecting to correct Activity
+ Assert.assertEquals(intentEnviado.component?.className, StatementsActivity::class.java.name)
+
+ // Assert that has added extra with correct key
+ Assert.assertTrue(intentEnviado.hasExtra(StatementsActivity.EXTRA_KEY_USER_ACCOUNT))
+
+ // Assert that extra type is correct
+ Assert.assertTrue(intentEnviado.getSerializableExtra(StatementsActivity.EXTRA_KEY_USER_ACCOUNT) is UserAccount)
+
+ }
+
+ private class LoginActivityOutputSpy: LoginInteractorInput {
+
+ var loginCalled = false
+ lateinit var loginRequestCopy: LoginRequest
+
+ override fun login(request: LoginRequest) {
+ loginCalled = true
+ loginRequestCopy = request
+
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/test/java/br/com/cauejannini/testesantander/LoginPresenterUnitTest.kt b/app/src/test/java/br/com/cauejannini/testesantander/LoginPresenterUnitTest.kt
new file mode 100644
index 000000000..c2d3444e4
--- /dev/null
+++ b/app/src/test/java/br/com/cauejannini/testesantander/LoginPresenterUnitTest.kt
@@ -0,0 +1,108 @@
+package br.com.cauejannini.testesantander
+
+import android.os.Build
+import br.com.cauejannini.testesantander.login.*
+import org.junit.Assert
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.annotation.Config
+import java.lang.ref.WeakReference
+
+@Config(sdk = [Build.VERSION_CODES.O_MR1])
+@RunWith(RobolectricTestRunner::class)
+class LoginPresenterUnitTest {
+
+ @Test
+ fun present_correctData_onLoginSuccessfull() {
+
+ val loginPresenter = LoginPresenter()
+ val outputSpy = LoginActivityInputSpy()
+ loginPresenter.output = WeakReference(outputSpy)
+
+ val loginResponseModel = LoginResponseModel()
+
+ val userAccount = UserAccount()
+ userAccount.userId = 1
+ userAccount.name = "Lucio dos Santos Teste"
+ userAccount.agency = "1234"
+ userAccount.bankAccount = "567890"
+ userAccount.balance = 990.34
+
+ loginResponseModel.userAccount = userAccount
+ loginResponseModel.error = null
+
+ loginPresenter.onLoginResponse(loginResponseModel)
+
+ Assert.assertTrue(outputSpy.onLoggedInCalled)
+ Assert.assertEquals(outputSpy.userAccountCopy, userAccount)
+
+ Assert.assertFalse(outputSpy.onLoginErrorCalled)
+ Assert.assertNull(outputSpy.errorMessageCopy)
+
+ }
+
+ @Test
+ fun present_correctData_onLoginResponseError() {
+
+ val loginPresenter = LoginPresenter()
+ val outputSpy = LoginActivityInputSpy()
+ loginPresenter.output = WeakReference(outputSpy)
+
+ val loginResponseModel = LoginResponseModel()
+
+ loginResponseModel.userAccount = null
+
+ val errorMessage = "Erro retornado no LoginResponseModel"
+ loginResponseModel.error = errorMessage
+
+ loginPresenter.onLoginResponse(loginResponseModel)
+
+ Assert.assertTrue(outputSpy.onLoginErrorCalled)
+ Assert.assertEquals(outputSpy.errorMessageCopy, errorMessage)
+
+ Assert.assertFalse(outputSpy.onLoggedInCalled)
+ Assert.assertNull(outputSpy.userAccountCopy)
+
+ }
+
+ @Test
+ fun present_correctData_onLoginFailed() {
+
+ val loginPresenter = LoginPresenter()
+ val outputSpy = LoginActivityInputSpy()
+ loginPresenter.output = WeakReference(outputSpy)
+
+ val errorMessage = "Erro de integração"
+
+ loginPresenter.onLoginFailed(errorMessage)
+
+ Assert.assertTrue(outputSpy.onLoginErrorCalled)
+ Assert.assertEquals(outputSpy.errorMessageCopy, errorMessage)
+
+ Assert.assertFalse(outputSpy.onLoggedInCalled)
+ Assert.assertNull(outputSpy.userAccountCopy)
+
+ }
+
+ private class LoginActivityInputSpy: LoginActivityInput {
+
+ var onLoggedInCalled = false
+ var userAccountCopy: UserAccount? = null
+
+ var onLoginErrorCalled = false
+ var errorMessageCopy: String? = null
+
+ override fun onLoggedIn(userAccount: UserAccount) {
+ onLoggedInCalled = true
+ userAccountCopy = userAccount
+ }
+
+ override fun onLoginError(message: String) {
+ onLoginErrorCalled = true
+ errorMessageCopy = message
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/test/java/br/com/cauejannini/testesantander/StatementsActivityUnitTest.kt b/app/src/test/java/br/com/cauejannini/testesantander/StatementsActivityUnitTest.kt
new file mode 100644
index 000000000..9dcaa51b6
--- /dev/null
+++ b/app/src/test/java/br/com/cauejannini/testesantander/StatementsActivityUnitTest.kt
@@ -0,0 +1,70 @@
+package br.com.cauejannini.testesantander
+
+import android.content.Context
+import android.content.Intent
+import android.os.Build
+import br.com.cauejannini.testesantander.commons.form.AppButton
+import br.com.cauejannini.testesantander.commons.form.InputTextField
+import br.com.cauejannini.testesantander.commons.form.validators.PasswordValidator
+import br.com.cauejannini.testesantander.commons.form.validators.UsernameValidator
+import br.com.cauejannini.testesantander.login.LoginActivity
+import br.com.cauejannini.testesantander.login.LoginInteractorInput
+import br.com.cauejannini.testesantander.login.LoginRequest
+import br.com.cauejannini.testesantander.login.UserAccount
+import br.com.cauejannini.testesantander.statements.StatementsActivity
+import br.com.cauejannini.testesantander.statements.StatementsInteractorInput
+import org.junit.Assert
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.Robolectric
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.RuntimeEnvironment
+import org.robolectric.Shadows
+import org.robolectric.annotation.Config
+
+@Config(sdk = [Build.VERSION_CODES.O_MR1])
+@RunWith(RobolectricTestRunner::class)
+class StatementsActivityUnitTest {
+
+ @Test
+ fun statementsActivity_shouldNot_beNull() {
+ val activity = Robolectric.setupActivity(StatementsActivity::class.java)
+
+ Assert.assertNotNull(activity)
+ }
+
+ @Test
+ fun onCreate_fetchStatements_withCorrectData() {
+
+ val activity = Robolectric.setupActivity(StatementsActivity::class.java)
+ val outputSpy = StatementsInteractorInputSpy()
+ activity.output = outputSpy
+
+ val userAccount = UserAccount()
+ userAccount.userId = 1
+ userAccount.name = "Lucio dos Santos Teste"
+ userAccount.agency = "1234"
+ userAccount.bankAccount = "567890"
+ userAccount.balance = 990.34
+
+ activity.fetchStatements(userAccount)
+
+ // Assert login is called in spy
+ Assert.assertTrue(outputSpy.fetchStatementsCalled)
+
+ // Assert request passed is not null and correct
+ Assert.assertEquals(outputSpy.userAccountCopy, userAccount)
+ }
+
+ private class StatementsInteractorInputSpy: StatementsInteractorInput {
+
+ var fetchStatementsCalled = false
+ var userAccountCopy: UserAccount? = null
+
+ override fun fetchStatements(userAccount: UserAccount?) {
+ fetchStatementsCalled = true
+ userAccountCopy = userAccount
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/test/java/br/com/cauejannini/testesantander/StatementsPresenterUnitTest.kt b/app/src/test/java/br/com/cauejannini/testesantander/StatementsPresenterUnitTest.kt
new file mode 100644
index 000000000..bec9d3f7a
--- /dev/null
+++ b/app/src/test/java/br/com/cauejannini/testesantander/StatementsPresenterUnitTest.kt
@@ -0,0 +1,139 @@
+package br.com.cauejannini.testesantander
+
+import android.os.Build
+import br.com.cauejannini.testesantander.commons.Utils
+import br.com.cauejannini.testesantander.login.*
+import br.com.cauejannini.testesantander.statements.*
+import org.junit.Assert
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.annotation.Config
+import java.lang.ref.WeakReference
+
+@Config(sdk = [Build.VERSION_CODES.O_MR1])
+@RunWith(RobolectricTestRunner::class)
+class StatementsPresenterUnitTest {
+
+ @Test
+ fun present_correctData_onGetStatementsSuccessfull() {
+
+ val presenter = StatementsPresenter()
+ val outputSpy = StatementsActivityInputSpy()
+ presenter.output = WeakReference(outputSpy)
+
+ val responseModel = StatementsResponseModel()
+
+ val list = ArrayList()
+ list.add(Statement("Crédito", "Transferência TED", "2020-05-30", 120.50))
+ list.add(Statement("Débito", "Supermercado", "2020-05-29", 80.0))
+ list.add(Statement("Débito", "Padaria", "2020-05-28", 30.90))
+
+ responseModel.statementList = list
+ responseModel.error = null
+
+ presenter.presentStatementsResponse(responseModel)
+
+ Assert.assertTrue(outputSpy.displayStatementsCalled)
+ Assert.assertEquals(outputSpy.statementsListCopy, list)
+
+ Assert.assertFalse(outputSpy.displayStatementsErrorCalled)
+ Assert.assertNull(outputSpy.statementsErrorMessageCopy)
+
+ }
+
+ @Test
+ fun present_correctData_onGetStatementsResponseError() {
+
+ val presenter = StatementsPresenter()
+ val outputSpy = StatementsActivityInputSpy()
+ presenter.output = WeakReference(outputSpy)
+
+ val responseModel = StatementsResponseModel()
+ responseModel.statementList = null
+ val errorMessage = "Erro devolvido pelo StatementsResponse"
+ responseModel.error = errorMessage
+
+ presenter.presentStatementsResponse(responseModel)
+
+ Assert.assertTrue(outputSpy.displayStatementsErrorCalled)
+ Assert.assertEquals(outputSpy.statementsErrorMessageCopy, errorMessage)
+
+ Assert.assertFalse(outputSpy.displayStatementsCalled)
+ Assert.assertNull(outputSpy.statementsListCopy)
+
+ }
+
+ @Test
+ fun present_correctData_onGetStatementsError() {
+
+ val presenter = StatementsPresenter()
+ val outputSpy = StatementsActivityInputSpy()
+ presenter.output = WeakReference(outputSpy)
+
+ val errorMessage = "Erro de integração"
+
+ presenter.presentStatementsFailed(errorMessage)
+
+ Assert.assertTrue(outputSpy.displayStatementsErrorCalled)
+ Assert.assertEquals(outputSpy.statementsErrorMessageCopy, errorMessage)
+
+ Assert.assertFalse(outputSpy.displayStatementsCalled)
+ Assert.assertNull(outputSpy.statementsListCopy)
+
+ }
+
+ @Test
+ fun present_userData_withCorrectData() {
+
+ val presenter = StatementsPresenter()
+ val outputSpy = StatementsActivityInputSpy()
+ presenter.output = WeakReference(outputSpy)
+
+ val userAccount = UserAccount()
+ userAccount.name = "João da Silva"
+ userAccount.balance = 120.3
+ userAccount.bankAccount = "1234"
+ userAccount.agency = "567890"
+ userAccount.userId = 1
+
+ presenter.presentUserAccount(userAccount)
+
+ Assert.assertTrue(outputSpy.displayUserDataCalled)
+ Assert.assertEquals(outputSpy.userDataModelCopy.userName, userAccount.name)
+ Assert.assertEquals(outputSpy.userDataModelCopy.saldo, Utils.currencyBrazil(userAccount.balance))
+
+ val conta = userAccount.bankAccount
+ val agencia = userAccount.agency
+ Assert.assertEquals(outputSpy.userDataModelCopy.agenciaConta, "$conta / $agencia")
+
+ }
+
+ private class StatementsActivityInputSpy: StatementsActivityInput {
+
+ var displayStatementsCalled = false
+ var displayStatementsErrorCalled = false
+ var displayUserDataCalled = false
+
+ var statementsListCopy: List? = null
+ var statementsErrorMessageCopy: String? = null
+ lateinit var userDataModelCopy: UserDataModel
+
+ override fun displayStatements(statements: List) {
+ displayStatementsCalled = true
+ statementsListCopy = statements
+ }
+
+ override fun displayStatementsError(message: String) {
+ displayStatementsErrorCalled = true
+ statementsErrorMessageCopy = message
+ }
+
+ override fun displayUserData(userDataModel: UserDataModel) {
+ displayUserDataCalled = true
+ userDataModelCopy = userDataModel
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/bank_app.sketch b/bank_app.sketch
deleted file mode 100644
index d4ed8cafc..000000000
Binary files a/bank_app.sketch and /dev/null differ
diff --git a/bank_app_layout/83F6DA3D-9F1B-4816-92B9-A74AAED40206/3817562E-CBB6-4CAD-BBCF-FD468BAA446F@0.5x.png b/bank_app_layout/83F6DA3D-9F1B-4816-92B9-A74AAED40206/3817562E-CBB6-4CAD-BBCF-FD468BAA446F@0.5x.png
deleted file mode 100644
index 53a1cdeb7..000000000
Binary files a/bank_app_layout/83F6DA3D-9F1B-4816-92B9-A74AAED40206/3817562E-CBB6-4CAD-BBCF-FD468BAA446F@0.5x.png and /dev/null differ
diff --git a/bank_app_layout/83F6DA3D-9F1B-4816-92B9-A74AAED40206/3817562E-CBB6-4CAD-BBCF-FD468BAA446F@1x.png b/bank_app_layout/83F6DA3D-9F1B-4816-92B9-A74AAED40206/3817562E-CBB6-4CAD-BBCF-FD468BAA446F@1x.png
deleted file mode 100644
index fb33afda6..000000000
Binary files a/bank_app_layout/83F6DA3D-9F1B-4816-92B9-A74AAED40206/3817562E-CBB6-4CAD-BBCF-FD468BAA446F@1x.png and /dev/null differ
diff --git a/bank_app_layout/83F6DA3D-9F1B-4816-92B9-A74AAED40206/3817562E-CBB6-4CAD-BBCF-FD468BAA446F@2x.png b/bank_app_layout/83F6DA3D-9F1B-4816-92B9-A74AAED40206/3817562E-CBB6-4CAD-BBCF-FD468BAA446F@2x.png
deleted file mode 100644
index 5f7313021..000000000
Binary files a/bank_app_layout/83F6DA3D-9F1B-4816-92B9-A74AAED40206/3817562E-CBB6-4CAD-BBCF-FD468BAA446F@2x.png and /dev/null differ
diff --git a/bank_app_layout/83F6DA3D-9F1B-4816-92B9-A74AAED40206/3817562E-CBB6-4CAD-BBCF-FD468BAA446F@3x.png b/bank_app_layout/83F6DA3D-9F1B-4816-92B9-A74AAED40206/3817562E-CBB6-4CAD-BBCF-FD468BAA446F@3x.png
deleted file mode 100644
index 1eda7465e..000000000
Binary files a/bank_app_layout/83F6DA3D-9F1B-4816-92B9-A74AAED40206/3817562E-CBB6-4CAD-BBCF-FD468BAA446F@3x.png and /dev/null differ
diff --git a/bank_app_layout/83F6DA3D-9F1B-4816-92B9-A74AAED40206/B27D4452-A3F9-4F53-991A-9E914520B88A@0.5x.png b/bank_app_layout/83F6DA3D-9F1B-4816-92B9-A74AAED40206/B27D4452-A3F9-4F53-991A-9E914520B88A@0.5x.png
deleted file mode 100644
index b79e2ecfb..000000000
Binary files a/bank_app_layout/83F6DA3D-9F1B-4816-92B9-A74AAED40206/B27D4452-A3F9-4F53-991A-9E914520B88A@0.5x.png and /dev/null differ
diff --git a/bank_app_layout/83F6DA3D-9F1B-4816-92B9-A74AAED40206/B27D4452-A3F9-4F53-991A-9E914520B88A@1x.png b/bank_app_layout/83F6DA3D-9F1B-4816-92B9-A74AAED40206/B27D4452-A3F9-4F53-991A-9E914520B88A@1x.png
deleted file mode 100644
index b961132ed..000000000
Binary files a/bank_app_layout/83F6DA3D-9F1B-4816-92B9-A74AAED40206/B27D4452-A3F9-4F53-991A-9E914520B88A@1x.png and /dev/null differ
diff --git a/bank_app_layout/83F6DA3D-9F1B-4816-92B9-A74AAED40206/B27D4452-A3F9-4F53-991A-9E914520B88A@2x.png b/bank_app_layout/83F6DA3D-9F1B-4816-92B9-A74AAED40206/B27D4452-A3F9-4F53-991A-9E914520B88A@2x.png
deleted file mode 100644
index a9483ea0f..000000000
Binary files a/bank_app_layout/83F6DA3D-9F1B-4816-92B9-A74AAED40206/B27D4452-A3F9-4F53-991A-9E914520B88A@2x.png and /dev/null differ
diff --git a/bank_app_layout/83F6DA3D-9F1B-4816-92B9-A74AAED40206/B27D4452-A3F9-4F53-991A-9E914520B88A@3x.png b/bank_app_layout/83F6DA3D-9F1B-4816-92B9-A74AAED40206/B27D4452-A3F9-4F53-991A-9E914520B88A@3x.png
deleted file mode 100644
index af2a6f7bb..000000000
Binary files a/bank_app_layout/83F6DA3D-9F1B-4816-92B9-A74AAED40206/B27D4452-A3F9-4F53-991A-9E914520B88A@3x.png and /dev/null differ
diff --git a/bank_app_layout/83F6DA3D-9F1B-4816-92B9-A74AAED40206/CA2B476F-0F1E-42F9-B119-2A2B3380A4D4@0.5x.png b/bank_app_layout/83F6DA3D-9F1B-4816-92B9-A74AAED40206/CA2B476F-0F1E-42F9-B119-2A2B3380A4D4@0.5x.png
deleted file mode 100644
index 08ae82bd9..000000000
Binary files a/bank_app_layout/83F6DA3D-9F1B-4816-92B9-A74AAED40206/CA2B476F-0F1E-42F9-B119-2A2B3380A4D4@0.5x.png and /dev/null differ
diff --git a/bank_app_layout/83F6DA3D-9F1B-4816-92B9-A74AAED40206/CA2B476F-0F1E-42F9-B119-2A2B3380A4D4@1x.png b/bank_app_layout/83F6DA3D-9F1B-4816-92B9-A74AAED40206/CA2B476F-0F1E-42F9-B119-2A2B3380A4D4@1x.png
deleted file mode 100644
index 66948cabd..000000000
Binary files a/bank_app_layout/83F6DA3D-9F1B-4816-92B9-A74AAED40206/CA2B476F-0F1E-42F9-B119-2A2B3380A4D4@1x.png and /dev/null differ
diff --git a/bank_app_layout/83F6DA3D-9F1B-4816-92B9-A74AAED40206/CA2B476F-0F1E-42F9-B119-2A2B3380A4D4@2x.png b/bank_app_layout/83F6DA3D-9F1B-4816-92B9-A74AAED40206/CA2B476F-0F1E-42F9-B119-2A2B3380A4D4@2x.png
deleted file mode 100644
index 0d20e8026..000000000
Binary files a/bank_app_layout/83F6DA3D-9F1B-4816-92B9-A74AAED40206/CA2B476F-0F1E-42F9-B119-2A2B3380A4D4@2x.png and /dev/null differ
diff --git a/bank_app_layout/83F6DA3D-9F1B-4816-92B9-A74AAED40206/CA2B476F-0F1E-42F9-B119-2A2B3380A4D4@3x.png b/bank_app_layout/83F6DA3D-9F1B-4816-92B9-A74AAED40206/CA2B476F-0F1E-42F9-B119-2A2B3380A4D4@3x.png
deleted file mode 100644
index 8d42293ac..000000000
Binary files a/bank_app_layout/83F6DA3D-9F1B-4816-92B9-A74AAED40206/CA2B476F-0F1E-42F9-B119-2A2B3380A4D4@3x.png and /dev/null differ
diff --git a/bank_app_layout/83F6DA3D-9F1B-4816-92B9-A74AAED40206/artboard.png b/bank_app_layout/83F6DA3D-9F1B-4816-92B9-A74AAED40206/artboard.png
deleted file mode 100644
index 20c1aa4bb..000000000
Binary files a/bank_app_layout/83F6DA3D-9F1B-4816-92B9-A74AAED40206/artboard.png and /dev/null differ
diff --git a/bank_app_layout/CCA3949D-B416-466A-8949-425E5DCD35B5/39D04EF6-4A67-4ED6-A6AC-624922CFD8A1@0.5x.png b/bank_app_layout/CCA3949D-B416-466A-8949-425E5DCD35B5/39D04EF6-4A67-4ED6-A6AC-624922CFD8A1@0.5x.png
deleted file mode 100644
index fed7233bc..000000000
Binary files a/bank_app_layout/CCA3949D-B416-466A-8949-425E5DCD35B5/39D04EF6-4A67-4ED6-A6AC-624922CFD8A1@0.5x.png and /dev/null differ
diff --git a/bank_app_layout/CCA3949D-B416-466A-8949-425E5DCD35B5/39D04EF6-4A67-4ED6-A6AC-624922CFD8A1@1x.png b/bank_app_layout/CCA3949D-B416-466A-8949-425E5DCD35B5/39D04EF6-4A67-4ED6-A6AC-624922CFD8A1@1x.png
deleted file mode 100644
index 5899f020f..000000000
Binary files a/bank_app_layout/CCA3949D-B416-466A-8949-425E5DCD35B5/39D04EF6-4A67-4ED6-A6AC-624922CFD8A1@1x.png and /dev/null differ
diff --git a/bank_app_layout/CCA3949D-B416-466A-8949-425E5DCD35B5/39D04EF6-4A67-4ED6-A6AC-624922CFD8A1@2x.png b/bank_app_layout/CCA3949D-B416-466A-8949-425E5DCD35B5/39D04EF6-4A67-4ED6-A6AC-624922CFD8A1@2x.png
deleted file mode 100644
index 2315098f8..000000000
Binary files a/bank_app_layout/CCA3949D-B416-466A-8949-425E5DCD35B5/39D04EF6-4A67-4ED6-A6AC-624922CFD8A1@2x.png and /dev/null differ
diff --git a/bank_app_layout/CCA3949D-B416-466A-8949-425E5DCD35B5/39D04EF6-4A67-4ED6-A6AC-624922CFD8A1@3x.png b/bank_app_layout/CCA3949D-B416-466A-8949-425E5DCD35B5/39D04EF6-4A67-4ED6-A6AC-624922CFD8A1@3x.png
deleted file mode 100644
index e47f46018..000000000
Binary files a/bank_app_layout/CCA3949D-B416-466A-8949-425E5DCD35B5/39D04EF6-4A67-4ED6-A6AC-624922CFD8A1@3x.png and /dev/null differ
diff --git a/bank_app_layout/CCA3949D-B416-466A-8949-425E5DCD35B5/4F2CC5EF-170E-45CA-94D1-9BCD2FA4A805@0.5x.png b/bank_app_layout/CCA3949D-B416-466A-8949-425E5DCD35B5/4F2CC5EF-170E-45CA-94D1-9BCD2FA4A805@0.5x.png
deleted file mode 100644
index 242c3fdaf..000000000
Binary files a/bank_app_layout/CCA3949D-B416-466A-8949-425E5DCD35B5/4F2CC5EF-170E-45CA-94D1-9BCD2FA4A805@0.5x.png and /dev/null differ
diff --git a/bank_app_layout/CCA3949D-B416-466A-8949-425E5DCD35B5/4F2CC5EF-170E-45CA-94D1-9BCD2FA4A805@1x.png b/bank_app_layout/CCA3949D-B416-466A-8949-425E5DCD35B5/4F2CC5EF-170E-45CA-94D1-9BCD2FA4A805@1x.png
deleted file mode 100644
index 23ff4626f..000000000
Binary files a/bank_app_layout/CCA3949D-B416-466A-8949-425E5DCD35B5/4F2CC5EF-170E-45CA-94D1-9BCD2FA4A805@1x.png and /dev/null differ
diff --git a/bank_app_layout/CCA3949D-B416-466A-8949-425E5DCD35B5/4F2CC5EF-170E-45CA-94D1-9BCD2FA4A805@2x.png b/bank_app_layout/CCA3949D-B416-466A-8949-425E5DCD35B5/4F2CC5EF-170E-45CA-94D1-9BCD2FA4A805@2x.png
deleted file mode 100644
index 35d07a14f..000000000
Binary files a/bank_app_layout/CCA3949D-B416-466A-8949-425E5DCD35B5/4F2CC5EF-170E-45CA-94D1-9BCD2FA4A805@2x.png and /dev/null differ
diff --git a/bank_app_layout/CCA3949D-B416-466A-8949-425E5DCD35B5/4F2CC5EF-170E-45CA-94D1-9BCD2FA4A805@3x.png b/bank_app_layout/CCA3949D-B416-466A-8949-425E5DCD35B5/4F2CC5EF-170E-45CA-94D1-9BCD2FA4A805@3x.png
deleted file mode 100644
index 97b68a58f..000000000
Binary files a/bank_app_layout/CCA3949D-B416-466A-8949-425E5DCD35B5/4F2CC5EF-170E-45CA-94D1-9BCD2FA4A805@3x.png and /dev/null differ
diff --git a/bank_app_layout/CCA3949D-B416-466A-8949-425E5DCD35B5/68A7CB4F-8C7B-480B-B544-271AB428EE8A@0.5x.png b/bank_app_layout/CCA3949D-B416-466A-8949-425E5DCD35B5/68A7CB4F-8C7B-480B-B544-271AB428EE8A@0.5x.png
deleted file mode 100644
index 117de969e..000000000
Binary files a/bank_app_layout/CCA3949D-B416-466A-8949-425E5DCD35B5/68A7CB4F-8C7B-480B-B544-271AB428EE8A@0.5x.png and /dev/null differ
diff --git a/bank_app_layout/CCA3949D-B416-466A-8949-425E5DCD35B5/68A7CB4F-8C7B-480B-B544-271AB428EE8A@1x.png b/bank_app_layout/CCA3949D-B416-466A-8949-425E5DCD35B5/68A7CB4F-8C7B-480B-B544-271AB428EE8A@1x.png
deleted file mode 100644
index 5ff77a675..000000000
Binary files a/bank_app_layout/CCA3949D-B416-466A-8949-425E5DCD35B5/68A7CB4F-8C7B-480B-B544-271AB428EE8A@1x.png and /dev/null differ
diff --git a/bank_app_layout/CCA3949D-B416-466A-8949-425E5DCD35B5/68A7CB4F-8C7B-480B-B544-271AB428EE8A@2x.png b/bank_app_layout/CCA3949D-B416-466A-8949-425E5DCD35B5/68A7CB4F-8C7B-480B-B544-271AB428EE8A@2x.png
deleted file mode 100644
index 73b2edbac..000000000
Binary files a/bank_app_layout/CCA3949D-B416-466A-8949-425E5DCD35B5/68A7CB4F-8C7B-480B-B544-271AB428EE8A@2x.png and /dev/null differ
diff --git a/bank_app_layout/CCA3949D-B416-466A-8949-425E5DCD35B5/68A7CB4F-8C7B-480B-B544-271AB428EE8A@3x.png b/bank_app_layout/CCA3949D-B416-466A-8949-425E5DCD35B5/68A7CB4F-8C7B-480B-B544-271AB428EE8A@3x.png
deleted file mode 100644
index beba6c0cf..000000000
Binary files a/bank_app_layout/CCA3949D-B416-466A-8949-425E5DCD35B5/68A7CB4F-8C7B-480B-B544-271AB428EE8A@3x.png and /dev/null differ
diff --git a/bank_app_layout/CCA3949D-B416-466A-8949-425E5DCD35B5/artboard.png b/bank_app_layout/CCA3949D-B416-466A-8949-425E5DCD35B5/artboard.png
deleted file mode 100644
index 9befa0cc5..000000000
Binary files a/bank_app_layout/CCA3949D-B416-466A-8949-425E5DCD35B5/artboard.png and /dev/null differ
diff --git a/bank_app_layout/D66AAA24-D61F-44D2-8D53-8B219D009DBF/05376719-28C6-4537-A0B4-097E3C72AB41@0.5x.png b/bank_app_layout/D66AAA24-D61F-44D2-8D53-8B219D009DBF/05376719-28C6-4537-A0B4-097E3C72AB41@0.5x.png
deleted file mode 100644
index 10ad360f4..000000000
Binary files a/bank_app_layout/D66AAA24-D61F-44D2-8D53-8B219D009DBF/05376719-28C6-4537-A0B4-097E3C72AB41@0.5x.png and /dev/null differ
diff --git a/bank_app_layout/D66AAA24-D61F-44D2-8D53-8B219D009DBF/05376719-28C6-4537-A0B4-097E3C72AB41@1x.png b/bank_app_layout/D66AAA24-D61F-44D2-8D53-8B219D009DBF/05376719-28C6-4537-A0B4-097E3C72AB41@1x.png
deleted file mode 100644
index 5fbb12417..000000000
Binary files a/bank_app_layout/D66AAA24-D61F-44D2-8D53-8B219D009DBF/05376719-28C6-4537-A0B4-097E3C72AB41@1x.png and /dev/null differ
diff --git a/bank_app_layout/D66AAA24-D61F-44D2-8D53-8B219D009DBF/05376719-28C6-4537-A0B4-097E3C72AB41@2x.png b/bank_app_layout/D66AAA24-D61F-44D2-8D53-8B219D009DBF/05376719-28C6-4537-A0B4-097E3C72AB41@2x.png
deleted file mode 100644
index fde4b9b75..000000000
Binary files a/bank_app_layout/D66AAA24-D61F-44D2-8D53-8B219D009DBF/05376719-28C6-4537-A0B4-097E3C72AB41@2x.png and /dev/null differ
diff --git a/bank_app_layout/D66AAA24-D61F-44D2-8D53-8B219D009DBF/05376719-28C6-4537-A0B4-097E3C72AB41@3x.png b/bank_app_layout/D66AAA24-D61F-44D2-8D53-8B219D009DBF/05376719-28C6-4537-A0B4-097E3C72AB41@3x.png
deleted file mode 100644
index af0e0ac72..000000000
Binary files a/bank_app_layout/D66AAA24-D61F-44D2-8D53-8B219D009DBF/05376719-28C6-4537-A0B4-097E3C72AB41@3x.png and /dev/null differ
diff --git a/bank_app_layout/D66AAA24-D61F-44D2-8D53-8B219D009DBF/372B9348-D602-4A3B-8E91-A66250670461@0.5x.png b/bank_app_layout/D66AAA24-D61F-44D2-8D53-8B219D009DBF/372B9348-D602-4A3B-8E91-A66250670461@0.5x.png
deleted file mode 100644
index ec991238a..000000000
Binary files a/bank_app_layout/D66AAA24-D61F-44D2-8D53-8B219D009DBF/372B9348-D602-4A3B-8E91-A66250670461@0.5x.png and /dev/null differ
diff --git a/bank_app_layout/D66AAA24-D61F-44D2-8D53-8B219D009DBF/372B9348-D602-4A3B-8E91-A66250670461@1x.png b/bank_app_layout/D66AAA24-D61F-44D2-8D53-8B219D009DBF/372B9348-D602-4A3B-8E91-A66250670461@1x.png
deleted file mode 100644
index 7307b9e13..000000000
Binary files a/bank_app_layout/D66AAA24-D61F-44D2-8D53-8B219D009DBF/372B9348-D602-4A3B-8E91-A66250670461@1x.png and /dev/null differ
diff --git a/bank_app_layout/D66AAA24-D61F-44D2-8D53-8B219D009DBF/372B9348-D602-4A3B-8E91-A66250670461@2x.png b/bank_app_layout/D66AAA24-D61F-44D2-8D53-8B219D009DBF/372B9348-D602-4A3B-8E91-A66250670461@2x.png
deleted file mode 100644
index 1178f890d..000000000
Binary files a/bank_app_layout/D66AAA24-D61F-44D2-8D53-8B219D009DBF/372B9348-D602-4A3B-8E91-A66250670461@2x.png and /dev/null differ
diff --git a/bank_app_layout/D66AAA24-D61F-44D2-8D53-8B219D009DBF/372B9348-D602-4A3B-8E91-A66250670461@3x.png b/bank_app_layout/D66AAA24-D61F-44D2-8D53-8B219D009DBF/372B9348-D602-4A3B-8E91-A66250670461@3x.png
deleted file mode 100644
index f0d400bea..000000000
Binary files a/bank_app_layout/D66AAA24-D61F-44D2-8D53-8B219D009DBF/372B9348-D602-4A3B-8E91-A66250670461@3x.png and /dev/null differ
diff --git a/bank_app_layout/D66AAA24-D61F-44D2-8D53-8B219D009DBF/D78AA366-D361-4FA8-AACE-0B91A0581654@0.5x.png b/bank_app_layout/D66AAA24-D61F-44D2-8D53-8B219D009DBF/D78AA366-D361-4FA8-AACE-0B91A0581654@0.5x.png
deleted file mode 100644
index 5066a058b..000000000
Binary files a/bank_app_layout/D66AAA24-D61F-44D2-8D53-8B219D009DBF/D78AA366-D361-4FA8-AACE-0B91A0581654@0.5x.png and /dev/null differ
diff --git a/bank_app_layout/D66AAA24-D61F-44D2-8D53-8B219D009DBF/D78AA366-D361-4FA8-AACE-0B91A0581654@1x.png b/bank_app_layout/D66AAA24-D61F-44D2-8D53-8B219D009DBF/D78AA366-D361-4FA8-AACE-0B91A0581654@1x.png
deleted file mode 100644
index 831e7bb47..000000000
Binary files a/bank_app_layout/D66AAA24-D61F-44D2-8D53-8B219D009DBF/D78AA366-D361-4FA8-AACE-0B91A0581654@1x.png and /dev/null differ
diff --git a/bank_app_layout/D66AAA24-D61F-44D2-8D53-8B219D009DBF/D78AA366-D361-4FA8-AACE-0B91A0581654@2x.png b/bank_app_layout/D66AAA24-D61F-44D2-8D53-8B219D009DBF/D78AA366-D361-4FA8-AACE-0B91A0581654@2x.png
deleted file mode 100644
index d857b805a..000000000
Binary files a/bank_app_layout/D66AAA24-D61F-44D2-8D53-8B219D009DBF/D78AA366-D361-4FA8-AACE-0B91A0581654@2x.png and /dev/null differ
diff --git a/bank_app_layout/D66AAA24-D61F-44D2-8D53-8B219D009DBF/D78AA366-D361-4FA8-AACE-0B91A0581654@3x.png b/bank_app_layout/D66AAA24-D61F-44D2-8D53-8B219D009DBF/D78AA366-D361-4FA8-AACE-0B91A0581654@3x.png
deleted file mode 100644
index d91b98e6d..000000000
Binary files a/bank_app_layout/D66AAA24-D61F-44D2-8D53-8B219D009DBF/D78AA366-D361-4FA8-AACE-0B91A0581654@3x.png and /dev/null differ
diff --git a/bank_app_layout/D66AAA24-D61F-44D2-8D53-8B219D009DBF/artboard.png b/bank_app_layout/D66AAA24-D61F-44D2-8D53-8B219D009DBF/artboard.png
deleted file mode 100644
index 2acdd074b..000000000
Binary files a/bank_app_layout/D66AAA24-D61F-44D2-8D53-8B219D009DBF/artboard.png and /dev/null differ
diff --git a/bank_app_layout/E822A087-B20C-42DD-8665-FE03A4AA8211/CDEBC6CD-73C4-47CB-B879-8A426D8163E1@0.5x.png b/bank_app_layout/E822A087-B20C-42DD-8665-FE03A4AA8211/CDEBC6CD-73C4-47CB-B879-8A426D8163E1@0.5x.png
deleted file mode 100644
index 535dafe15..000000000
Binary files a/bank_app_layout/E822A087-B20C-42DD-8665-FE03A4AA8211/CDEBC6CD-73C4-47CB-B879-8A426D8163E1@0.5x.png and /dev/null differ
diff --git a/bank_app_layout/E822A087-B20C-42DD-8665-FE03A4AA8211/CDEBC6CD-73C4-47CB-B879-8A426D8163E1@1x.png b/bank_app_layout/E822A087-B20C-42DD-8665-FE03A4AA8211/CDEBC6CD-73C4-47CB-B879-8A426D8163E1@1x.png
deleted file mode 100644
index 466ef1c09..000000000
Binary files a/bank_app_layout/E822A087-B20C-42DD-8665-FE03A4AA8211/CDEBC6CD-73C4-47CB-B879-8A426D8163E1@1x.png and /dev/null differ
diff --git a/bank_app_layout/E822A087-B20C-42DD-8665-FE03A4AA8211/CDEBC6CD-73C4-47CB-B879-8A426D8163E1@2x.png b/bank_app_layout/E822A087-B20C-42DD-8665-FE03A4AA8211/CDEBC6CD-73C4-47CB-B879-8A426D8163E1@2x.png
deleted file mode 100644
index cb118e91b..000000000
Binary files a/bank_app_layout/E822A087-B20C-42DD-8665-FE03A4AA8211/CDEBC6CD-73C4-47CB-B879-8A426D8163E1@2x.png and /dev/null differ
diff --git a/bank_app_layout/E822A087-B20C-42DD-8665-FE03A4AA8211/CDEBC6CD-73C4-47CB-B879-8A426D8163E1@3x.png b/bank_app_layout/E822A087-B20C-42DD-8665-FE03A4AA8211/CDEBC6CD-73C4-47CB-B879-8A426D8163E1@3x.png
deleted file mode 100644
index a650b8199..000000000
Binary files a/bank_app_layout/E822A087-B20C-42DD-8665-FE03A4AA8211/CDEBC6CD-73C4-47CB-B879-8A426D8163E1@3x.png and /dev/null differ
diff --git a/bank_app_layout/E822A087-B20C-42DD-8665-FE03A4AA8211/E3013759-B45A-4E98-8B34-278A50F98591@0.5x.png b/bank_app_layout/E822A087-B20C-42DD-8665-FE03A4AA8211/E3013759-B45A-4E98-8B34-278A50F98591@0.5x.png
deleted file mode 100644
index de8e4b18f..000000000
Binary files a/bank_app_layout/E822A087-B20C-42DD-8665-FE03A4AA8211/E3013759-B45A-4E98-8B34-278A50F98591@0.5x.png and /dev/null differ
diff --git a/bank_app_layout/E822A087-B20C-42DD-8665-FE03A4AA8211/E3013759-B45A-4E98-8B34-278A50F98591@1x.png b/bank_app_layout/E822A087-B20C-42DD-8665-FE03A4AA8211/E3013759-B45A-4E98-8B34-278A50F98591@1x.png
deleted file mode 100644
index d720a4f0d..000000000
Binary files a/bank_app_layout/E822A087-B20C-42DD-8665-FE03A4AA8211/E3013759-B45A-4E98-8B34-278A50F98591@1x.png and /dev/null differ
diff --git a/bank_app_layout/E822A087-B20C-42DD-8665-FE03A4AA8211/E3013759-B45A-4E98-8B34-278A50F98591@2x.png b/bank_app_layout/E822A087-B20C-42DD-8665-FE03A4AA8211/E3013759-B45A-4E98-8B34-278A50F98591@2x.png
deleted file mode 100644
index 9f57e7902..000000000
Binary files a/bank_app_layout/E822A087-B20C-42DD-8665-FE03A4AA8211/E3013759-B45A-4E98-8B34-278A50F98591@2x.png and /dev/null differ
diff --git a/bank_app_layout/E822A087-B20C-42DD-8665-FE03A4AA8211/E3013759-B45A-4E98-8B34-278A50F98591@3x.png b/bank_app_layout/E822A087-B20C-42DD-8665-FE03A4AA8211/E3013759-B45A-4E98-8B34-278A50F98591@3x.png
deleted file mode 100644
index fee69c198..000000000
Binary files a/bank_app_layout/E822A087-B20C-42DD-8665-FE03A4AA8211/E3013759-B45A-4E98-8B34-278A50F98591@3x.png and /dev/null differ
diff --git a/bank_app_layout/E822A087-B20C-42DD-8665-FE03A4AA8211/artboard.png b/bank_app_layout/E822A087-B20C-42DD-8665-FE03A4AA8211/artboard.png
deleted file mode 100644
index c620ef44d..000000000
Binary files a/bank_app_layout/E822A087-B20C-42DD-8665-FE03A4AA8211/artboard.png and /dev/null differ
diff --git a/bank_app_layout/data.js b/bank_app_layout/data.js
deleted file mode 100644
index a692702f5..000000000
--- a/bank_app_layout/data.js
+++ /dev/null
@@ -1 +0,0 @@
-var pageData = {"exportEveryLayer":1,"sketchName":"","language":"en","I18N":{"UNIT":"Unit","SIZE":"Size","SHOWSLICE":"Show slices","DRAGTOSAVE":"Drag to desktop to save.","WIDTH":"Width","HEIGHT":"Height","BORDER":"Border","COLOR":"Color","FILLCOLOR":"Fill","RADIUS":"Radius","LAYERTEXT":"Content","FONTSIZE":"Font Size","CODE":"Code","EXPORT":"Export","FORMAT":"Format","EXPORTLAYER":"Export Activity Layer","COPYSUCCESS":"Copy Success","TEXTSHAREDSTYLE":"Text Styles","SHAPESHAREDSTYLE":"Layer Styles"},"pageOrder":["E1DD8600-6E88-4482-9447-55D03DD87354","6774A219-5AFB-46B1-8AD2-8BE278B93378"],"pageData":{"E1DD8600-6E88-4482-9447-55D03DD87354":{"pageId":"E1DD8600-6E88-4482-9447-55D03DD87354","name":"Symbols","artboardId":["D66AAA24-D61F-44D2-8D53-8B219D009DBF","CCA3949D-B416-466A-8949-425E5DCD35B5"]},"6774A219-5AFB-46B1-8AD2-8BE278B93378":{"pageId":"6774A219-5AFB-46B1-8AD2-8BE278B93378","name":"Page%201","artboardId":["E822A087-B20C-42DD-8665-FE03A4AA8211","83F6DA3D-9F1B-4816-92B9-A74AAED40206"]}},"artboard":{"D66AAA24-D61F-44D2-8D53-8B219D009DBF":{"id":"D66AAA24-D61F-44D2-8D53-8B219D009DBF","src":"D66AAA24-D61F-44D2-8D53-8B219D009DBF","name":"Status%20Bar%2FBlack%2F100%25","type":"MSSymbolMaster","x":100,"y":0,"zIndex":0,"width":375,"height":20,"sharedStyleType":"","sharedStyle":"","symbolId":"8F28FC9A-816F-4541-8734-BC8EF14D685F","slice":[],"layer":[{"id":"372B9348-D602-4A3B-8E91-A66250670461","src":"372B9348-D602-4A3B-8E91-A66250670461","name":"Battery","type":"MSShapeGroup","x":445,"y":6,"zIndex":1,"width":25,"height":10,"sharedStyleType":"","sharedStyle":"","style":{"background":"#030303","border-radius":"1.5px","width":"25px","height":"10px"},"radius":"1.5","background":"#030303"},{"id":"6CE93654-D8D2-4BD3-85AF-C2D9A33E8979","src":"6CE93654-D8D2-4BD3-85AF-C2D9A33E8979","name":"100%25","type":"MSTextLayer","x":409,"y":4,"zIndex":2,"width":33,"height":14,"sharedStyleType":"","sharedStyle":"","html":"100%25","style":{"font-family":"Helvetica","font-size":"12px","color":"#030303","letter-spacing":"0","text-align":"right"}},{"id":"5DE5A561-10FF-40B7-864C-B93F09A30B82","src":"5DE5A561-10FF-40B7-864C-B93F09A30B82","name":"9%3A41%20AM","type":"MSTextLayer","x":263,"y":4,"zIndex":3,"width":49,"height":14,"sharedStyleType":"","sharedStyle":"","html":"9%3A41%20AM","style":{"font-family":"Helvetica","font-size":"12px","color":"#030303","letter-spacing":"0","text-align":"center"}},{"id":"05376719-28C6-4537-A0B4-097E3C72AB41","src":"05376719-28C6-4537-A0B4-097E3C72AB41","name":"Wi-Fi","type":"MSShapeGroup","x":188,"y":6,"zIndex":4,"width":13,"height":10,"sharedStyleType":"","sharedStyle":"","style":{"background":"#030303","width":"13px","height":"10px"},"background":"#030303"},{"id":"5B618E12-BBE6-411D-85B1-8668F4646AC1","src":"5B618E12-BBE6-411D-85B1-8668F4646AC1","name":"Carrier","type":"MSTextLayer","x":144,"y":4,"zIndex":5,"width":40,"height":14,"sharedStyleType":"","sharedStyle":"","html":"Sketch","style":{"font-family":"Helvetica","font-size":"12px","color":"#030303","letter-spacing":"0","text-align":"left"}},{"id":"D78AA366-D361-4FA8-AACE-0B91A0581654","src":"D78AA366-D361-4FA8-AACE-0B91A0581654","name":"Mobile%20Signal","type":"MSShapeGroup","x":107,"y":8,"zIndex":6,"width":34,"height":6,"sharedStyleType":"","sharedStyle":"","style":{"background":"#030303","width":"34px","height":"6px"},"background":"#030303"}],"mask":{}},"CCA3949D-B416-466A-8949-425E5DCD35B5":{"id":"CCA3949D-B416-466A-8949-425E5DCD35B5","src":"CCA3949D-B416-466A-8949-425E5DCD35B5","name":"Status%20Bar%2FWhite%2F100%25","type":"MSSymbolMaster","x":100,"y":120,"zIndex":0,"width":375,"height":20,"sharedStyleType":"","sharedStyle":"","symbolId":"FC386540-E94F-407E-88AC-44E4621D13CF","slice":[],"layer":[{"id":"68A7CB4F-8C7B-480B-B544-271AB428EE8A","src":"68A7CB4F-8C7B-480B-B544-271AB428EE8A","name":"Battery","type":"MSShapeGroup","x":445,"y":126,"zIndex":1,"width":25,"height":10,"sharedStyleType":"","sharedStyle":"","style":{"background":"#ffffff","border-radius":"1.5px","width":"25px","height":"10px"},"radius":"1.5","background":"#ffffff"},{"id":"D4D25776-86EB-4EB6-8037-476B91D3D811","src":"D4D25776-86EB-4EB6-8037-476B91D3D811","name":"100%25","type":"MSTextLayer","x":409,"y":124,"zIndex":2,"width":33,"height":14,"sharedStyleType":"","sharedStyle":"","html":"100%25","style":{"font-family":"Helvetica","font-size":"12px","color":"#ffffff","letter-spacing":"0","text-align":"right"}},{"id":"A14DAE1C-2900-467F-B2EA-41189DAF5B7D","src":"A14DAE1C-2900-467F-B2EA-41189DAF5B7D","name":"9%3A41%20AM","type":"MSTextLayer","x":263,"y":124,"zIndex":3,"width":49,"height":14,"sharedStyleType":"","sharedStyle":"","html":"9%3A41%20AM","style":{"font-family":"Helvetica","font-size":"12px","color":"#ffffff","letter-spacing":"0","text-align":"center"}},{"id":"39D04EF6-4A67-4ED6-A6AC-624922CFD8A1","src":"39D04EF6-4A67-4ED6-A6AC-624922CFD8A1","name":"Wi-Fi","type":"MSShapeGroup","x":188,"y":126,"zIndex":4,"width":13,"height":10,"sharedStyleType":"","sharedStyle":"","style":{"background":"#ffffff","width":"13px","height":"10px"},"background":"#ffffff"},{"id":"FDA2694C-D63C-41E9-A93A-4BF70C3F3364","src":"FDA2694C-D63C-41E9-A93A-4BF70C3F3364","name":"Carrier","type":"MSTextLayer","x":144,"y":124,"zIndex":5,"width":40,"height":14,"sharedStyleType":"","sharedStyle":"","html":"Sketch","style":{"font-family":"Helvetica","font-size":"12px","color":"#ffffff","letter-spacing":"0","text-align":"left"}},{"id":"4F2CC5EF-170E-45CA-94D1-9BCD2FA4A805","src":"4F2CC5EF-170E-45CA-94D1-9BCD2FA4A805","name":"Mobile%20Signal","type":"MSShapeGroup","x":107,"y":128,"zIndex":6,"width":34,"height":6,"sharedStyleType":"","sharedStyle":"","style":{"background":"#ffffff","width":"34px","height":"6px"},"background":"#ffffff"}],"mask":{}},"E822A087-B20C-42DD-8665-FE03A4AA8211":{"id":"E822A087-B20C-42DD-8665-FE03A4AA8211","src":"E822A087-B20C-42DD-8665-FE03A4AA8211","name":"Location","type":"MSArtboardGroup","x":513,"y":55,"zIndex":0,"width":375,"height":667,"sharedStyleType":"","sharedStyle":"","slice":[],"layer":[{"id":"12DBC3B3-F637-40BB-AE47-0B1653A29FFC","src":"12DBC3B3-F637-40BB-AE47-0B1653A29FFC","name":"login","type":"MSTextLayer","x":680,"y":644,"zIndex":0,"width":42,"height":36,"sharedStyleType":"","sharedStyle":"","html":"Login%0A","style":{"font-family":"HelveticaNeue","font-size":"16px","color":"#ffffff","letter-spacing":"0.44px","text-align":"left"}},{"id":"A9F8A6FB-8379-4EF3-A6A9-B93581CF3A25","src":"A9F8A6FB-8379-4EF3-A6A9-B93581CF3A25","name":"Password","type":"MSTextLayer","x":543,"y":370,"zIndex":1,"width":69,"height":23,"sharedStyleType":"","sharedStyle":"","html":"Password","style":{"font-family":"HelveticaNeue","font-size":"15px","color":"#a8b4c4","letter-spacing":"0.29px","text-align":"center","line-height":"23px"}},{"id":"4D1D953A-F49D-42D4-AC1B-051E802681E8","src":"4D1D953A-F49D-42D4-AC1B-051E802681E8","name":"User","type":"MSTextLayer","x":543,"y":298,"zIndex":2,"width":33,"height":23,"sharedStyleType":"","sharedStyle":"","html":"User","style":{"font-family":"HelveticaNeue","font-size":"15px","color":"#a8b4c4","letter-spacing":"0.29px","text-align":"center","line-height":"23px"}},{"id":"CDEBC6CD-73C4-47CB-B879-8A426D8163E1","src":"CDEBC6CD-73C4-47CB-B879-8A426D8163E1","name":"Screen%20Shot%202018-10-18%20at%2022.49.51","type":"MSBitmapLayer","x":639,"y":111,"zIndex":3,"width":125,"height":70,"sharedStyleType":"","sharedStyle":""},{"id":"E3013759-B45A-4E98-8B34-278A50F98591","src":"E3013759-B45A-4E98-8B34-278A50F98591","name":"Status%20Bar%2FBlack%2F100%25","type":"MSSymbolInstance","x":513,"y":55,"zIndex":4,"width":375,"height":20,"sharedStyleType":"","sharedStyle":"","symbolId":"8F28FC9A-816F-4541-8734-BC8EF14D685F"}],"mask":{}},"83F6DA3D-9F1B-4816-92B9-A74AAED40206":{"id":"83F6DA3D-9F1B-4816-92B9-A74AAED40206","src":"83F6DA3D-9F1B-4816-92B9-A74AAED40206","name":"Currency","type":"MSArtboardGroup","x":988,"y":55,"zIndex":0,"width":375,"height":667,"sharedStyleType":"","sharedStyle":"","slice":[],"layer":[{"id":"09BAE240-CC06-46A5-B01E-A15117109EAC","src":"09BAE240-CC06-46A5-B01E-A15117109EAC","name":"Find%20out%20where","type":"MSTextLayer","x":1127,"y":491,"zIndex":0,"width":118,"height":18,"sharedStyleType":"","sharedStyle":"","html":"Find%20out%20where%20","style":{"font-family":"HelveticaNeue","font-size":"16px","color":"#ffffff","letter-spacing":"0.44px","text-align":"left"}},{"id":"2E3D38C9-2ED3-499B-BF39-008733AAE30C","src":"2E3D38C9-2ED3-499B-BF39-008733AAE30C","name":"Market%20rate%3A%201%20USD%20%3D","type":"MSTextLayer","x":1064,"y":420,"zIndex":1,"width":212,"height":17,"sharedStyleType":"","sharedStyle":"","html":"Market%20rate%3A%201%20USD%20%3D%200.8900%20EUR","style":{"font-family":"HelveticaNeue","font-size":"14px","color":"#a8b4c4","text-align":"center"}},{"id":"07B6D69B-B539-432D-A0F1-45428FCF5C13","src":"07B6D69B-B539-432D-A0F1-45428FCF5C13","name":"890%2C10","type":"MSTextLayer","x":1239,"y":351,"zIndex":2,"width":89,"height":33,"sharedStyleType":"","sharedStyle":"","html":"890%2C10","style":{"font-family":"HelveticaNeue-Light","font-size":"28px","color":"#485465","letter-spacing":"0.5px","text-align":"center"}},{"id":"99544A31-44A6-4BAD-B88E-99DDD345E9EA","src":"99544A31-44A6-4BAD-B88E-99DDD345E9EA","name":"1%20EUR%20%3D%201.123%20USD","type":"MSTextLayer","x":1216,"y":331,"zIndex":3,"width":110,"height":14,"sharedStyleType":"","sharedStyle":"","html":"1%20EUR%20%3D%201.123%20USD","style":{"font-family":"HelveticaNeue","font-size":"12px","color":"#a8b4c4","letter-spacing":"0.2px","text-align":"center"}},{"id":"66E60EFF-B648-4673-85F3-323550B705D3","src":"66E60EFF-B648-4673-85F3-323550B705D3","name":"EUR","type":"MSTextLayer","x":1065,"y":356,"zIndex":4,"width":49,"height":29,"sharedStyleType":"","sharedStyle":"","html":"EUR","style":{"font-family":"HelveticaNeue-Light","font-size":"25px","color":"#485465","text-align":"center"}},{"id":"B9A6B04C-614B-4F9E-8BD4-E59A0D050361","src":"B9A6B04C-614B-4F9E-8BD4-E59A0D050361","name":"I%20want","type":"MSTextLayer","x":1024,"y":325,"zIndex":5,"width":44,"height":18,"sharedStyleType":"","sharedStyle":"","html":"I%20want","style":{"font-family":"HelveticaNeue","font-size":"16px","color":"#a8b4c4","text-align":"center"}},{"id":"2B156AE2-C7CC-403A-AD6A-8414CCB60279","src":"2B156AE2-C7CC-403A-AD6A-8414CCB60279","name":"1%20000%2C00","type":"MSTextLayer","x":1214,"y":245,"zIndex":6,"width":113,"height":33,"sharedStyleType":"","sharedStyle":"","html":"1%20000%2C00","style":{"font-family":"HelveticaNeue-Light","font-size":"28px","color":"#485465","letter-spacing":"0.5px","text-align":"center"}},{"id":"246AE036-104B-41CF-B0F4-FCAEDF1CFC25","src":"246AE036-104B-41CF-B0F4-FCAEDF1CFC25","name":"1%20USD%20%3D%200.8900%20EUR","type":"MSTextLayer","x":1209,"y":224,"zIndex":7,"width":117,"height":14,"sharedStyleType":"","sharedStyle":"","html":"1%20USD%20%3D%200.8900%20EUR","style":{"font-family":"HelveticaNeue","font-size":"12px","color":"#a8b4c4","letter-spacing":"0.2px","text-align":"center"}},{"id":"3817562E-CBB6-4CAD-BBCF-FD468BAA446F","src":"3817562E-CBB6-4CAD-BBCF-FD468BAA446F","name":"Shape","type":"MSShapeGroup","x":1025,"y":247,"zIndex":8,"width":15,"height":15,"sharedStyleType":"","sharedStyle":"","style":{"background":"#0052b4","width":"15px","height":"15px"},"background":"#0052b4"},{"id":"D8815B79-8031-4DBF-AC66-0A035AD9905D","src":"D8815B79-8031-4DBF-AC66-0A035AD9905D","name":"USD","type":"MSTextLayer","x":1065,"y":248,"zIndex":9,"width":50,"height":29,"sharedStyleType":"","sharedStyle":"","html":"USD","style":{"font-family":"HelveticaNeue-Light","font-size":"25px","color":"#485465","text-align":"center"}},{"id":"4D7068B6-0C46-4E07-A300-CF23125F168C","src":"4D7068B6-0C46-4E07-A300-CF23125F168C","name":"I%20have","type":"MSTextLayer","x":1024,"y":218,"zIndex":10,"width":43,"height":18,"sharedStyleType":"","sharedStyle":"","html":"I%20have","style":{"font-family":"HelveticaNeue","font-size":"16px","color":"#a8b4c4","text-align":"center"}},{"id":"B27D4452-A3F9-4F53-991A-9E914520B88A","src":"B27D4452-A3F9-4F53-991A-9E914520B88A","name":"Shape","type":"MSShapeGroup","x":1016,"y":142,"zIndex":11,"width":11,"height":16,"sharedStyleType":"","sharedStyle":"","style":{"background":"#485465","width":"11px","height":"16px"},"background":"#485465"},{"id":"904C402D-FC98-45E0-9270-068D9717010D","src":"904C402D-FC98-45E0-9270-068D9717010D","name":"Paris%2C%20France","type":"MSTextLayer","x":1042,"y":143,"zIndex":12,"width":100,"height":20,"sharedStyleType":"","sharedStyle":"","html":"Paris%2C%20France","style":{"font-family":"HelveticaNeue","font-size":"17px","color":"#485465","text-align":"center"}},{"id":"4EE21DE4-3DDC-4464-B264-4223F0236C08","src":"4EE21DE4-3DDC-4464-B264-4223F0236C08","name":"Looking%20for%20the%20best","type":"MSTextLayer","x":1012,"y":168,"zIndex":13,"width":284,"height":16,"sharedStyleType":"","sharedStyle":"","html":"Looking%20for%20the%20best%20exchange%20rates%20in%20Paris%3F","style":{"font-family":"HelveticaNeue","font-size":"14px","color":"#a8b4c4","text-align":"center"}},{"id":"45B169DE-B3B5-4D12-B1B3-25F826B339CB","src":"45B169DE-B3B5-4D12-B1B3-25F826B339CB","name":"Currency","type":"MSTextLayer","x":1142,"y":87,"zIndex":14,"width":67,"height":20,"sharedStyleType":"","sharedStyle":"","html":"Currency","style":{"font-family":".SFNSText","font-size":"17px","color":"#ffffff","letter-spacing":"-0.41px","text-align":"center"}},{"id":"CA2B476F-0F1E-42F9-B119-2A2B3380A4D4","src":"CA2B476F-0F1E-42F9-B119-2A2B3380A4D4","name":"Status%20Bar%2FWhite%2F100%25","type":"MSSymbolInstance","x":988,"y":55,"zIndex":15,"width":375,"height":20,"sharedStyleType":"","sharedStyle":"","symbolId":"FC386540-E94F-407E-88AC-44E4621D13CF"}],"mask":{}}}}
\ No newline at end of file
diff --git a/bank_app_layout/index.html b/bank_app_layout/index.html
deleted file mode 100644
index 28125fac2..000000000
--- a/bank_app_layout/index.html
+++ /dev/null
@@ -1,1569 +0,0 @@
-
-
-
-
-
-
- Spec Export - Sketch Measure 2.4
-
-
-
-
-
-
-
diff --git a/bank_app_layout/links/page-1-currency.html b/bank_app_layout/links/page-1-currency.html
deleted file mode 100644
index 5652fa211..000000000
--- a/bank_app_layout/links/page-1-currency.html
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/bank_app_layout/links/page-1-location.html b/bank_app_layout/links/page-1-location.html
deleted file mode 100644
index 5ca1854a1..000000000
--- a/bank_app_layout/links/page-1-location.html
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/bank_app_layout/preview/page-1-currency.png b/bank_app_layout/preview/page-1-currency.png
deleted file mode 100644
index d0c171355..000000000
Binary files a/bank_app_layout/preview/page-1-currency.png and /dev/null differ
diff --git a/bank_app_layout/preview/page-1-location.png b/bank_app_layout/preview/page-1-location.png
deleted file mode 100644
index c620ef44d..000000000
Binary files a/bank_app_layout/preview/page-1-location.png and /dev/null differ
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 000000000..71b78e029
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,26 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+buildscript {
+ ext.kotlin_version = "1.3.72"
+ repositories {
+ google()
+ jcenter()
+ }
+ dependencies {
+ classpath "com.android.tools.build:gradle:4.0.0"
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
\ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 000000000..4d15d015f
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,21 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app"s APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Automatically convert third-party libraries to use AndroidX
+android.enableJetifier=true
+# Kotlin code style for this project: "official" or "obsolete":
+kotlin.code.style=official
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 000000000..f6b961fd5
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 000000000..7b2de5d33
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Sat Jun 06 19:49:37 BRT 2020
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
diff --git a/gradlew b/gradlew
new file mode 100755
index 000000000..cccdd3d51
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,172 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 000000000..e95643d6a
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,84 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 000000000..f80fb383f
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1,2 @@
+include ':app'
+rootProject.name = "Teste Santander"
\ No newline at end of file
diff --git a/telas.png b/telas.png
deleted file mode 100644
index 783374440..000000000
Binary files a/telas.png and /dev/null differ