Published on

How to implement Login/Registration features in Android! Part 2

Welcome to Part 2 of the How to implement Login/Registration features in Android blog post series! We will be continuing where we left off in Part 1, so make sure to read Part 1 before continuing on with this post. With that being said, let's get building! 👷‍♂️🛠

Development

Based on where we left off in Part 1, now it's just a matter of implementing the necessary Activity classes in the activities package. So create the following classes in the activities package: LoginActivity, and RegisterActivity. Now add the following code to the matching Activity:

// RegisterActivity
public class RegisterActivity extends AppCompatActivity {

    private static final String TAG = RegisterActivity.class.getSimpleName();

    private DatabaseHelper openHelper;
    private Button registerBtn;
    private Button loginBtn;
    private EditText regUsername;
    private EditText regEmail;
    private EditText regPassword;
    private EditText regConfirmPassword;
    private EditText regFirstName;
    private EditText regLastName;


    @RequiresApi(Build.VERSION_CODES.M)
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_register);
        openHelper = new DatabaseHelper(this);
        registerBtn = findViewById(R.id.register_btn);
        loginBtn =  findViewById(R.id.login_btn);
        regUsername =  findViewById(R.id.username);
        regEmail =  findViewById(R.id.email);
        regPassword= findViewById(R.id.password);
        regConfirmPassword = findViewById(R.id.confirm_password);
        regFirstName = findViewById(R.id.first_name);
        regLastName = findViewById(R.id.last_name);

        registerBtn.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View view) {
                String fUsername = regUsername.getText().toString().trim();
                String fEmail = regEmail.getText().toString().trim();
                String fPassword = regPassword.getText().toString().trim();
                String fConfirmPassword = regConfirmPassword.getText().toString().trim();
                String fFirstName = regFirstName.getText().toString().trim();
                String fLastName = regLastName.getText().toString().trim();
                if (fUsername.isEmpty() || fPassword.isEmpty() || fConfirmPassword.isEmpty() || fFirstName.isEmpty() || fLastName.isEmpty()) {
                    Toast.makeText(
                            RegisterActivity.this, "Please fill all the details", Toast.LENGTH_SHORT).show();
                } else {
                    insertData(
                            fUsername,
                            fEmail,
                            fPassword,
                            fConfirmPassword,
                            fFirstName,
                            fLastName);
                    Toast.makeText(RegisterActivity.this, "Registration Successful", Toast.LENGTH_SHORT)
                    .show();
                }
            }
        });

        loginBtn.setOnClickListener(new View.OnClickListener(){
                @Override
                public void onClick(View view) {
                    Intent intent = new Intent(RegisterActivity.this, LoginActivity.class);
                    startActivity(intent);
                }
        });

    }

    public void insertData(String username, String email, String password, String confirmPassword, String firstName, String lastName){
        User newUser = new User();
        if (username != null) {
            newUser.setUsername(username);
        }
        if (email != null) {
            newUser.setEmail(email);
        }
        if (password != null) {
            newUser.setPassword(password);
        }
        if (firstName != null) {
            newUser.setFirstName(firstName);
        }
        if (lastName != null){
            newUser.setLastName(lastName);
        }

        if (password.equals(confirmPassword)) {
            String passwordSalt = BCrypt.gensalt();
            String hashedPassword = BCrypt.hashpw(password, passwordSalt);
            newUser.setPassword(hashedPassword);
            newUser.setPasswordSalt(passwordSalt);
            DatabaseHelper databaseHelper = new DatabaseHelper(this);
            AppDatabase db = databaseHelper.getDatabase(getApplicationContext());
            if (db != null) {
                db.getUserDAO().insertUser(newUser);
            }
        } else {
            Toast.makeText(
                    getApplicationContext(),
                    "Password does not match Confirm Password",
                    Toast.LENGTH_LONG
            ).show();
        }
    }
}
// LoginActivity
public class LoginActivity extends AppCompatActivity {

