Welcome to the fourth part of the Firebase Authentication series. In the previous part I showed you how to register users and let them log in and log out. In this part we will make it possible for users to reset their password. When a user clicks on the TextView in the login fragment that says "Forgot your password? Click here to reset it", a popup will show up with an EditText where the user will be able to input his or her email. It will look something like this:
The AlertDialog will inflate a view that only consists of one element, an EditText. Create a new xml file in res/layout and name it dialog_reset_password.xml. Fill it with the code below.
dialog_reset_password.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<EditText
android:id="@+id/email_txt"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Email..."
android:inputType="textEmailAddress"
android:layout_marginTop="20dp"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
android:layout_marginBottom="5dp"/>
</LinearLayout>
Now create a new Java file in the DialogFragments folder and name it ResetPasswordDialog.java.
ResetPasswordDialog.java
package com.frogitecture.authenticatedgoose.DialogFragments;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.EditText;
import com.frogitecture.authenticatedgoose.R;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.DialogFragment;
public class ResetPasswordDialog extends DialogFragment {
public interface ResetPasswordListener {
void onResetPassword(String email);
}
public ResetPasswordListener listener;
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
LayoutInflater inflater = requireActivity().getLayoutInflater();
View view = inflater.inflate(R.layout.dialog_reset_password, null);
final EditText emailTxt = view.findViewById(R.id.email_txt);
builder.setView(view)
.setTitle("Send Reset Password Email")
.setPositiveButton("Send", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
String email = emailTxt.getText().toString();
listener.onResetPassword(email);
}
})
.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {}
});
return builder.create();
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
try {
listener = (ResetPasswordListener) context;
} catch (ClassCastException e) {
throw new ClassCastException(getActivity().toString() + " must implement the listener you silly goose");
}
}
}
If you're not familiar with AlertDialog you can read more about it here. This class have a public interface ResetPasswordListener with the method onResetPassword(String email) which is called when the user click on the send button. The argument email will be the text from the EditText we just created. Notice that the onClick method connected to the cancel button is empty. This is because when cancel is clicked, the popup is automatically dismissed. It's ime to open up LoginFragment.
LoginFragment.java
package com.frogitecture.authenticatedgoose.Fragments;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import com.frogitecture.authenticatedgoose.AuthActivity;
import com.frogitecture.authenticatedgoose.DialogFragments.LoadingDialog;
import com.frogitecture.authenticatedgoose.DialogFragments.ResetPasswordDialog;
import com.frogitecture.authenticatedgoose.MainActivity;
import com.frogitecture.authenticatedgoose.R;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.auth.AuthResult;
import com.google.firebase.auth.FirebaseAuth;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
public class LoginFragment extends Fragment {
private LoadingDialog loadingDialog;
private FirebaseAuth firebaseAuth;
private TextView emailTxt;
private TextView passwordTxt;
private TextView registerTxt;
private Button signInBtn;
// Add this line
private TextView forgotPasswordTxt;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View parentView = inflater.inflate(R.layout.fragment_login, container, false);
loadingDialog = new LoadingDialog();
firebaseAuth = FirebaseAuth.getInstance();
emailTxt = parentView.findViewById(R.id.email_txt);
passwordTxt = parentView.findViewById(R.id.password_txt);
registerTxt = parentView.findViewById(R.id.not_registered_txt);
signInBtn = parentView.findViewById(R.id.login_btn);
// And this line
forgotPasswordTxt = parentView.findViewById(R.id.forgot_password_txt);
registerTxt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
((AuthActivity)getActivity()).changeFragment(new RegisterFragment());
}
});
signInBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String email = emailTxt.getText().toString();
String password = passwordTxt.getText().toString();
signIn(email, password);
loadingDialog.setMessage("Signing in...");
loadingDialog.show(getActivity().getSupportFragmentManager(),"Signing In");
}
});
// And these lines
forgotPasswordTxt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ResetPasswordDialog resetPasswordDialog = new ResetPasswordDialog();
resetPasswordDialog.show(getActivity().getSupportFragmentManager(),"Change Display Name");
}
});
return parentView;
}
private void signIn(String email, String password) {
firebaseAuth.signInWithEmailAndPassword(email, password)
.addOnCompleteListener(getActivity(), new OnCompleteListener<AuthResult>() {
@Override
public void onComplete(@NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
getActivity().finish();
startActivity(new Intent(getActivity(), MainActivity.class));
} else {
Toast.makeText(getActivity(), "Authentication failed.", Toast.LENGTH_SHORT).show();
}
loadingDialog.dismiss();
}
});
}
}
A variable is added for for the reset password TextView:
private TextView forgotPasswordTxt;
We connect it to the xml element in onCreateView:
forgotPasswordTxt = parentView.findViewById(R.id.forgot_password_txt);
Then we add a click listener to that TextView. When it is clicked, the popup is created and displayed:
forgotPasswordTxt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ResetPasswordDialog resetPasswordDialog = new ResetPasswordDialog();
resetPasswordDialog.show(getActivity().getSupportFragmentManager(),"Change Display Name");
}
});
That is all for the LoginFragment. Since this is a fragment, ResetPasswordListener needs to be implemented in the base class, AuthActivity.
AuthActivity.java
package com.frogitecture.authenticatedgoose;
import android.os.Bundle;
import android.widget.Toast;
import com.frogitecture.authenticatedgoose.DialogFragments.ResetPasswordDialog;
import com.frogitecture.authenticatedgoose.Fragments.LoginFragment;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.auth.FirebaseAuth;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentTransaction;
// Implement ResetPasswordListener
public class AuthActivity extends AppCompatActivity implements ResetPasswordDialog.ResetPasswordListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_auth);
if (savedInstanceState != null) {
return;
}
LoginFragment loginFragment = new LoginFragment();
getSupportFragmentManager().beginTransaction().add(R.id.container, loginFragment).commit();
}
public void changeFragment(Fragment fragment) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.container, fragment);
transaction.addToBackStack(null);
transaction.commit();
}
// Implement the listeners method onResetPassword
@Override
public void onResetPassword(String email) {
FirebaseAuth auth = FirebaseAuth.getInstance();
auth.sendPasswordResetEmail(email)
.addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(@NonNull Task<Void> task) {
if (task.isSuccessful()) {
Toast.makeText(AuthActivity.this, "Email sent! Check your inbox", Toast.LENGTH_SHORT).show();
}
}
});
}
}
ResetPasswordListener is implemented:
public class AuthActivity extends AppCompatActivity implements ResetPasswordDialog.ResetPasswordListener
And the listeners method onResetPassword(String email) is implemented:
@Override
public void onResetPassword(String email) {
FirebaseAuth auth = FirebaseAuth.getInstance();
auth.sendPasswordResetEmail(email)
.addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(@NonNull Task<Void> task) {
if (task.isSuccessful()) {
Toast.makeText(AuthActivity.this, "Email sent! Check your inbox", Toast.LENGTH_SHORT).show();
}
}
});
}
In this method, an instance of FirebaseAuth is created, then we use that variable to call sendPasswordResetEmail, along with a complete listener. When an email is sent, we show the users a Toast message to inform them to check their inbox.
In the next part I will show you how to verify an email.
Author
2020-06-20
Changing a display name in Android Studio with firebase auth
Changing email in Android with Firebase Authentication
Changing a password in Android with Firebase Authentication
Deleting an account in Android with Firebase Authentication