Skip to content
This repository was archived by the owner on Dec 16, 2023. It is now read-only.

Commit e23d410

Browse files
committed
Add password reset flow
1 parent e6261d2 commit e23d410

13 files changed

Lines changed: 370 additions & 11 deletions

File tree

app/src/main/AndroidManifest.xml

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,21 @@
1515
android:supportsRtl="false"
1616
android:theme="@style/AppTheme"
1717
tools:ignore="GoogleAppIndexingWarning">
18+
<activity
19+
android:name=".ui.resetPassword.ResetPasswordActivity"
20+
android:theme="@style/AppTheme.NoBar">
21+
<intent-filter android:autoVerify="true">
22+
<action android:name="android.intent.action.VIEW" />
23+
24+
<category android:name="android.intent.category.DEFAULT" />
25+
<category android:name="android.intent.category.BROWSABLE" />
26+
27+
<data
28+
android:host="justjava.store"
29+
android:pathPattern="/reset-password"
30+
android:scheme="https" />
31+
</intent-filter>
32+
</activity>
1833
<activity android:name=".ui.payCard.PayCardActivity" />
1934
<activity android:name=".ui.payMpesa.PayMpesaActivity" />
2035
<activity android:name=".ui.orderDetail.OrderDetailActivity" />
@@ -24,10 +39,10 @@
2439
<activity android:name=".ui.checkout.CheckoutActivity" />
2540
<activity
2641
android:name=".ui.addAddress.AddAddressActivity"
42+
android:configChanges="orientation|keyboardHidden"
2743
android:parentActivityName=".ui.addressBook.AddressBookActivity"
2844
android:screenOrientation="portrait"
29-
android:configChanges="orientation|keyboardHidden"
30-
android:theme="@style/AppTheme.TranslucentStatus"/>
45+
android:theme="@style/AppTheme.TranslucentStatus" />
3146
<activity
3247
android:name=".ui.addressBook.AddressBookActivity"
3348
android:parentActivityName=".ui.profile.ProfileActivity" />
@@ -90,7 +105,6 @@
90105
<meta-data
91106
android:name="preloaded_fonts"
92107
android:resource="@array/preloaded_fonts" />
93-
94108
<meta-data
95109
android:name="com.google.android.geo.API_KEY"
96110
android:value="@string/google_maps_key" />

app/src/main/java/com/marknkamau/justjava/ui/login/SignInActivity.kt

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@ package com.marknkamau.justjava.ui.login
22