    private static final String TAG = LoginActivity.class.getSimpleName();

    private EditText inputUsernameField;
    private EditText inputPasswordField;
    private Button loginBtn;
    private Button registerBtn;
    private String username;
    private String password;
    private DatabaseHelper db;
    private SessionManager session;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);
        inputUsernameField = findViewById(R.id.input_username);
        inputPasswordField = findViewById(R.id.input_password);
        loginBtn = findViewById(R.id.login_btn);
        registerBtn = findViewById(R.id.register_btn);

        // create SQLite database
        db = new DatabaseHelper(this);
        session = new SessionManager(this);
        if (session.isLoggedIn()) {
            Intent intent = new Intent(LoginActivity.this, MainActivity.class);
            startActivity(intent);
        }

        initialize(inputUsernameField, inputPasswordField);
    }

    public void initialize(EditText inputUsernameField, EditText inputPasswordField) {
        loginBtn.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View view) {
                String username = inputUsernameField.getText().toString().trim();
                String password = inputPasswordField.getText().toString().trim();
                if(username != null && password != null){
                    loginProcess(username, password);
                } else {
                    Toast.makeText(getApplicationContext(),
                            "Please enter the credentials!",
                            Toast.LENGTH_LONG).show();
                }
            }
        });

        registerBtn.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(LoginActivity.this, RegisterActivity.class);
                startActivity(intent);
            }
        });
    }

    private void loginProcess(String username, String password) {
        String tag_string_req = "req_login";
        DialogFragment dialogFragment = showDialog("Logging in...");
        DatabaseHelper databaseHelper = new DatabaseHelper(getApplicationContext());
        String storedUserPassword = databaseHelper.getUserFieldSQLQuery(
                getApplicationContext(),
                UserFieldType.PASSWORD,
                username
        );
        String storedPasswordSalt = databaseHelper.getUserFieldSQLQuery(
                getApplicationContext(),
                UserFieldType.PASSWORD_SALT,
                username
        );


        new Timer().schedule(new TimerTask() {
            @Override
            public void run() {
                String message = null;
                if (storedUserPassword != null) {
                    String hashedInputPassword = BCrypt.hashpw(password, storedPasswordSalt);
                    if (hashedInputPassword.equals(storedUserPassword)) {
                        message = "Login successful!";
                    } else {
                        message = "Login failed! Please try again...";
                    }
                } else {
                    message = "Login failed! Please try again...";
                }
                String finalMessage = message;
                LoginActivity.this.runOnUiThread(() -> {
                    Toast.makeText(getApplicationContext(), finalMessage, Toast.LENGTH_LONG).show();
                });
            }
        }, 500);
    }


    private DialogFragment showDialog(String title){
        return Functions.showProgressDialog(LoginActivity.this, title);
    }
}

Now for a brief overview of the important points in both activities that we just added:

RegisterActivity

  • registerBtn.setOnClickListener(new View.OnClickListener(){ ... }): This click event extracts the user data inputted in the registration layout(which we still have to create...) and saves them into variables. Some validations to make sure the fields are not empty, are done after that. Finally, if valid data is received it then passed to the insertData() method. The insertData() method will ensure the user data get persisted to the database
  • loginBtn.setOnClickListener(new View.OnClickListener(){ ... }): This click uses Intents(to learn about Intent check out their Android Developers Documentation page) to transfer the user over to the Login page
  • insertData(): This method is used to persist user data to the database as a user table entry. Note that the password field will not be stored in plain text, as this is a major security breach, but hashed using the JBCrypt library. The password salt will also be stored alongside the password field to allow for hashes to be compared in the context of validating login passwords

