diff --git a/app/src/main/java/deakin/gopher/guardian/services/NavigationService.kt b/app/src/main/java/deakin/gopher/guardian/services/NavigationService.kt index 255c2e34f..383e056ce 100644 --- a/app/src/main/java/deakin/gopher/guardian/services/NavigationService.kt +++ b/app/src/main/java/deakin/gopher/guardian/services/NavigationService.kt @@ -16,40 +16,21 @@ import deakin.gopher.guardian.view.general.TasksListActivity class NavigationService(val activity: Activity) { fun toHomeScreenForRole(role: Role) { - when (role) { - Role.Caretaker -> { - activity.startActivity( - Intent( - activity.applicationContext, - Homepage4caretaker::class.java, - ), - ) - } - - Role.Nurse -> { - activity.startActivity( - Intent( - activity.applicationContext, - Homepage4nurse::class.java, - ), - ) - } - - Role.Admin -> { - activity.startActivity( - Intent( - activity.applicationContext, - Homepage4admin::class.java, - ), - ) - } + val intent = when (role) { + Role.Caretaker -> Intent(activity, Homepage4caretaker::class.java) + Role.Nurse -> Intent(activity, Homepage4nurse::class.java) + Role.Admin -> Intent(activity, Homepage4admin::class.java) } + // Clear back stack so user cannot go back to Login/PIN screens + intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK + activity.startActivity(intent) + activity.finish() } fun toRegistration() { activity.startActivity( Intent( - activity.applicationContext, + activity, RegisterActivity::class.java, ), ) @@ -58,25 +39,23 @@ class NavigationService(val activity: Activity) { fun onSettings() { activity.startActivity( Intent( - activity.applicationContext, + activity, Setting::class.java, ), ) } fun onSignOut() { - activity.startActivity( - Intent( - activity.applicationContext, - LoginActivity::class.java, - ), - ) + val intent = Intent(activity, LoginActivity::class.java) + intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK + activity.startActivity(intent) + activity.finish() } fun onLaunchPatientList() { activity.startActivity( Intent( - activity.applicationContext, + activity, PatientListActivity::class.java, ), ) @@ -85,7 +64,7 @@ class NavigationService(val activity: Activity) { fun onLaunchTasks() { activity.startActivity( Intent( - activity.applicationContext, + activity, TasksListActivity::class.java, ), ) @@ -94,24 +73,26 @@ class NavigationService(val activity: Activity) { fun onLaunchTaskCreator() { activity.startActivity( Intent( - activity.applicationContext, + activity, TaskAddActivity::class.java, ), ) } fun toLogin() { - activity.startActivity( - Intent( - activity.applicationContext, - LoginActivity::class.java, - ), - ) + val intent = Intent(activity, LoginActivity::class.java) + // If coming from Registration, we want to clear the Registration screen from stack + intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP + activity.startActivity(intent) + activity.finish() } fun toPinCodeActivity(role: Role) { - val intent = Intent(activity.applicationContext, PinCodeActivity::class.java) + val intent = Intent(activity, PinCodeActivity::class.java) intent.putExtra("role", role) activity.startActivity(intent) + // We keep LoginActivity in the stack in case user wants to go back from PIN screen? + // Actually, usually you'd want to finish() it too if PIN is mandatory. + // If we want the back button on PIN screen to go back to Login, we don't finish() here. } } diff --git a/app/src/main/java/deakin/gopher/guardian/view/general/LoginActivity.kt b/app/src/main/java/deakin/gopher/guardian/view/general/LoginActivity.kt index c03d645d6..660de7c99 100644 --- a/app/src/main/java/deakin/gopher/guardian/view/general/LoginActivity.kt +++ b/app/src/main/java/deakin/gopher/guardian/view/general/LoginActivity.kt @@ -10,6 +10,7 @@ import android.widget.ProgressBar import android.widget.TextView import android.widget.Toast import androidx.appcompat.app.AlertDialog +import androidx.core.widget.addTextChangedListener import com.google.android.gms.auth.api.signin.GoogleSignIn import com.google.android.gms.auth.api.signin.GoogleSignInAccount import com.google.android.gms.auth.api.signin.GoogleSignInClient @@ -17,6 +18,7 @@ import com.google.android.gms.auth.api.signin.GoogleSignInOptions import com.google.android.gms.common.SignInButton import com.google.android.gms.common.api.ApiException import com.google.android.gms.tasks.Task +import com.google.android.material.textfield.TextInputLayout import com.google.firebase.auth.FirebaseAuth import com.google.firebase.auth.GoogleAuthProvider import com.google.gson.Gson @@ -44,6 +46,8 @@ class LoginActivity : BaseActivity() { setContentView(R.layout.activity_login) val mEmail: EditText = findViewById(R.id.Email) val mPassword: EditText = findViewById(R.id.password) + val emailLayout: TextInputLayout = findViewById(R.id.emailTextInputLayout) + val passwordLayout: TextInputLayout = findViewById(R.id.passwordTextInputLayout) val progressBar: ProgressBar = findViewById(R.id.progressBar) val loginButton: Button = findViewById(R.id.loginBtn) val loginGoogleButton: SignInButton = findViewById(R.id.loginGoogleBtn) @@ -56,23 +60,30 @@ class LoginActivity : BaseActivity() { .build() gsoClient = GoogleSignIn.getClient(this, gso) + mEmail.addTextChangedListener { emailLayout.error = null } + mPassword.addTextChangedListener { passwordLayout.error = null } + loginButton.setOnClickListener { - progressBar.show() val emailInput = mEmail.text.toString().trim { it <= ' ' } val passwordInput = mPassword.text.toString().trim { it <= ' ' } - val loginValidationError = validateInputs(emailInput) + val loginValidationError = validateInputs(emailInput, passwordInput) if (loginValidationError != null) { - progressBar.hide() - Toast.makeText( - applicationContext, - loginValidationError.messageResoureId, - Toast.LENGTH_LONG, - ).show() + when (loginValidationError) { + LoginValidationError.EmptyEmail, LoginValidationError.InvalidEmail -> { + emailLayout.error = getString(loginValidationError.messageResoureId) + } + LoginValidationError.EmptyPassword -> { + passwordLayout.error = getString(loginValidationError.messageResoureId) + } + else -> showMessage(getString(loginValidationError.messageResoureId)) + } return@setOnClickListener } + setLoading(true, loginButton, progressBar) + val call = ApiClient.apiService.login(emailInput, passwordInput) call.enqueue( @@ -81,21 +92,15 @@ class LoginActivity : BaseActivity() { call: Call, response: Response, ) { - progressBar.hide() + setLoading(false, loginButton, progressBar) if (response.isSuccessful && response.body() != null) { - // Handle successful login val user = response.body()!!.user val token = response.body()!!.token SessionManager.createLoginSession(user, token) NavigationService(this@LoginActivity).toPinCodeActivity(user.role) } else { - // Handle error - val errorResponse = - Gson().fromJson( - response.errorBody()?.string(), - ApiErrorResponse::class.java, - ) - showMessage(errorResponse.apiError ?: response.message()) + val errorResponse = parseError(response) + showMessage(errorResponse ?: response.message()) } } @@ -103,9 +108,8 @@ class LoginActivity : BaseActivity() { call: Call, t: Throwable, ) { - // Handle failure - progressBar.hide() - showMessage(getString(R.string.toast_login_error, t.message)) + setLoading(false, loginButton, progressBar) + showMessage(getString(R.string.toast_login_error, t.localizedMessage)) } }, ) @@ -143,7 +147,29 @@ class LoginActivity : BaseActivity() { } } - private fun validateInputs(rawEmail: String?): LoginValidationError? { + private fun setLoading(isLoading: Boolean, button: Button, progressBar: ProgressBar) { + if (isLoading) { + button.isEnabled = false + progressBar.show() + } else { + button.isEnabled = true + progressBar.hide() + } + } + + private fun parseError(response: Response<*>): String? { + return try { + val errorResponse = Gson().fromJson( + response.errorBody()?.string(), + ApiErrorResponse::class.java, + ) + errorResponse.apiError + } catch (e: Exception) { + null + } + } + + private fun validateInputs(rawEmail: String?, rawPassword: String?): LoginValidationError? { if (rawEmail.isNullOrEmpty()) { return LoginValidationError.EmptyEmail } @@ -153,6 +179,10 @@ class LoginActivity : BaseActivity() { return LoginValidationError.InvalidEmail } + if (rawPassword.isNullOrEmpty()) { + return LoginValidationError.EmptyPassword + } + return null } @@ -170,13 +200,8 @@ class LoginActivity : BaseActivity() { ?: getString(R.string.toast_reset_link_sent_to_your_email), ) } else { - // Handle error - val errorResponse = - Gson().fromJson( - response.errorBody()?.string(), - ApiErrorResponse::class.java, - ) - showMessage(errorResponse.apiError ?: response.message()) + val errorResponse = parseError(response) + showMessage(errorResponse ?: response.message()) } } diff --git a/app/src/main/java/deakin/gopher/guardian/view/general/PinCodeActivity.kt b/app/src/main/java/deakin/gopher/guardian/view/general/PinCodeActivity.kt index 2659b6c0e..3a0678274 100644 --- a/app/src/main/java/deakin/gopher/guardian/view/general/PinCodeActivity.kt +++ b/app/src/main/java/deakin/gopher/guardian/view/general/PinCodeActivity.kt @@ -1,8 +1,13 @@ package deakin.gopher.guardian.view.general +import android.content.Context import android.os.Bundle import android.os.CountDownTimer +import android.text.Editable +import android.text.TextWatcher +import android.view.KeyEvent import android.view.View +import android.view.inputmethod.InputMethodManager import android.widget.Button import android.widget.EditText import android.widget.ProgressBar @@ -10,6 +15,7 @@ import android.widget.TextView import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.widget.LinearLayoutCompat +import androidx.core.view.isVisible import com.google.gson.Gson import deakin.gopher.guardian.R import deakin.gopher.guardian.model.ApiErrorResponse @@ -18,7 +24,6 @@ import deakin.gopher.guardian.model.login.Role import deakin.gopher.guardian.model.login.SessionManager import deakin.gopher.guardian.services.NavigationService import deakin.gopher.guardian.services.api.ApiClient -import deakin.gopher.guardian.view.hide import retrofit2.Call import retrofit2.Callback import retrofit2.Response @@ -28,34 +33,36 @@ class PinCodeActivity : AppCompatActivity() { private val userEmail = SessionManager.getCurrentUser().email private lateinit var progressBar: ProgressBar + private lateinit var submitButton: Button + private lateinit var resendButton: Button + private lateinit var pinDigits: List override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.login_enter_pin) userRole = intent.getSerializableExtra("role") as Role - val pinDigit1 = findViewById(R.id.pin_digit_1) - val pinDigit2 = findViewById(R.id.pin_digit_2) - val pinDigit3 = findViewById(R.id.pin_digit_3) - val pinDigit4 = findViewById(R.id.pin_digit_4) - val pinDigit5 = findViewById(R.id.pin_digit_5) - val pinDigit6 = findViewById(R.id.pin_digit_6) - val submitButton = findViewById