33
import android.content.Intent
44
import android.os.Bundle
5+
import android.view.LayoutInflater
56
import android.view.View
7+
import android.widget.EditText
8+
import android.widget.Toast
9+
import androidx.appcompat.app.AlertDialog
610
import androidx.appcompat.app.AppCompatActivity
711
import androidx.lifecycle.Observer
812
import com.google.android.gms.auth.api.signin.GoogleSignIn
@@ -32,6 +36,9 @@ class SignInActivity : AppCompatActivity() {
3236

3337
tilEmail.resetErrorOnChange(etEmail)
3438
tilPassword.resetErrorOnChange(etPassword)
39+
tvForgotPassword.setOnClickListener {
40+
showForgotPasswordDialog()
41+
}
3542
btnSignInGoogle.setOnClickListener {
3643
startActivityForResult(googleSignInClient.signInIntent, RC_SIGN_IN)
3744
hideKeyboard()
@@ -70,9 +77,41 @@ class SignInActivity : AppCompatActivity() {
7077
}
7178
}
7279

80+
private fun showForgotPasswordDialog() {
81+
val dialog = AlertDialog.Builder(this)
82+
dialog.setTitle("Reset password")
83+
84+
val view =
85+
LayoutInflater.from(this).inflate(R.layout.dialog_reset_password, findViewById(android.R.id.content), false)
86+
val input: EditText = view.findViewById(R.id.etEmailAddress)
87+
dialog.setView(view)
88+
89+
dialog.setPositiveButton("Reset") { _, _ ->
90+
Timber.d(input.text.toString())
91+
val email = input.text.toString().trim()
92+
if (email.isNotEmpty()) {
93+
requestPasswordReset(email)
94+
}
95+
}
96+
dialog.setNegativeButton("Cancel") { d, _ ->
97+
d.cancel()
98+
}
99+
100+
dialog.show()
101+
}
102+
103+
private fun requestPasswordReset(email: String) {
104+
signInViewModel.requestPasswordReset(email).observe(this, Observer { resource ->
105+
when (resource) {
106+
is Resource.Success -> toast("A password reset email has been sent")
107+
is Resource.Failure -> toast(resource.message, Toast.LENGTH_LONG)
108+
}
109+
})
110+
}
111+
73112
private fun signIn() {
74113
signInViewModel.signIn(etEmail.trimmedText, etPassword.trimmedText).observe(this, Observer { resource ->
75-
when(resource){
114+
when (resource) {
76115
is Resource.Success -> finish()
77116
is Resource.Failure -> toast(resource.message)
78117
}

app/src/main/java/com/marknkamau/justjava/ui/login/SignInViewModel.kt

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import androidx.lifecycle.LiveData
44
import androidx.lifecycle.MutableLiveData
55
import androidx.lifecycle.ViewModel
66
import androidx.lifecycle.viewModelScope
7+
import com.marknjunge.core.data.model.ApiResponse
78
import com.marknjunge.core.data.model.Resource
89
import com.marknjunge.core.data.model.User
910
import com.marknjunge.core.data.repository.AuthRepository
@@ -24,7 +25,7 @@ class SignInViewModel(
2425
_loading.value = true
2526
liveData.value = authRepository.signInWithGoogle(idToken)
2627

27-
if(liveData.value is Resource.Success){
28+
if (liveData.value is Resource.Success) {
2829
usersRepository.updateFcmToken()
2930
}
3031

@@ -41,7 +42,7 @@ class SignInViewModel(
4142
_loading.value = true
4243
liveData.value = authRepository.signIn(email, password)
4344

44-
if(liveData.value is Resource.Success){
45+
if (liveData.value is Resource.Success) {
4546
usersRepository.updateFcmToken()
4647
}
4748

@@ -50,4 +51,16 @@ class SignInViewModel(
5051

5152
return liveData
5253
}
54+
55+
fun requestPasswordReset(email: String): LiveData<Resource<ApiResponse>> {
56+
val liveData = MutableLiveData<Resource<ApiResponse>>()
57+
58+
viewModelScope.launch {
59+
_loading.value = true
60+
liveData.value = authRepository.requestPasswordReset(email)
61+
_loading.value = false
62+
}
63+
64+
return liveData
65+
}
5366
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package com.marknkamau.justjava.ui.resetPassword
2+
3+
import android.content.Intent
4+
import android.os.Bundle
5+
import android.view.View
6+
import android.widget.Toast
7+
import androidx.appcompat.app.AppCompatActivity
8+
import androidx.core.app.TaskStackBuilder
9+
import com.marknjunge.core.data.model.Resource
10+
import com.marknjunge.core.data.repository.AuthRepository
11+
import com.marknkamau.justjava.R
12+
import com.marknkamau.justjava.ui.login.SignInActivity
13+
import com.marknkamau.justjava.ui.main.MainActivity
14+
import com.marknkamau.justjava.utils.resetErrorOnChange
15+
import com.marknkamau.justjava.utils.toast
16+
import kotlinx.android.synthetic.main.activity_reset_password.*
17+
import kotlinx.coroutines.CoroutineScope
18+
import kotlinx.coroutines.Dispatchers
19+
import kotlinx.coroutines.launch
20+
import org.koin.android.ext.android.inject
21+
22+
class ResetPasswordActivity : AppCompatActivity() {
23+
24+
private val authRepository: AuthRepository by inject()
25+
private val uiScope = CoroutineScope(Dispatchers.Main)
26+
private lateinit var token: String
27+
28+
override fun onCreate(savedInstanceState: Bundle?) {
29+
super.onCreate(savedInstanceState)
30+
setContentView(R.layout.activity_reset_password)
31+
32+
intent.data?.getQueryParameter("token")?.let { token ->
33+
this.token = token
34+
} ?: showError()
35+
36+
tilnewPassword.resetErrorOnChange(etNewPassword)
37+
38+
btnChangePassword.setOnClickListener {
39+
resetPassword()
40+
}
41+
}
42+
43+
private fun showError() {
44+
layoutResetPassword.visibility = View.GONE
45+
layoutResetPasswordError.visibility = View.VISIBLE
46+
}
47+
48+
private fun resetPassword() {
49+
val newPassword = etNewPassword.text.toString().trim()
50+
if (newPassword.isEmpty()) {
51+
tilnewPassword.error = getString(R.string.required)
52+
return
53+
}
54+
55+
uiScope.launch {
56+
pbLoading.visibility = View.VISIBLE
57+
when (val resource = authRepository.resetPassword(token, newPassword)) {
58+
is Resource.Success -> {
59+
toast("Your password has been changed")
60+
val intent = Intent(this@ResetPasswordActivity, SignInActivity::class.java)
61+
TaskStackBuilder.create(this@ResetPasswordActivity)
62+
.addNextIntentWithParentStack(Intent(this@ResetPasswordActivity, MainActivity::class.java))
63+
.addNextIntentWithParentStack(intent)
64+
.startActivities()
65+
}
66+
is Resource.Failure -> toast(resource.message, Toast.LENGTH_LONG)
67+
}
68+
pbLoading.visibility = View.GONE
69+
}
70+
}
71+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
android:width="24dp"
3+
android:height="24dp"
4+
android:viewportWidth="24.0"
5+
android:viewportHeight="24.0">
6+
<path
7+
android:fillColor="@color/colorTextSecondary"
8+
android:pathData="M11,15h2v2h-2zM11,7h2v6h-2zM11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z"/>
9+
</vector>
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
3+
xmlns:app="http://schemas.android.com/apk/res-auto"
4+
xmlns:tools="http://schemas.android.com/tools"
5+
android:layout_width="match_parent"
6+
android:layout_height="match_parent">
7+
8+
<LinearLayout
9+
android:id="@+id/layoutResetPasswordError"
10+
android:layout_width="match_parent"
11+
android:layout_height="match_parent"
12+
android:gravity="center"
13+
android:orientation="vertical"
14+
android:visibility="gone">
15+
16+
<TextView
17+
android:layout_width="wrap_content"
18+
android:layout_height="wrap_content"
19+
android:fontFamily="@font/bree_serif"
20+
android:text="@string/app_name"
21+
android:textColor="@color/colorPrimary"
22+
android:textSize="48sp" />
23+
24+
<ImageView
25+
android:id="@+id/imageView8"
26+
android:layout_width="48dp"
27+
android:layout_height="48dp"
28+
android:layout_marginTop="32dp"
29+
app:srcCompat="@drawable/ic_error" />
30+
31+
<TextView
32+
android:id="@+id/textView2"
33+
android:layout_width="wrap_content"
34+
android:layout_height="wrap_content"
35+
android:layout_marginTop="16dp"
36+
android:gravity="center"
37+
android:text="@string/reset_password_error_message"
38+
android:textColor="@color/colorTextPrimary"
39+
android:textSize="16sp" />
40+
</LinearLayout>
41+
42+
<androidx.constraintlayout.widget.ConstraintLayout
43+
android:id="@+id/layoutResetPassword"
44+
android:layout_width="match_parent"
45+
android:layout_height="match_parent"
46+
tools:context="com.marknkamau.justjava.ui.resetPassword.ResetPasswordActivity">
47+
48+
<ProgressBar
49+
android:id="@+id/pbLoading"
50+
style="?android:attr/progressBarStyleHorizontal"
51+
android:layout_width="0dp"
52+
android:layout_height="wrap_content"
53+
android:indeterminate="true"
54+
android:visibility="gone"
55+
app:layout_constraintEnd_toEndOf="parent"
56+
app:layout_constraintStart_toStartOf="parent"
57+
app:layout_constraintTop_toTopOf="parent"
58+
tools:visibility="visible" />
59+
60+
<ImageView
61+
android:id="@+id/imageView"
62+
android:layout_width="72dp"
63+
android:layout_height="72dp"
64+
android:layout_marginStart="16dp"
65+
android:contentDescription="@string/app_icon_content_desc"
66+
android:tint="@color/colorPrimary"
67+
app:layout_constraintBottom_toTopOf="@+id/textView20"
68+
app:layout_constraintStart_toStartOf="parent"
69+
app:layout_constraintTop_toTopOf="parent"
70+
app:layout_constraintVertical_chainStyle="packed"
71+
app:srcCompat="@drawable/ic_just_java_logo" />
72+
73+
<TextView
74+
android:id="@+id/textView20"
75+
android:layout_width="wrap_content"
76+
android:layout_height="wrap_content"
77+
android:layout_marginStart="16dp"
78+
android:layout_marginBottom="8dp"
79+
android:fontFamily="@font/bree_serif"
80+
android:text="@string/app_name"
81+
android:textColor="@color/colorPrimary"
82+
android:textSize="48sp"
83+
app:layout_constraintBottom_toTopOf="@+id/guideline"
84+
app:layout_constraintStart_toStartOf="parent"
85+
app:layout_constraintTop_toBottomOf="@+id/imageView" />
86+
87+
<TextView
88+
android:id="@+id/textView9"
89+
style="@style/AppTheme.Text.18Primary"
90+
android:layout_width="wrap_content"
91+
android:layout_height="wrap_content"
92+
android:layout_marginStart="16dp"
93+
android:text="Enter your new password"
94+
app:layout_constraintBottom_toTopOf="@+id/guideline"
95+
app:layout_constraintStart_toStartOf="parent" />
96+
97+
<androidx.constraintlayout.widget.Guideline
98+
android:id="@+id/guideline"
99+
android:layout_width="wrap_content"
100+
android:layout_height="wrap_content"
101+
android:orientation="horizontal"
102+
app:layout_constraintGuide_percent="0.4" />
103+
104+
<com.google.android.material.textfield.TextInputLayout
105+
android:id="@+id/tilnewPassword"
106+
style="@style/AppTheme.InputField"
107+
android:layout_width="0dp"
108+
android:layout_height="wrap_content"
109+
android:layout_marginStart="16dp"
110+
android:layout_marginTop="8dp"
111+
android:layout_marginEnd="16dp"
112+
android:hint="@string/password"
113+
app:passwordToggleEnabled="true"
114+
app:layout_constraintEnd_toEndOf="parent"
115+
app:layout_constraintStart_toStartOf="parent"
116+
app:layout_constraintTop_toTopOf="@+id/guideline">
117+
118+
<com.google.android.material.textfield.TextInputEditText
119+
android:id="@+id/etNewPassword"
120+
android:layout_width="match_parent"
121+
android:layout_height="wrap_content"
122+
android:inputType="textPassword" />
123+
</com.google.android.material.textfield.TextInputLayout>
124+
125+
<Button
126+
android:id="@+id/btnChangePassword"
127+
android:layout_width="0dp"
128+
android:layout_height="56dp"
129+
android:layout_marginStart="32dp"
130+
android:layout_marginTop="32dp"
131+
android:layout_marginEnd="32dp"
132+
android:text="Change Password"
133+
app:layout_constraintEnd_toEndOf="parent"
134+
app:layout_constraintStart_toStartOf="parent"
135+
app:layout_constraintTop_toBottomOf="@+id/tilnewPassword" />
136+
</androidx.constraintlayout.widget.ConstraintLayout>
137+
138+
</FrameLayout>

0 commit comments

Comments
 (0)