LoginActivity

  • initialize(): Attaches an click listener to the Login button so that when it is pressed, the provided username and password can be extracted to be validated against the data in the user table. It also attaches a click listener to the Register button so that pressing it results in the user being sent to the Registration page
  • loginProcess(): Serves to validate user login attempt by comparing the provided password value against the one stored in the database. First, the stored password and password salt values are fetched from the database. Then, the stored password salt is used to hash the inputted password. Finally the newly created hash is compared with the stored password in order to determine the validity of the login attempt. The Timer class is used to create a 500 millisecond delay in order to display a loading screen, while the password validation takes place asynchronously, with the help of the showDialog() method

Ok we are nearing completion! We still have to create and build the layout files for the activities above, so let's do that by navigating to the res folder and creating the following files: activitiy_login.xml, activity_register.xml, dialog_progress.xml. Once that's done, add the following code to the respective files:

<!-- activity_login.xml -->

<?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:layout_width="match_parent"
    android:layout_height="match_parent">

    <EditText
        android:id="@+id/input_username"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="32dp"
        android:layout_marginBottom="44dp"
        android:ems="10"
        android:inputType="textPersonName"
        app:layout_constraintBottom_toTopOf="@+id/input_password"
        app:layout_constraintStart_toStartOf="parent" />

    <TextView
        android:id="@+id/textView6"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Username:"
        app:layout_constraintBottom_toTopOf="@+id/input_username"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.093"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.951" />

    <Button
        android:id="@+id/login_btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="52dp"
        android:text="Login"
        app:layout_constraintBottom_toTopOf="@+id/register_btn"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.095"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView5"
        app:layout_constraintVertical_bias="0.85" />

    <Button
        android:id="@+id/register_btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="32dp"
        android:text="Register"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView5"
        app:layout_constraintVertical_bias="0.603" />

    <Button
        android:id="@+id/forgot_password_btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="32dp"
        android:text="Forgot Password"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.288"
        app:layout_constraintStart_toEndOf="@+id/register_btn"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.816" />

    <TextView
        android:id="@+id/textView5"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="52dp"
        android:text="Password:"
        app:layout_constraintBottom_toTopOf="@+id/input_password"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.092"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/input_username"
        app:layout_constraintVertical_bias="1.0" />

    <TextView
        android:id="@+id/textView15"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Login Page"
        android:textSize="24sp"
        app:layout_constraintBottom_toTopOf="@+id/textView6"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.109"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.571" />

    <EditText
        android:id="@+id/input_password"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="32dp"
        android:ems="10"
        android:inputType="textPassword"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.367" />
</androidx.constraintlayout.widget.ConstraintLayout>

<!-- activity_register.xml -->

