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/.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/gradle.xml b/.idea/gradle.xml new file mode 100644 index 000000000..5cd135a06 --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,20 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 000000000..0d40bc82d --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + 1.8 + + + + + + + + \ 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..35eb1ddfb --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/README.md b/README.md index bd73feb5f..25cffb415 100644 --- a/README.md +++ b/README.md @@ -1,41 +1,14 @@ -# Show me the code +# TesteAndroidv2 -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 +Aplicação Android com Kotlin para aplicação à vaga de de Desenvolvedor 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) +### Decisões tomadas sobre o projeto -![Image of Yaktocat](https://github.com/SantanderTecnologia/TesteiOS/blob/new_test/telas.png) +###### Retrofit +Escolhida por facilitar a comunicação com api's, simplificando a configuração e a serialização e deserialização dos objetos -### # DESAFIO: +###### Arquitetura +Escrevi o projeto utilizando a arquitetura MVVM e DataBinding. Essa arquitetura torna mais simples a testabilidade e manutenção do app, principalmente por diminuir consideravelmente o acoplamento dentro do projeto. -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! +###### Testes +Testes unitários realizados com jUnit. diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 000000000..796b96d1c --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 000000000..b09a96105 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,67 @@ +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply plugin: 'kotlin-android-extensions' +apply plugin: "kotlin-kapt" + +android { + compileSdkVersion 29 + buildToolsVersion "29.0.3" + + defaultConfig { + applicationId "com.joaoneto.testeandroidv2" + minSdkVersion 24 + targetSdkVersion 29 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + dataBinding { + enabled = true + } + + kapt { + generateStubs = true + } + compileOptions { + sourceCompatibility = 1.8 + targetCompatibility = 1.8 + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + implementation 'androidx.appcompat:appcompat:1.1.0' + implementation 'androidx.constraintlayout:constraintlayout:1.1.3' + //noinspection GradleDependency + implementation 'com.google.android.material:material:1.0.0' + testImplementation 'junit:junit:4.13' + androidTestImplementation 'androidx.test.ext:junit:1.1.1' + testImplementation 'org.mockito:mockito-core:2.28.2' + androidTestImplementation 'org.mockito:mockito-android:2.28.2' + testImplementation 'org.mockito:mockito-inline:2.28.2' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' + + // architecture + implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' + implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0' + //noinspection LifecycleAnnotationProcessorWithJava8 + kapt "androidx.lifecycle:lifecycle-compiler:2.2.0" + + //retrofit + implementation 'com.squareup.retrofit2:retrofit:2.5.0' + implementation 'com.squareup.retrofit2:converter-jackson:2.5.0' + implementation 'com.squareup.retrofit2:converter-gson:2.5.0' + implementation 'com.squareup.okhttp3:logging-interceptor:3.13.1' +} + + diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 000000000..f1b424510 --- /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 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 000000000..3a2c3c710 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/joaoneto/testeandroidv2/loginScreen/api/LoginService.kt b/app/src/main/java/com/joaoneto/testeandroidv2/loginScreen/api/LoginService.kt new file mode 100644 index 000000000..835c2d7ab --- /dev/null +++ b/app/src/main/java/com/joaoneto/testeandroidv2/loginScreen/api/LoginService.kt @@ -0,0 +1,16 @@ +package com.joaoneto.testeandroidv2.loginScreen.api + +import com.joaoneto.testeandroidv2.loginScreen.model.LoginResponseModel +import retrofit2.Call +import retrofit2.http.Field +import retrofit2.http.FormUrlEncoded +import retrofit2.http.POST + +interface LoginService { + @FormUrlEncoded + @POST("login") + fun signIn( + @Field("user") user: String, + @Field("password") password: String + ): Call +} \ No newline at end of file diff --git a/app/src/main/java/com/joaoneto/testeandroidv2/loginScreen/model/LoginResponseModel.kt b/app/src/main/java/com/joaoneto/testeandroidv2/loginScreen/model/LoginResponseModel.kt new file mode 100644 index 000000000..14ab5ec76 --- /dev/null +++ b/app/src/main/java/com/joaoneto/testeandroidv2/loginScreen/model/LoginResponseModel.kt @@ -0,0 +1,8 @@ +package com.joaoneto.testeandroidv2.loginScreen.model + +import java.io.Serializable + +class LoginResponseModel : Serializable { + var userAccount: UserAccountModel? = null + var error: Void? = null +} \ No newline at end of file diff --git a/app/src/main/java/com/joaoneto/testeandroidv2/loginScreen/model/UserAccountModel.kt b/app/src/main/java/com/joaoneto/testeandroidv2/loginScreen/model/UserAccountModel.kt new file mode 100644 index 000000000..25ab41a54 --- /dev/null +++ b/app/src/main/java/com/joaoneto/testeandroidv2/loginScreen/model/UserAccountModel.kt @@ -0,0 +1,11 @@ +package com.joaoneto.testeandroidv2.loginScreen.model + +import java.io.Serializable + +class UserAccountModel : 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/com/joaoneto/testeandroidv2/loginScreen/ui/view/activity/LoginActivity.kt b/app/src/main/java/com/joaoneto/testeandroidv2/loginScreen/ui/view/activity/LoginActivity.kt new file mode 100644 index 000000000..de9e9c4e2 --- /dev/null +++ b/app/src/main/java/com/joaoneto/testeandroidv2/loginScreen/ui/view/activity/LoginActivity.kt @@ -0,0 +1,86 @@ +package com.joaoneto.testeandroidv2.loginScreen.ui.view.activity + +import android.content.Intent +import android.os.Bundle +import android.text.Editable +import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.Observer +import androidx.lifecycle.ViewModelProvider +import com.joaoneto.testeandroidv2.R +import com.joaoneto.testeandroidv2.loginScreen.ui.viewModel.LoginViewModel +import com.joaoneto.testeandroidv2.util.system.PreferencesHelper +import com.joaoneto.testeandroidv2.util.system.SnackbarHelper +import com.joaoneto.testeandroidv2.mainScreen.view.activity.MainActivity +import kotlinx.android.synthetic.main.activity_login.* + +class LoginActivity : AppCompatActivity() { + + + private val preferencesHelper: PreferencesHelper by lazy { + PreferencesHelper(this) + } + + private lateinit var viewModel: LoginViewModel + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_login); + viewModel = ViewModelProvider(this).get(LoginViewModel::class.java) + checkPreferences() + checkFields() + } + + private fun checkFields() { + + loginButton.setOnClickListener { + val user = inputEmail.text.toString() + val pass = inputPass.text.toString() + + if (user.isNotEmpty() && pass.isNotEmpty()) { + + viewModel.signIn(user, pass).observe(this, Observer { + if (it == null) { + SnackbarHelper.message( + loginConstraint, + "Erro ao acessar conta, verifique suas credenciais e tente novamente" + ) + } else { + + preferencesHelper.setUsername(user) + preferencesHelper.setPassword(pass) + + val intent = Intent(this@LoginActivity, MainActivity::class.java) + val bundle = Bundle() + bundle.putSerializable("userAccountData", it.userAccount) + intent.putExtras(bundle) + startActivity(intent) + finish() + } + + }) + + } else { + SnackbarHelper.message(loginConstraint, "Campos obrigatórios vazios") + } + } + + } + + private fun checkPreferences() { + if (preferencesHelper.getUsername().isNotEmpty()) { + inputEmail.text = + Editable.Factory.getInstance().newEditable(preferencesHelper.getUsername()) + + } else { + inputEmail.text = Editable.Factory.getInstance().newEditable("") + } + if (preferencesHelper.getPassword().isNotEmpty()) { + inputPass.text = + Editable.Factory.getInstance().newEditable(preferencesHelper.getPassword()) + } else { + inputPass.text = + Editable.Factory.getInstance().newEditable("") + } + } + +} diff --git a/app/src/main/java/com/joaoneto/testeandroidv2/loginScreen/ui/viewModel/LoginViewModel.kt b/app/src/main/java/com/joaoneto/testeandroidv2/loginScreen/ui/viewModel/LoginViewModel.kt new file mode 100644 index 000000000..377179256 --- /dev/null +++ b/app/src/main/java/com/joaoneto/testeandroidv2/loginScreen/ui/viewModel/LoginViewModel.kt @@ -0,0 +1,51 @@ +package com.joaoneto.testeandroidv2.loginScreen.ui.viewModel + + +import RetrofitInitializer +import android.util.Log +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.joaoneto.testeandroidv2.loginScreen.model.LoginResponseModel +import retrofit2.Call +import retrofit2.Callback +import retrofit2.Response + + +class LoginViewModel : + ViewModel() { + + + fun signIn(user: String, password: String): LiveData { + + val loginResponse = MutableLiveData() + + RetrofitInitializer().loginService().signIn(user, password).enqueue(object : Callback { + override fun onFailure(call: Call, t: Throwable) { + + Log.i("--->", t.message!!) + } + + override fun onResponse( + call: Call, + response: Response + ) { + + if (response.code() == 200) { + loginResponse.value = response.body() + } else { + Log.i("--->", response.code().toString()) + Log.i("--->", response.message()) + } + + } + + }) + + return loginResponse + } +} + + + + diff --git a/app/src/main/java/com/joaoneto/testeandroidv2/mainScreen/api/StatementsService.kt b/app/src/main/java/com/joaoneto/testeandroidv2/mainScreen/api/StatementsService.kt new file mode 100644 index 000000000..dba1c45d2 --- /dev/null +++ b/app/src/main/java/com/joaoneto/testeandroidv2/mainScreen/api/StatementsService.kt @@ -0,0 +1,10 @@ +package com.joaoneto.testeandroidv2.mainScreen.api + +import com.joaoneto.testeandroidv2.mainScreen.model.StatementsResponseModel +import retrofit2.Call +import retrofit2.http.GET + +interface StatementsService { + @GET("statements/1") + fun getStatements(): Call +} diff --git a/app/src/main/java/com/joaoneto/testeandroidv2/mainScreen/model/StatementModel.kt b/app/src/main/java/com/joaoneto/testeandroidv2/mainScreen/model/StatementModel.kt new file mode 100644 index 000000000..454f26edd --- /dev/null +++ b/app/src/main/java/com/joaoneto/testeandroidv2/mainScreen/model/StatementModel.kt @@ -0,0 +1,10 @@ +package com.joaoneto.testeandroidv2.mainScreen.model + +import java.io.Serializable + +class StatementModel: Serializable { + 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/com/joaoneto/testeandroidv2/mainScreen/model/StatementsResponseModel.kt b/app/src/main/java/com/joaoneto/testeandroidv2/mainScreen/model/StatementsResponseModel.kt new file mode 100644 index 000000000..3087cc1ea --- /dev/null +++ b/app/src/main/java/com/joaoneto/testeandroidv2/mainScreen/model/StatementsResponseModel.kt @@ -0,0 +1,8 @@ +package com.joaoneto.testeandroidv2.mainScreen.model + +import java.io.Serializable + +class StatementsResponseModel : Serializable{ + var statementList: List? = null + var error: Void? = null +} \ No newline at end of file diff --git a/app/src/main/java/com/joaoneto/testeandroidv2/mainScreen/view/activity/MainActivity.kt b/app/src/main/java/com/joaoneto/testeandroidv2/mainScreen/view/activity/MainActivity.kt new file mode 100644 index 000000000..94890cb24 --- /dev/null +++ b/app/src/main/java/com/joaoneto/testeandroidv2/mainScreen/view/activity/MainActivity.kt @@ -0,0 +1,108 @@ +package com.joaoneto.testeandroidv2.mainScreen.view.activity + +import android.content.Intent +import android.os.Bundle +import android.view.View +import androidx.appcompat.app.AppCompatActivity +import androidx.core.content.ContextCompat +import androidx.databinding.DataBindingUtil +import androidx.lifecycle.Observer +import androidx.lifecycle.ViewModelProvider +import androidx.recyclerview.widget.LinearLayoutManager +import com.google.android.material.appbar.AppBarLayout +import com.joaoneto.testeandroidv2.R +import com.joaoneto.testeandroidv2.databinding.ActivityMainBinding +import com.joaoneto.testeandroidv2.loginScreen.model.UserAccountModel +import com.joaoneto.testeandroidv2.loginScreen.ui.view.activity.LoginActivity +import com.joaoneto.testeandroidv2.mainScreen.model.StatementModel +import com.joaoneto.testeandroidv2.mainScreen.view.adapter.StatementsAdapter +import com.joaoneto.testeandroidv2.mainScreen.viewModel.MainViewModel +import com.joaoneto.testeandroidv2.util.system.Formatter +import com.joaoneto.testeandroidv2.util.system.SnackbarHelper +import kotlinx.android.synthetic.main.activity_main.* + +class MainActivity : AppCompatActivity() { + + private lateinit var viewModel: MainViewModel + + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + val binding = DataBindingUtil + .setContentView(this, R.layout.activity_main) + val userAccount = intent.extras?.getSerializable("userAccountData") as UserAccountModel + binding.userAccount = userAccount + binding.formatter = Formatter() + + viewModel = ViewModelProvider(this).get(MainViewModel::class.java) + setUpAppBar() + setUpView() + } + + override fun onBackPressed() { + super.onBackPressed() + logout() + + } + + + private fun setUpView() { + logoutImageView.setOnClickListener { + logout() + } + + viewModel.getStatements().observe(this, Observer { + + if (it == null) { + SnackbarHelper.message( + mainConstraint, + "Não foi possivel buscar as transações, tente novamente mais tarde" + ) + } else { + it.statementList?.let { statementList -> setUpRecyclerView(statementList) } + } + }) + + } + + private fun setUpAppBar() { + setSupportActionBar(toolbar) + supportActionBar!!.setDisplayHomeAsUpEnabled(false) + toolbar.setNavigationOnClickListener { + } + collapsingToolbar.setCollapsedTitleTextColor( + ContextCompat.getColor( + this, + R.color.colorWhite + ) + ) + appBar.addOnOffsetChangedListener(AppBarLayout.OnOffsetChangedListener { appBarLayout, verticalOfSet -> + if (verticalOfSet == 0) { + appbarConstraint.visibility = View.VISIBLE + collapsingToolbar.title = " " + } else { + appbarConstraint.visibility = View.INVISIBLE + collapsingToolbar.title = "Bank" + + } + }) + } + + private fun setUpRecyclerView(statements: List) { + operationsRecyclerView.adapter = + StatementsAdapter( + statements + ) + operationsRecyclerView.layoutManager = + LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false) + } + + + private fun logout() { + val intent = Intent(this, LoginActivity::class.java) + startActivity(intent) + finish() + } + + +} diff --git a/app/src/main/java/com/joaoneto/testeandroidv2/mainScreen/view/adapter/StatementsAdapter.kt b/app/src/main/java/com/joaoneto/testeandroidv2/mainScreen/view/adapter/StatementsAdapter.kt new file mode 100644 index 000000000..5bb109554 --- /dev/null +++ b/app/src/main/java/com/joaoneto/testeandroidv2/mainScreen/view/adapter/StatementsAdapter.kt @@ -0,0 +1,42 @@ +package com.joaoneto.testeandroidv2.mainScreen.view.adapter + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.databinding.DataBindingUtil +import androidx.recyclerview.widget.RecyclerView +import com.joaoneto.testeandroidv2.R +import com.joaoneto.testeandroidv2.databinding.AdapterItemListBinding +import com.joaoneto.testeandroidv2.mainScreen.model.StatementModel +import com.joaoneto.testeandroidv2.util.system.Formatter + +class StatementsAdapter(private val statements: List) : + RecyclerView.Adapter() { + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder = + ViewHolder( + DataBindingUtil.inflate( + LayoutInflater.from(parent.context), + R.layout.adapter_item_list, + parent, + false + ) + ) + + + override fun getItemCount(): Int = statements.size + + override fun onBindViewHolder(holder: ViewHolder, position: Int) = + holder.bindView(statements[position]) + + class ViewHolder(private val binding: AdapterItemListBinding) : + RecyclerView.ViewHolder(binding.root) { + + fun bindView(statement: StatementModel) { + + binding.executePendingBindings() + binding.statement = statement + binding.formatter = Formatter() + + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/joaoneto/testeandroidv2/mainScreen/viewModel/MainViewModel.kt b/app/src/main/java/com/joaoneto/testeandroidv2/mainScreen/viewModel/MainViewModel.kt new file mode 100644 index 000000000..b81e1571d --- /dev/null +++ b/app/src/main/java/com/joaoneto/testeandroidv2/mainScreen/viewModel/MainViewModel.kt @@ -0,0 +1,45 @@ +package com.joaoneto.testeandroidv2.mainScreen.viewModel + + +import RetrofitInitializer +import android.util.Log +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.joaoneto.testeandroidv2.mainScreen.model.StatementsResponseModel +import retrofit2.Call +import retrofit2.Callback +import retrofit2.Response + +class MainViewModel : ViewModel() { + + fun getStatements(): LiveData { + + val statementResponse = MutableLiveData() + + RetrofitInitializer().statementsService().getStatements() + .enqueue(object : Callback { + override fun onFailure(call: Call, t: Throwable) { + Log.e("--->", t.message!!) + } + + override fun onResponse( + call: Call, + response: Response + ) { + if (response.code() == 200) { + + statementResponse.value = response.body() + + } else { + Log.i("--->", response.code().toString()) + Log.i("--->", response.message()) + } + + } + + }) + + return statementResponse + } +} \ No newline at end of file diff --git a/app/src/main/java/com/joaoneto/testeandroidv2/util/api/RetrofitInitializer.kt b/app/src/main/java/com/joaoneto/testeandroidv2/util/api/RetrofitInitializer.kt new file mode 100644 index 000000000..3f3bf0919 --- /dev/null +++ b/app/src/main/java/com/joaoneto/testeandroidv2/util/api/RetrofitInitializer.kt @@ -0,0 +1,34 @@ +import com.joaoneto.testeandroidv2.loginScreen.api.LoginService +import com.joaoneto.testeandroidv2.mainScreen.api.StatementsService +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import retrofit2.Retrofit +import retrofit2.converter.jackson.JacksonConverterFactory +import java.util.concurrent.TimeUnit + +class RetrofitInitializer { + private val retrofit: Retrofit + + init { + + val interceptor = HttpLoggingInterceptor() + interceptor.level = HttpLoggingInterceptor.Level.BODY + val client = OkHttpClient.Builder() + .readTimeout(60, TimeUnit.SECONDS) + .connectTimeout(60, TimeUnit.SECONDS) + .retryOnConnectionFailure(false) + .build() + + retrofit = Retrofit.Builder() + .baseUrl("https://bank-app-test.herokuapp.com/api/") + .addConverterFactory(JacksonConverterFactory.create()) + .client(client) + .build() + } + + fun loginService(): LoginService = retrofit.create( + LoginService::class.java) + + fun statementsService(): StatementsService = retrofit.create( + StatementsService::class.java) +} \ No newline at end of file diff --git a/app/src/main/java/com/joaoneto/testeandroidv2/util/constant/AppConstants.kt b/app/src/main/java/com/joaoneto/testeandroidv2/util/constant/AppConstants.kt new file mode 100644 index 000000000..91da747a3 --- /dev/null +++ b/app/src/main/java/com/joaoneto/testeandroidv2/util/constant/AppConstants.kt @@ -0,0 +1,5 @@ +package com.joaoneto.testeandroidv2.util.constant + +object AppConstants { + const val BASE_URL = "https://jsonplaceholder.typicode.com" +} \ No newline at end of file diff --git a/app/src/main/java/com/joaoneto/testeandroidv2/util/system/Formatter.kt b/app/src/main/java/com/joaoneto/testeandroidv2/util/system/Formatter.kt new file mode 100644 index 000000000..5e727e84d --- /dev/null +++ b/app/src/main/java/com/joaoneto/testeandroidv2/util/system/Formatter.kt @@ -0,0 +1,27 @@ +package com.joaoneto.testeandroidv2.util.system + +import java.text.NumberFormat + +class Formatter { + + fun formatToCurrency(value: Double): String{ + + return NumberFormat.getCurrencyInstance().format(value) + + } + + fun formatToDate(date: String): String{ + + return "${date.substring(8, 9)}/${date.substring( + 5, + 6 + )}/${date.substring(0, 3)}" + } + + fun formatAccountNumber(agency: String, bankAccount: String): String{ + return "${bankAccount}/${agency.substring( + 0, + 7 + )}-${agency.substring(8)}" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/joaoneto/testeandroidv2/util/system/PreferencesHelper.kt b/app/src/main/java/com/joaoneto/testeandroidv2/util/system/PreferencesHelper.kt new file mode 100644 index 000000000..5b95db479 --- /dev/null +++ b/app/src/main/java/com/joaoneto/testeandroidv2/util/system/PreferencesHelper.kt @@ -0,0 +1,29 @@ +package com.joaoneto.testeandroidv2.util.system + +import android.content.Context +import android.content.SharedPreferences + +class PreferencesHelper(var context: Context) { + + private var preferences: SharedPreferences = + context.getSharedPreferences("testeAndroid2", Context.MODE_PRIVATE) + private var editor = preferences.edit() + + fun getUsername(): String { + return preferences.getString("username", "") as String + } + + fun setUsername(username: String) { + editor.putString("username", username) + editor.commit() + } + + fun getPassword(): String { + return preferences.getString("password", "") as String + } + + fun setPassword(password: String) { + editor.putString("password", password) + editor.commit() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/joaoneto/testeandroidv2/util/system/SnackbarHelper.kt b/app/src/main/java/com/joaoneto/testeandroidv2/util/system/SnackbarHelper.kt new file mode 100644 index 000000000..9c20d2296 --- /dev/null +++ b/app/src/main/java/com/joaoneto/testeandroidv2/util/system/SnackbarHelper.kt @@ -0,0 +1,14 @@ +package com.joaoneto.testeandroidv2.util.system + +import android.view.View +import com.google.android.material.snackbar.Snackbar + + +class SnackbarHelper { + + companion object { + fun message(view: View, message: String) { + Snackbar.make(view, message, Snackbar.LENGTH_LONG).show() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/joaoneto/testeandroidv2/util/system/SystemUtil.kt b/app/src/main/java/com/joaoneto/testeandroidv2/util/system/SystemUtil.kt new file mode 100644 index 000000000..5bb76955c --- /dev/null +++ b/app/src/main/java/com/joaoneto/testeandroidv2/util/system/SystemUtil.kt @@ -0,0 +1,18 @@ +package com.joaoneto.testeandroidv2.util.system + +import android.app.Activity +import android.content.Context +import android.view.inputmethod.InputMethodManager + +class SystemUtil { + + companion object { + fun hideKeyboard(activity: Activity) { + val view = activity.currentFocus + if (view != null) { + val input = activity.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager + input.hideSoftInputFromWindow(view.windowToken, 0) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 000000000..2b068d114 --- /dev/null +++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ 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/logo.png b/app/src/main/res/drawable/logo.png new file mode 100644 index 000000000..66bdc8d5d Binary files /dev/null and b/app/src/main/res/drawable/logo.png differ diff --git a/app/src/main/res/drawable/logout.png b/app/src/main/res/drawable/logout.png new file mode 100644 index 000000000..de1e4ae3c Binary files /dev/null and b/app/src/main/res/drawable/logout.png differ 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..10079bcb6 --- /dev/null +++ b/app/src/main/res/layout/activity_login.xml @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + + +