From 00921763b6de6b204ca7ec3726ffd251fa70f683 Mon Sep 17 00:00:00 2001
From: yansans <66671259+yansans@users.noreply.github.com>
Date: Sat, 30 Mar 2024 09:48:56 +0700
Subject: [PATCH] refactor: login to activity

---
 app/src/main/AndroidManifest.xml              |  10 +-
 .../java/com/example/bondoyap/ApiClient.kt    |   1 +
 .../java/com/example/bondoyap/MainActivity.kt |  27 ++-
 .../bondoyap/ui/login/LoginActivity.kt        | 157 ++++++++++++++++++
 .../bondoyap/ui/login/LoginFragment.kt        | 137 ---------------
 .../bondoyap/ui/settings/SettingsFragment.kt  |  11 +-
 .../activity_login.xml}                       |  20 +--
 .../main/res/layout-w936dp/activity_login.xml |  78 +++++++++
 app/src/main/res/layout/activity_login.xml    |  71 ++++++++
 .../main/res/navigation/mobile_navigation.xml |  10 +-
 app/src/main/res/values-land/dimens.xml       |   3 +
 app/src/main/res/values-w1240dp/dimens.xml    |   3 +
 app/src/main/res/values-w600dp/dimens.xml     |   3 +
 app/src/main/res/values/strings.xml           |   3 +-
 14 files changed, 356 insertions(+), 178 deletions(-)
 create mode 100644 app/src/main/java/com/example/bondoyap/ui/login/LoginActivity.kt
 delete mode 100644 app/src/main/java/com/example/bondoyap/ui/login/LoginFragment.kt
 rename app/src/main/res/{layout/fragment_login.xml => layout-w1240dp/activity_login.xml} (81%)
 create mode 100644 app/src/main/res/layout-w936dp/activity_login.xml
 create mode 100644 app/src/main/res/layout/activity_login.xml
 create mode 100644 app/src/main/res/values-land/dimens.xml
 create mode 100644 app/src/main/res/values-w1240dp/dimens.xml
 create mode 100644 app/src/main/res/values-w600dp/dimens.xml

diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 07c75b6..d03d4fa 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -14,16 +14,20 @@
         android:supportsRtl="true"
         android:theme="@style/Theme.BondoYap"
         tools:targetApi="31">
-
         <activity
-            android:name=".MainActivity"
+            android:name=".ui.login.LoginActivity"
             android:exported="true">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
-
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
+
+        <activity
+            android:name=".MainActivity"
+            android:exported="true">
+        </activity>
+
     </application>
 
 </manifest>
\ No newline at end of file
diff --git a/app/src/main/java/com/example/bondoyap/ApiClient.kt b/app/src/main/java/com/example/bondoyap/ApiClient.kt
index 352a80e..d1858bc 100644
--- a/app/src/main/java/com/example/bondoyap/ApiClient.kt
+++ b/app/src/main/java/com/example/bondoyap/ApiClient.kt
@@ -7,6 +7,7 @@ import retrofit2.http.POST
 
 object Constants {
     const val BASE_URL: String = "https://pbd-backend-2024.vercel.app/api/"
+    const val SHARED_PREFS_NAME = "Prefs"
 }
 
 interface ApiClient{
diff --git a/app/src/main/java/com/example/bondoyap/MainActivity.kt b/app/src/main/java/com/example/bondoyap/MainActivity.kt
index 9fd6eef..7002b98 100644
--- a/app/src/main/java/com/example/bondoyap/MainActivity.kt
+++ b/app/src/main/java/com/example/bondoyap/MainActivity.kt
@@ -1,6 +1,7 @@
 package com.example.bondoyap
 
 import android.content.Context
+import android.content.Intent
 import android.content.SharedPreferences
 import android.os.Bundle
 import com.google.android.material.bottomnavigation.BottomNavigationView
@@ -9,17 +10,25 @@ import androidx.navigation.findNavController
 import androidx.navigation.ui.AppBarConfiguration
 import androidx.navigation.ui.setupActionBarWithNavController
 import androidx.navigation.ui.setupWithNavController
+import com.example.bondoyap.Constants.SHARED_PREFS_NAME
 import com.example.bondoyap.databinding.ActivityMainBinding
+import com.example.bondoyap.ui.login.LoginActivity
 
 class MainActivity : AppCompatActivity() {
 
     private lateinit var binding: ActivityMainBinding
     private lateinit var sharedPreferences: SharedPreferences
 
-
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
 
+        sharedPreferences = getSharedPreferences(SHARED_PREFS_NAME, Context.MODE_PRIVATE)
+
+        if (!isLoggedIn()) {
+            val intent = Intent(this, LoginActivity::class.java)
+            startActivity(intent)
+            finish()
+        }
         binding = ActivityMainBinding.inflate(layoutInflater)
         setContentView(binding.root)
 
@@ -27,28 +36,14 @@ class MainActivity : AppCompatActivity() {
 
         val navController = findNavController(R.id.nav_host_fragment_activity_main)
 
-        sharedPreferences = getSharedPreferences("Prefs", Context.MODE_PRIVATE)
-
-        if (!isLoggedIn()) {
-            navController.navigate(R.id.navigation_login)
-        }
         val appBarConfiguration = AppBarConfiguration(setOf(
                 R.id.navigation_settings, R.id.navigation_transactions, R.id.navigation_scanner,
-                R.id.navigation_graph, R.id.navigation_login))
+                R.id.navigation_graph))
 
         setupActionBarWithNavController(navController, appBarConfiguration)
 
         navView.setupWithNavController(navController)
-
-        navController.addOnDestinationChangedListener { _, destination, _ ->
-            if (destination.id == R.id.navigation_login) {
-                navView.visibility = BottomNavigationView.GONE
-            } else {
-                navView.visibility = BottomNavigationView.VISIBLE
-            }
-        }
     }