<?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:layout_width="match_parent"
    android:layout_height="match_parent">

    <EditText
        android:id="@+id/username"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="28dp"
        android:layout_marginBottom="32dp"
        android:ems="10"
        android:inputType="textPersonName"
        app:layout_constraintBottom_toTopOf="@+id/email"
        app:layout_constraintStart_toStartOf="parent" />

    <TextView
        android:id="@+id/password_label"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="32dp"
        android:layout_marginTop="8dp"
        android:text="Password:"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/email" />

    <EditText
        android:id="@+id/email"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="28dp"
        android:ems="10"
        android:inputType="textPersonName"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.341" />

    <EditText
        android:id="@+id/first_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="32dp"
        android:layout_marginTop="28dp"
        android:ems="10"
        android:inputType="textPersonName"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/password" />

    <TextView
        android:id="@+id/first_name_label"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="32dp"
        android:layout_marginBottom="4dp"
        android:text="First Name:"
        app:layout_constraintBottom_toTopOf="@+id/first_name"
        app:layout_constraintStart_toStartOf="parent" />

    <TextView
        android:id="@+id/username_label"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="28dp"
        android:layout_marginBottom="24dp"
        android:text="Username:"
        app:layout_constraintBottom_toTopOf="@+id/username"
        app:layout_constraintStart_toStartOf="parent" />

    <EditText
        android:id="@+id/last_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="28dp"
        android:layout_marginBottom="48dp"
        android:ems="10"
        android:inputType="textPersonName"
        app:layout_constraintBottom_toTopOf="@+id/register_btn"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/first_name"
        app:layout_constraintVertical_bias="1.0" />

    <Button
        android:id="@+id/register_btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="28dp"
        android:layout_marginTop="548dp"
        android:text="Register"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/email_label"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="28dp"
        android:layout_marginBottom="4dp"
        android:text="Email:"
        app:layout_constraintBottom_toTopOf="@+id/email"
        app:layout_constraintStart_toStartOf="parent" />

    <TextView
        android:id="@+id/confirm_password_label"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="140dp"
        android:text="Confirm Password:"
        app:layout_constraintBottom_toTopOf="@+id/confirm_password"
        app:layout_constraintStart_toEndOf="@+id/password_label" />

    <TextView
        android:id="@+id/last_name_label"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="32dp"
        android:layout_marginBottom="4dp"
        android:text="LastName"
        app:layout_constraintBottom_toTopOf="@+id/last_name"
        app:layout_constraintStart_toStartOf="parent" />

    <Button
        android:id="@+id/login_btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="20dp"
        android:layout_marginTop="548dp"
        android:text="Login"
        app:layout_constraintStart_toEndOf="@+id/register_btn"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/textView9"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="24dp"
        android:text="Registration Page"
        android:textSize="24sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.139"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <EditText
        android:id="@+id/password"
        android:layout_width="195dp"
        android:layout_height="40dp"
        android:layout_marginStart="28dp"
        android:layout_marginTop="4dp"
        android:ems="10"
        android:inputType="textPassword"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/password_label" />

    <EditText
        android:id="@+id/confirm_password"
        android:layout_width="174dp"
        android:layout_height="46dp"
        android:layout_marginEnd="4dp"
        android:ems="10"
        android:inputType="textPassword"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.452" />

</androidx.constraintlayout.widget.ConstraintLayout>
<!-- dialog_progress.xml -->

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

</androidx.constraintlayout.widget.ConstraintLayout>

Last but not least we will need to add some code to the Main Activity in order setup the main screen in the application. For starters, let's add the following code to the MainActivity class:

// MainActivity
public class MainActivity extends AppCompatActivity {

    private Button loginBtn;
    private Button signUpBtn;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        loginBtn = findViewById(R.id.login_btn);
        signUpBtn = findViewById(R.id.sign_up_btn);

        loginBtn.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this, LoginActivity.class);
                startActivity(intent);
            }
        });

        signUpBtn.setOnClickListener(new View.OnClickListener() {
           public void onClick(View v){
               Intent intent = new Intent(MainActivity.this, RegisterActivity.class);
               startActivity(intent);
           }
        });

        DatabaseHelper.createDatabase(getApplicationContext());

    }
}

Basically, we are setting up a simple activity containing the Login and Registration buttons. Now add the following code the activity_main.xml file in order to create our desired layout:



<!-- activity_main.xml -->
<?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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/login_btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Login"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.321"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.683" />

    <Button
        android:id="@+id/sign_up_btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="48dp"
        android:text="Sign Up"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.01"
        app:layout_constraintStart_toEndOf="@+id/login_btn"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.683" />

</androidx.constraintlayout.widget.ConstraintLayout>

If you're running into errors or need more clarification on the code, check out the GitHub repository for this application.

Finally we have reached the end of development!🐱‍🏍

Now all that's left is to run and test the application:

  • Successful Login attempt
  • Login Page
  • Failed Login attempt

If you made it this far, congrats!👏 You now know how to implement Login and Registration features for Android applications!🚀

Conclusion

Thanks again for reading this blog post series. If you need access to the source code for this application you can access it by visiting it's GitHub link.

Well that's it for this post! Thanks for following along in this article and if you have any questions or concerns please feel free to post a comment in this post and I will get back to you when I find the time.

If you found this article helpful please share it and make sure to follow me on Twitter and GitHub, connect with me on LinkedIn and subscribe to my YouTube channel.