-
     private fun isLoggedIn(): Boolean {
         return sharedPreferences.getBoolean("isLoggedIn", false)
     }
diff --git a/app/src/main/java/com/example/bondoyap/ui/login/LoginActivity.kt b/app/src/main/java/com/example/bondoyap/ui/login/LoginActivity.kt
new file mode 100644
index 0000000..8b99746
--- /dev/null
+++ b/app/src/main/java/com/example/bondoyap/ui/login/LoginActivity.kt
@@ -0,0 +1,157 @@
+package com.example.bondoyap.ui.login
+
+import android.content.Context
+import android.content.Intent
+import android.content.SharedPreferences
+import androidx.lifecycle.Observer
+import androidx.lifecycle.ViewModelProvider
+import androidx.annotation.StringRes
+import android.os.Bundle
+import android.text.Editable
+import android.text.TextWatcher
+import android.view.View
+import android.view.inputmethod.EditorInfo
+import android.widget.EditText
+import android.widget.Toast
+import androidx.appcompat.app.AppCompatActivity
+import com.example.bondoyap.Constants.SHARED_PREFS_NAME
+import com.example.bondoyap.MainActivity
+import com.example.bondoyap.R
+import com.example.bondoyap.databinding.ActivityLoginBinding
+
+
+
+class LoginActivity : AppCompatActivity() {
+
+    private lateinit var loginViewModel: LoginViewModel
+    private lateinit var binding: ActivityLoginBinding
+    private lateinit var sharedPreferences: SharedPreferences
+
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+
+        sharedPreferences = getSharedPreferences(SHARED_PREFS_NAME, Context.MODE_PRIVATE)
+
+        if (isLoggedIn()) {
+            navigateToMainActivity()
+            finish()
+        } else {
+            binding = ActivityLoginBinding.inflate(layoutInflater)
+            setContentView(binding.root)
+
+            val emailEditText = binding.email
+            val passwordEditText = binding.password
+            val loginButton = binding.login
+            val loadingProgressBar = binding.loading
+
+            val factory = LoginViewModelFactory(this)
+            loginViewModel = ViewModelProvider(this, factory)[LoginViewModel::class.java]
+
+            loginViewModel.loginFormState.observe(this@LoginActivity, Observer {
+                val loginState = it ?: return@Observer
+
+                // disable login button unless both username / password is valid
+                loginButton.isEnabled = loginState.isDataValid
+
+                if (loginState.emailError != null) {
+                    emailEditText.error = getString(loginState.emailError)
+                }
+                if (loginState.passwordError != null) {
+                    passwordEditText.error = getString(loginState.passwordError)
+                }
+            })
+
+            loginViewModel.loginResult.observe(this@LoginActivity, Observer {
+                val loginResult = it ?: return@Observer
+
+                loadingProgressBar.visibility = View.GONE
+                if (loginResult.error != null) {
+                    showLoginFailed(loginResult.error)
+                }
+                if (loginResult.success != null) {
+                    updateUiWithUser(loginResult.success)
+                }
+                setResult(RESULT_OK)
+
+                //Complete and destroy login activity once successful
+                finish()
+            })
+
+            emailEditText.afterTextChanged {
+                loginViewModel.loginDataChanged(
+                    emailEditText.text.toString(),
+                    passwordEditText.text.toString()
+                )
+            }
+
+            passwordEditText.apply {
+                afterTextChanged {
+                    loginViewModel.loginDataChanged(
+                        emailEditText.text.toString(),
+                        passwordEditText.text.toString()
+                    )
+                }
+
+                setOnEditorActionListener { _, actionId, _ ->
+                    when (actionId) {
+                        EditorInfo.IME_ACTION_DONE ->
+                            loginViewModel.login(
+                                emailEditText.text.toString(),
+                                passwordEditText.text.toString()
+                            )
+                    }
+                    false
+                }
+
+                loginButton.setOnClickListener {
+                    loadingProgressBar.visibility = View.VISIBLE
+                    loginViewModel.login(
+                        emailEditText.text.toString(),
+                        passwordEditText.text.toString()
+                    )
+                }
+            }
+        }
+    }
+
+    private fun updateUiWithUser(model: LoggedInUserView) {
+        val welcome = getString(R.string.welcome) + "\n" + model.email
+        // TODO : initiate successful logged in experience
+        Toast.makeText(
+            applicationContext,
+            welcome,
+            Toast.LENGTH_LONG
+        ).show()
+        navigateToMainActivity()
+    }
+
+    private fun showLoginFailed(@StringRes errorString: Int) {
+        Toast.makeText(applicationContext, errorString, Toast.LENGTH_SHORT).show()
+    }
+
+    private fun navigateToMainActivity() {
+        val intent = Intent(this, MainActivity::class.java)
+        startActivity(intent)
+        finish()
+    }
+
+    private fun isLoggedIn(): Boolean {
+        return sharedPreferences.getBoolean("isLoggedIn", false)
+    }
+}
+
+/**
+ * Extension function to simplify setting an afterTextChanged action to EditText components.
+ */
+fun EditText.afterTextChanged(afterTextChanged: (String) -> Unit) {
+    this.addTextChangedListener(object : TextWatcher {
+        override fun afterTextChanged(editable: Editable?) {
+            afterTextChanged.invoke(editable.toString())
+        }
+
+        override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
+
+        override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
+    })
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/bondoyap/ui/login/LoginFragment.kt b/app/src/main/java/com/example/bondoyap/ui/login/LoginFragment.kt
deleted file mode 100644
index 97a8f59..0000000
--- a/app/src/main/java/com/example/bondoyap/ui/login/LoginFragment.kt
+++ /dev/null
@@ -1,137 +0,0 @@
-package com.example.bondoyap.ui.login
-
-import androidx.lifecycle.Observer
-import androidx.lifecycle.ViewModelProvider
-import androidx.annotation.StringRes
-import androidx.fragment.app.Fragment
-import android.os.Bundle
-import android.text.Editable
-import android.text.TextWatcher
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.view.inputmethod.EditorInfo
-import android.widget.Toast
-import androidx.navigation.fragment.findNavController
-import com.example.bondoyap.R
-import com.example.bondoyap.databinding.FragmentLoginBinding
-
-class LoginFragment : Fragment() {
-
-    private lateinit var loginViewModel: LoginViewModel
-    private var _binding: FragmentLoginBinding? = null
-
-    // This property is only valid between onCreateView and
-    // onDestroyView.
-    private val binding get() = _binding!!
-
-    override fun onCreateView(
-        inflater: LayoutInflater,
-        container: ViewGroup?,
-        savedInstanceState: Bundle?
-    ): View {
-
-        _binding = FragmentLoginBinding.inflate(inflater, container, false)
-        return binding.root
-
-    }
-
-    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
-        super.onViewCreated(view, savedInstanceState)
-        val factory = context?.let { LoginViewModelFactory(it) }
-        if (factory != null) {
-            loginViewModel = ViewModelProvider(this, factory)[LoginViewModel::class.java]
-        } else {
-            throw IllegalStateException("Context is null. Cannot create LoginViewModelFactory.")
-        }
-        val emailEditText = binding.email
-        val passwordEditText = binding.password
-        val loginButton = binding.login
-        val loadingProgressBar = binding.loading
-
-        loginViewModel.loginFormState.observe(viewLifecycleOwner,
-            Observer { loginFormState ->
-                if (loginFormState == null) {
-                    return@Observer
-                }
-                loginButton.isEnabled = loginFormState.isDataValid
-                loginFormState.emailError?.let {
-                    emailEditText.error = getString(it)
-                }
-                loginFormState.passwordError?.let {
-                    passwordEditText.error = getString(it)
-                }
-            })
-
-        loginViewModel.loginResult.observe(viewLifecycleOwner,
-            Observer { loginResult ->
-                loginResult ?: return@Observer
-                loadingProgressBar.visibility = View.GONE
-                loginResult.error?.let {
-                    showLoginFailed(it)
-                }
-                loginResult.success?.let {
-                    updateUiWithUser(it)
-                    requireActivity().actionBar?.setDisplayHomeAsUpEnabled(false)
-                    navigateToTransactionFragment()
-                }
-            })
-
-        val afterTextChangedListener = object : TextWatcher {
-            override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
-                // ignore
-            }
-
-            override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
-                // ignore
-            }
-
-            override fun afterTextChanged(s: Editable) {
-                loginViewModel.loginDataChanged(
-                    emailEditText.text.toString(),
-                    passwordEditText.text.toString()
-                )
-            }
-        }
-        emailEditText.addTextChangedListener(afterTextChangedListener)
-        passwordEditText.addTextChangedListener(afterTextChangedListener)
-        passwordEditText.setOnEditorActionListener { _, actionId, _ ->
-            if (actionId == EditorInfo.IME_ACTION_DONE) {
-                loginViewModel.login(
-                    emailEditText.text.toString(),
-                    passwordEditText.text.toString()
-                )
-            }
-            false
-        }
-
-        loginButton.setOnClickListener {
-            loadingProgressBar.visibility = View.VISIBLE
-            loginViewModel.login(
-                emailEditText.text.toString(),
-                passwordEditText.text.toString()
-            )
-        }
-    }
-
-    private fun updateUiWithUser(model: LoggedInUserView) {
-        val welcome = getString(R.string.welcome) + "\n" + model.email
-        // TODO : initiate successful logged in experience
-        val appContext = context?.applicationContext ?: return
-        Toast.makeText(appContext, welcome, Toast.LENGTH_LONG).show()
-    }
-
-    private fun showLoginFailed(@StringRes errorString: Int) {
-        val appContext = context?.applicationContext ?: return
-        Toast.makeText(appContext, errorString, Toast.LENGTH_LONG).show()
-    }
-
-    override fun onDestroyView() {
-        super.onDestroyView()
-        _binding = null
-    }
-
-    private fun navigateToTransactionFragment() {
-        findNavController().navigate(R.id.navigation_transactions)
-    }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/bondoyap/ui/settings/SettingsFragment.kt b/app/src/main/java/com/example/bondoyap/ui/settings/SettingsFragment.kt
index 774af8c..01acfc9 100644
--- a/app/src/main/java/com/example/bondoyap/ui/settings/SettingsFragment.kt
+++ b/app/src/main/java/com/example/bondoyap/ui/settings/SettingsFragment.kt
@@ -1,14 +1,14 @@
 package com.example.bondoyap.ui.settings
 
+import android.content.Intent
 import android.os.Bundle
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
 import androidx.fragment.app.Fragment
 import androidx.lifecycle.ViewModelProvider
-import androidx.navigation.fragment.findNavController
-import com.example.bondoyap.R
 import com.example.bondoyap.databinding.FragmentSettingsBinding
+import com.example.bondoyap.ui.login.LoginActivity
 
 class SettingsFragment : Fragment() {
 
@@ -44,7 +44,12 @@ class SettingsFragment : Fragment() {
         logoutButton.setOnClickListener {
             settingsViewModel.getUser()
             settingsViewModel.logout()
-            findNavController().navigate(R.id.navigation_login)
+//            findNavController().navigate(R.id.navigation_login)
+
+            val activity = requireActivity()
+            val intent = Intent(activity, LoginActivity::class.java)
+            activity.startActivity(intent)
+            activity.finish()
         }
     }
 
diff --git a/app/src/main/res/layout/fragment_login.xml b/app/src/main/res/layout-w1240dp/activity_login.xml
similarity index 81%
rename from app/src/main/res/layout/fragment_login.xml
rename to app/src/main/res/layout-w1240dp/activity_login.xml
index 3835370..cf19409 100644
--- a/app/src/main/res/layout/fragment_login.xml
+++ b/app/src/main/res/layout-w1240dp/activity_login.xml
@@ -2,21 +2,20 @@
 <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/container"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:paddingLeft="@dimen/fragment_horizontal_margin"
-    android:paddingTop="@dimen/fragment_vertical_margin"
-    android:paddingRight="@dimen/fragment_horizontal_margin"
-    android:paddingBottom="@dimen/fragment_vertical_margin"
-    tools:context=".ui.login.LoginFragment">
+    android:paddingLeft="@dimen/activity_horizontal_margin"
+    android:paddingTop="@dimen/activity_vertical_margin"
+    android:paddingRight="@dimen/activity_horizontal_margin"
+    android:paddingBottom="@dimen/activity_vertical_margin"
+    tools:context=".ui.login.LoginActivity">
 
     <EditText
         android:id="@+id/email"
         android:layout_width="0dp"
         android:layout_height="wrap_content"
-        android:layout_marginStart="24dp"
         android:layout_marginTop="96dp"
-        android:layout_marginEnd="24dp"
         android:autofillHints="@string/prompt_email"
         android:hint="@string/prompt_email"
         android:inputType="textEmailAddress"
@@ -29,9 +28,7 @@
         android:id="@+id/password"
         android:layout_width="0dp"
         android:layout_height="wrap_content"
-        android:layout_marginStart="24dp"
         android:layout_marginTop="8dp"
-        android:layout_marginEnd="24dp"
         android:autofillHints="@string/prompt_password"
         android:hint="@string/prompt_password"
         android:imeActionLabel="@string/action_sign_in_short"
@@ -47,9 +44,7 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_gravity="start"
-        android:layout_marginStart="48dp"
         android:layout_marginTop="16dp"
-        android:layout_marginEnd="48dp"
         android:layout_marginBottom="64dp"
         android:enabled="false"
         android:text="@string/action_sign_in"
@@ -64,9 +59,7 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_gravity="center"
-        android:layout_marginStart="32dp"
         android:layout_marginTop="64dp"
-        android:layout_marginEnd="32dp"
         android:layout_marginBottom="64dp"
         android:visibility="gone"
         app:layout_constraintBottom_toBottomOf="parent"
@@ -74,4 +67,5 @@
         app:layout_constraintStart_toStartOf="@+id/password"
         app:layout_constraintTop_toTopOf="parent"
         app:layout_constraintVertical_bias="0.3" />
+
 </androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout-w936dp/activity_login.xml b/app/src/main/res/layout-w936dp/activity_login.xml
new file mode 100644
index 0000000..0b64788
--- /dev/null
+++ b/app/src/main/res/layout-w936dp/activity_login.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/container"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:paddingLeft="@dimen/activity_horizontal_margin"
+    android:paddingTop="@dimen/activity_vertical_margin"
+    android:paddingRight="@dimen/activity_horizontal_margin"
+    android:paddingBottom="@dimen/activity_vertical_margin"
+    tools:context=".ui.login.LoginActivity">
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:layout_width="840dp"
+        android:layout_height="match_parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent">
+
+        <EditText
+            android:id="@+id/email"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="96dp"
+            android:autofillHints="@string/prompt_email"
+            android:hint="@string/prompt_email"
+            android:inputType="textEmailAddress"
+            android:selectAllOnFocus="true"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent" />
+
+        <EditText
+            android:id="@+id/password"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="8dp"
+            android:autofillHints="@string/prompt_password"
+            android:hint="@string/prompt_password"
+            android:imeActionLabel="@string/action_sign_in_short"
+            android:imeOptions="actionDone"
+            android:inputType="textPassword"
+            android:selectAllOnFocus="true"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toBottomOf="@+id/email" />
+
+        <Button
+            android:id="@+id/login"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="start"
+            android:layout_marginTop="16dp"
+            android:layout_marginBottom="64dp"
+            android:enabled="false"
+            android:text="@string/action_sign_in"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toBottomOf="@+id/password"
+            app:layout_constraintVertical_bias="0.2" />
+
+        <ProgressBar
+            android:id="@+id/loading"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:layout_marginTop="64dp"
+            android:layout_marginBottom="64dp"
+            android:visibility="gone"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="@+id/password"
+            app:layout_constraintStart_toStartOf="@+id/password"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintVertical_bias="0.3" />
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
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 0000000..cf19409
--- /dev/null
+++ b/app/src/main/res/layout/activity_login.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/container"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:paddingLeft="@dimen/activity_horizontal_margin"
+    android:paddingTop="@dimen/activity_vertical_margin"
+    android:paddingRight="@dimen/activity_horizontal_margin"
+    android:paddingBottom="@dimen/activity_vertical_margin"
+    tools:context=".ui.login.LoginActivity">
+
+    <EditText
+        android:id="@+id/email"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="96dp"
+        android:autofillHints="@string/prompt_email"
+        android:hint="@string/prompt_email"
+        android:inputType="textEmailAddress"
+        android:selectAllOnFocus="true"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
+
+    <EditText
+        android:id="@+id/password"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="8dp"
+        android:autofillHints="@string/prompt_password"
+        android:hint="@string/prompt_password"
+        android:imeActionLabel="@string/action_sign_in_short"
+        android:imeOptions="actionDone"
+        android:inputType="textPassword"
+        android:selectAllOnFocus="true"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@+id/email" />
+
+    <Button
+        android:id="@+id/login"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="start"
+        android:layout_marginTop="16dp"
+        android:layout_marginBottom="64dp"
+        android:enabled="false"
+        android:text="@string/action_sign_in"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@+id/password"
+        app:layout_constraintVertical_bias="0.2" />
+
+    <ProgressBar
+        android:id="@+id/loading"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:layout_marginTop="64dp"
+        android:layout_marginBottom="64dp"
+        android:visibility="gone"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="@+id/password"
+        app:layout_constraintStart_toStartOf="@+id/password"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintVertical_bias="0.3" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/app/src/main/res/navigation/mobile_navigation.xml b/app/src/main/res/navigation/mobile_navigation.xml
index efa5003..4412b02 100644
--- a/app/src/main/res/navigation/mobile_navigation.xml
+++ b/app/src/main/res/navigation/mobile_navigation.xml
@@ -5,11 +5,11 @@
     android:id="@+id/mobile_navigation"
     app:startDestination="@+id/navigation_transactions">
 
-    <fragment
-        android:id="@+id/navigation_login"
-        android:name="com.example.bondoyap.ui.login.LoginFragment"
-        android:label="@string/title_login"
-        tools:layout="@layout/fragment_transactions" />
+<!--    <fragment-->
+<!--        android:id="@+id/navigation_login"-->
+<!--        android:name="com.example.bondoyap.ui.login.LoginActivity"-->
+<!--        android:label="@string/title_login"-->
+<!--        tools:layout="@layout/fragment_transactions" />-->
 
     <fragment
         android:id="@+id/navigation_transactions"
diff --git a/app/src/main/res/values-land/dimens.xml b/app/src/main/res/values-land/dimens.xml
new file mode 100644
index 0000000..5f681ae
--- /dev/null
+++ b/app/src/main/res/values-land/dimens.xml
@@ -0,0 +1,3 @@
+<resources>
+    <dimen name="activity_horizontal_margin">48dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/app/src/main/res/values-w1240dp/dimens.xml b/app/src/main/res/values-w1240dp/dimens.xml
new file mode 100644
index 0000000..7e06511
--- /dev/null
+++ b/app/src/main/res/values-w1240dp/dimens.xml
@@ -0,0 +1,3 @@
+<resources>
+    <dimen name="activity_horizontal_margin">200dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/app/src/main/res/values-w600dp/dimens.xml b/app/src/main/res/values-w600dp/dimens.xml
new file mode 100644
index 0000000..5f681ae
--- /dev/null
+++ b/app/src/main/res/values-w600dp/dimens.xml
@@ -0,0 +1,3 @@
+<resources>
+    <dimen name="activity_horizontal_margin">48dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 05bf732..e7666cd 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -12,7 +12,8 @@
     <string name="action_sign_in_short">Sign in</string>
     <string name="welcome">"Welcome!"</string>
     <string name="invalid_username">Not a valid email</string>
-    <string name="invalid_password">Password must be >5 characters</string>
+    <string name="invalid_password">Password must not empty</string>
     <string name="login_failed">"Login failed"</string>
     <string name="logout">Logout</string>
+    <string name="title_activity_login">Login</string>
 </resources>
\ No newline at end of file
-- 
GitLab