TL;DR: In this part of the tutorial, you will learn how to develop a simple to-do list Android app from scratch. Here, you will start by scaffolding your app with the help of Android Studio. After that, you will build a list to show to-do items and you will create a form to enable users to update this list. In the end, you will also learn how to secure your to-do app with Auth0. If you need to, you can check the full code developed throughout this tutorial in this GitHub repository.

"Check out this tutorial to learn how to build and secure Android apps from scratch!"


Prerequisites

To follow along with this tutorial, there are two things you will need and a third one that you can benefit from. First, you will need Java Development Kit 7 (JDK 7) or higher on your computer. If you don't have it, follow the instructions here to download a JDK.

Second, you will need Android Studio, the official Integrated Development Environment (IDE) for Android. If you don't have it yet, you can download Android Studio from this resource.

Lastly, if you have prior knowledge around Java development, you will benefit a lot from it. However, this is not a hard requirement. If you are new to Java (but have experience with some other programming languages), you might still find it easy to follow the instructions here.

Scaffolding an Android App

Scaffolding a new Android application is quite easy. For starters, open the Android Studio and, on the splash screen, click on Start a new Android Studio project. After clicking on it, the IDE will ask you to choose your project. Make sure you are on the Phone and Tablet tab, click on the Empty Activity option, then click on Next.

Using Android Studio to scaffold a new Android project.

After clicking on this button, the IDE will show you a form that you can fill in as follows:

  • Name: "To-Do List"
  • Package name: com.auth0.todo
  • Save location: Use the default value or adjust to your needs.
  • Language: "Java"
  • Minimum API level: "API 21: Android 5.0 (Lollipop)"

From the properties above, there are two items that you can change: name and save location. Apart from that, you can also change the package name, but be aware that this tutorial uses this value multiple times (so, you will have to replace them a lot too). The other two properties, language and minimum API level, must remain untouched. So, leave them like that and click on the Finish button.

Configure the final settings of your new Android project.

After clicking on the finish button, Android Studio will scaffold your new project for you, and it will open its source code on a new window. Now, the next thing you can do is to run your new (and empty) app to confirm that everything is in place. For that, you have two options: you can run the app on a real device (like your Android mobile phone), or you can use an emulator. The latter is the easiest approach but, check this resource if you want to learn how to run it on a real device.

If you opt to use an emulator, you can proceed as follows:

  • In the IDE, click on the green Run button in the toolbar.
  • In the Select Deployment Target window, click Create New Virtual Device.
  • In the Select Hardware screen, select a phone device, such as Nexus 5, and then click Next.
  • In the System Image screen, select the version with the highest API level. If you don't have that version installed, the IDE will show a Download link. If that is the case, click the link and complete the download.
  • Click Next.
  • On the Android Virtual Device (AVD) screen, leave all the settings alone and click Finish.
  • Back in the Select Deployment Target dialog, select the device you just created and click OK.

Clicking on the OK button will make Android Studio start the emulator (it can take a couple of minutes as it is the first time you are using it) and open the app on it. You should now see "Hello World!" displayed in the app running on the emulator.

Running the Hello World Android app.

Adding Android Material to Your Project

By default, the Android framework provides a bunch of UI components that you can use to build your app. These components are quite flexible and there is nothing wrong on using them. However, as their default appearance is a bit ugly, you will be better off using some more advanced component framework.

In this tutorial, to avoid having to invest too much time on the UI and to avoid ending up with an ugly app, you will use one of the most popular component libraries available: Android Material. This library provides components that adhere to the Material Design visual language that Google created. This visual language allows developers to:

  • rely on classic principles of good design;
  • and to unify the user experience across platforms, devices, and input methods.

To install this library, open the ./app/build.gradle in your project, and update the dependencies section as follows:

// ./app/build.gradle

dependencies {
    // ... leave the rest untouche and add ...
    implementation 'com.google.android.material:material:1.0.0'
}

After adding this dependency to your project, you will need to click the Sync Now link that the IDE shows to download the Material library (if you don't, you won't be able to use the Material components in your project). Then, click on the Refactor menu in your IDE (at the horizontal menu at the top) and click on the Migrate to AndroidX option. You will need this to update your app's dependencies and code to use the new androidx package and the com.google.android.material library.

Lastly, you can open the AndroidManifest.xml file and replace the value that you have on the android:theme property of the application element:

<!-- ./app/src/main/AndroidManifest.xml -->

<manifest ...>

    <application
        ...
        android:theme="@style/Theme.MaterialComponents.Bridge">
        <!-- Main Activity -->
    </application>

</manifest>

The new value you are using is changing the theme (the look and feel) of your app to a darker one.

That's it for the moment. In the next section, you will use your first Android Material component.

Showing a Static List of To-Do Items

After testing your hello-world app and adding Android Material to your project, it is time to start developing your new app. To begin with, you will remodel the main activity of your Android app to show a list of static to-do items.

An activity is a single, focused thing that the user can do. Almost all activities interact with the user, so the Activity class takes care of creating a window for you in which you can place your UI (User Interface). - Android Docs

In Android, to define how an activity will look like, you will usually use layout resources. These resources are XML files that reside in the ./app/src/main/res/layout/ directory of your application. For example, if you open this directory, you will see a file called activity_main.xml there. Then, if you open the MainActivity class source code (./app/src/main/java/com/auth0/todo/MainActivity.java), you will see that this class calls the setContentView method passing R.layout.activity_main as the parameter.

With that information, the Android framework will know that you want to use the activity_main layout resource (R stands for resource, hence R.layout.activity_main) to set the content view (setContentView) of the MainActivity class. Knowing that, the framework will interpret this file to compose your UI.

Now that you know more about how this process works, open the activity_main.xml file, and replace its code with this:

<!-- ./app/src/main/res/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">

    <ListView
        android:id="@+id/to_do_items"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:divider="@null"
        android:paddingTop="8dp"
        android:paddingBottom="8dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

The only change on this file is that you are adding a component called ListView that has @id/to_do_items as its identifier (note that the + sign after @ is telling Android that you are defining this id here, not using it). Don't worry about the other properties now. After creating your first Android application, you can learn more about them.

With that in place, you will create a new layout resource that your app will use to show each to-do item in particular. So, create a new file called to_do_item.xml inside ./app/src/main/res/layout/ and add the following code to it:

<!-- ./app/src/main/res/layout/to_do_item.xml -->

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/transparent"
    android:clipToPadding="false"
    android:orientation="vertical"
    android:paddingLeft="16dp"
    android:paddingTop="8dp"
    android:paddingRight="16dp"
    android:paddingBottom="8dp">

    <com.google.android.material.card.MaterialCardView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <TextView
            android:id="@+id/to_do_message"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:padding="16dp"
            android:textSize="24sp" />
    </com.google.android.material.card.MaterialCardView>
</LinearLayout>

Although verbose, this XML file is quite simple to understand. Here, you are using a LinearLayout component to serve as the parent of another component called MaterialCardView. Then, inside this card view, you are defining a TextView that will receive the description of the to-do item it represents.

Note: The primary reason to nest cards inside LinearLayout is to define some space around (android:padding...) each one of them.

Now, to make your app use this new layout resource, you will need to create an adapter. Adapters, as explained by the official documentation, exist to act as a bridge between view and the underlying data. In this case, you will use an adapter to make your app use the new resource to render to-do items on the ListView you added to the activity_main.xml file.

As you are dealing with to-do items, you will name this adapter ToDoListAdapter. To add this class to the project, create a new package called util inside com.auth0.todo. Then, create the ToDoListAdapter class inside the new package and add the following code to it:

// ./app/src/main/java/com/auth0/todo/util/ToDoListAdapter.java

package com.auth0.todo.util;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

import com.auth0.todo.R;

import java.util.ArrayList;
import java.util.List;

public class ToDoListAdapter extends BaseAdapter {
    private LayoutInflater inflater;
    private List<String> toDoList = new ArrayList<>();

    public ToDoListAdapter(Context context) {
        toDoList.add("My first task");
        toDoList.add("My second task");
        inflater = LayoutInflater.from(context);
    }

    @Override
    public View getView(int position, View view, ViewGroup parent) {
        String message = (String) getItem(position);
        if (view == null) {
            view = inflater.inflate(R.layout.to_do_item, null);
        }

        TextView textView = view.findViewById(R.id.to_do_message);
        textView.setText(message);

        return view;
    }

    @Override
    public Object getItem(int position) {
        return toDoList.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public int getCount() {
        return toDoList.size();
    }
}

As you can see, this class extends BaseAdapter and overrides four of its methods (getView, getItem, getItemId, and getCount) to render the to-do list. To do so, the getView method receives a position parameter and uses it to get the item (getItem) of the to-do list it must render. Then, getView extracts the message of this to-do item and uses it to populate the TextView element (the one you put inside MaterialCardView).

Note: Your app will call getView for each one of the to-do items the adapter holds. The app will know how many items the adapter has through the getCount method and, as explained, will use getItem to get each one in particular.

For now, your adapter is using a static to-do list that contains two items. You are defining this static list on the constructor of the ToDoListAdapter class.

After defining the adapter, you will have to open the MainActivity class and update it as follows:

// ./app/src/main/java/com/auth0/todo/MainActivity.java

// ... other import statements ...
import android.widget.ListView;
import com.auth0.todo.util.ToDoListAdapter;

public class MainActivity extends AppCompatActivity {
    private ToDoListAdapter toDoListAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // ... super.onCreate and setContentView ...

        // create and configure the adapter
        this.toDoListAdapter = new ToDoListAdapter(this);
        ListView toDoItemsListView = findViewById(R.id.to_do_items);
        toDoItemsListView.setAdapter(toDoListAdapter);
    }
}

The new version of this class is defining a field that references the ToDoListAdapter class, and it is using the onCreate lifecycle method to create and configure this adapter. As you can see, you are making the ListView element (the one you added to activity_main.xml) use your new adapter (setAdapter(toDoListAdapter)).

After these changes, if you hit the play button again, you will see the new version of your app with a static to-do list (and with a dark theme).

Using Android Material to create a static to-do list Android app.

Allowing Users to Insert To-Do Items

Now that you have built an app capable of showing a list of to-do items, the next logical thing to do is to enhance it to enable users to insert more items. To accomplish this, you will create a new activity that will implement this feature. So, for starters, create a new file called activity_to_do_form.xml inside ./app/src/main/res/layout/ (this will be the layout resource of the activity) and add the following code to it:

<!-- ./app/src/main/res/layout/activity_to_do_form.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=".ToDoFormActivity">

    <EditText
        android:id="@+id/editText"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginTop="16dp"
        android:ems="10"
        android:hint="@string/to_do_message"
        android:inputType="textPersonName"
        app:layout_constraintEnd_toStartOf="@+id/button"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginEnd="16dp"
        android:onClick="addToDoItem"
        android:text="@string/button_insert"
        app:layout_constraintBaseline_toBaselineOf="@+id/editText"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toEndOf="@+id/editText" />
</androidx.constraintlayout.widget.ConstraintLayout>

This layout resource is using a ConstraintLayout to put two elements side by side:

  • an EditText where users will input the description of the new to-do item;
  • a Button that, when clicked, will add the to-do item in the list.

Note that the layout above is using two string resources: @string/to_do_message and @string/button_insert. On Android development, it is a best practice to use String resources to declare static text. So, open the strings.xml file and replace its contents with this:

<!-- ./app/src/main/res/values/strings.xml -->

<resources>
    <string name="app_name">To-Do List</string>
    <string name="to_do_message">Describe your to-do item</string>
    <string name="button_insert">Insert</string>
</resources>

This will make your input text show (while empty) a message saying "Describe your to-do item", and will add "Insert" as the label of the button.

Next, you will create the activity's class that will render and handle this layout. In this case, you will create a file called ToDoFormActivity.java inside the com.auth0.todo package and you will add the following code to it:

// ./app/src/main/java/com/auth0/todo/ToDoFormActivity.java

package com.auth0.todo;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;

import androidx.appcompat.app.AppCompatActivity;

public class ToDoFormActivity extends AppCompatActivity {

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

    public void addToDoItem(View view) {
        EditText editText = findViewById(R.id.editText);
        String message = editText.getText().toString();

        Intent returnIntent = new Intent();
        returnIntent.putExtra("item", message);
        setResult(Activity.RESULT_OK, returnIntent);
        finish();
    }
}

Here, on the onCreate method, you are defining that this activity will use the activity_to_do_form layout resource. As this resource has a button that depends on a method called addToDoItem, you are defining it here as well. This method, when triggered, reads the message inserted by the user and send it back to the main activity.

Now you are probably wondering: "What does 'send it back to the main activity' really mean?" As you will soon see, the main activity will start this activity through a method called startActivityForResult. By using this method, the activity is telling Android that it wants to create a temporary ToDoFormActivity to get a result back from it (in this case, the message that the user writes on the text field).

To wrap the creation of the new activity, you will need to register it in your app. So, open the AndroidManifest.xml file and update it as follows:

<!-- ./app/src/main/AndroidManifest.xml -->

<manifest ...>

    <application ...>
        <activity android:name=".MainActivity">
        ...
        </activity>

        <!-- NEW: register ToDoFormActivity -->
        <activity android:name=".ToDoFormActivity"></activity>
    </application>

</manifest>

Having done that, open the activity_main.xml file and update it as follows:

<!-- ./app/src/main/res/layout/activity_main.xml -->

<androidx.constraintlayout.widget.ConstraintLayout ...>

    <!-- ... ListView ... -->

    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/floatingActionButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="end|bottom"
        android:layout_marginEnd="16dp"
        android:layout_marginBottom="16dp"
        android:onClick="openToDoForm"
        android:src="@drawable/ic_add_white_24dp"
        app:layout_constraintBottom_toBottomOf="@id/to_do_items"
        app:layout_constraintEnd_toEndOf="@id/to_do_items" />
</androidx.constraintlayout.widget.ConstraintLayout>

This will add a Floating Action Button (a.k.a, FAB) to your main activity. If you take a close look at the XML you just added, you will see that, among other things, it is defining that this button will call openToDoForm when clicked. Also, the XML is telling Android that it wants to use an icon (android:src) that has the following id: @drawable/ic_add_white_24dp.

So, first, you will create the icon that will appear on the button. To do this, create a file called ic_add_white_24dp.xml inside ./app/src/main/res/drawable/ and insert the following code to it:

<!-- ./app/src/main/res/drawable/ic_add_white_24dp.xml -->

<vector android:height="24dp" android:tint="#FFFFFF"
    android:viewportHeight="24.0" android:viewportWidth="24.0"
    android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
    <path android:fillColor="#FF000000" android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
</vector>

This file defines a vector that will draw a plus (+) sign in your button.

Now, you can open the MainActivity class and update it as follows:

// ./app/src/main/java/com/auth0/todo/MainActivity.java

package com.auth0.todo;

// ... other import statements ...
import android.content.Intent;
import android.view.View;

public class MainActivity extends AppCompatActivity {
    // ... ToDoListAdapter and onCreate definition ...

    public void openToDoForm(View view) {
        startActivityForResult(new Intent(this, ToDoFormActivity.class), 1);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == 1) {
            if (resultCode == RESULT_OK) {
                String newItem = data.getStringExtra("item");
                this.toDoListAdapter.addItem(newItem);
            }
        }
    }
}

Here, you are adding two new methods:

  • openToDoForm: This method, as mentioned before, will use the startActivityForResult method to start the ToDoFormActivity (and to get a result back from it).
  • onActivityResult: This method will get the value inserted on the ToDoFormActivity and add to the toDoListAdapter instance.

Note: To use startActivityForResult, you have to inform an integer that represents the "operation" you want the new activity to perform. In the code above, you are using 1 to represent this operation. That's why you check if requestCode == 1 inside the onActivityResult method. Check this resource to learn more about startActivityForResult.

If you recall, your ToDoListAdapter class you created earlier doesn't have a method called addItem. So, open this class and add the method:

// ./app/src/main/java/com/auth0/todo/util/ToDoListAdapter.java

// ... package definition and import statements ...

public class ToDoListAdapter extends BaseAdapter {
    // ... other fields and methods ...

    public void addItem(String newItem) {
        toDoList.add(newItem);
        notifyDataSetChanged();
    }
}

This change will make your adapter add the new item to the toDoList instance and will make the adapter notify (through the notifyDataSetChanged) the UI about the change. This notification will allow Android to adjust the view accordingly (i.e., create the card for the new item).

With that in place, you have finished creating and integrating the form that will allow users to add new to-do items. So, click on the green play button to reload your app, and test it to see if everything is working as expected.

Creating a new Android activity to allow users to add to-do items to the list.

Note: If you change the orientation of your device (from vertical to horizontal, for example), you might end up losing the new items added to the to-do list. This problem happens because Android reconstructs the view on such scenarios, calling the onCreate lifecycle method again (which ends up creating a new ToDoListAdapter instance). On the second part of this tutorial, you will fix this situation by adding a backend API to support your app.

Securing Android Apps with Auth0

Cool, you now have a simple to-do application that persists items in memory. In the second part of this tutorial, you will learn how to integrate this Android app with a backend API. The idea is to be able to provide a persistent database to your app, so your users can rest assured they will have access to their items even if they lose their phones. However, the best thing you can do now is to learn how to secure your app.

By using Auth0, as you will see, providing means so users can authenticate themselves is quite easy. You will have to start by creating an Auth0 account (or logging into an existing one), then you will have to configure some properties in your Auth0 dashboard, then you will need to make some changes in your code. After that, you will have a consistent, full-featured identity management system that will allow users to sign up, sign in, reset passwords, activate multifactor authentication, and more.

If this sounds interesting, keep reading!

Creating and configuring an Auth0 account

For starters, if you don't have one yet, you can sign up for a free Auth0 account now (if you already have one, feel free to reuse it). After that, go to the Applications section of your Auth0 dashboard and click on Create Application.

When you click on this button, Auth0 will show you a dialog where it will ask you for two things:

  • Name: Here, you can type an identifier for your application (e.g., "Android To-Do App").
  • Application Type: Here, you will have to choose "Native".

After filling in this form, click on the Create button. Doing so will make Auth0 redirect you to the Quick Start section of your new application. From there, head to the Settings section, and search for the Allowed Callback URLs field. In this field, insert the following value:

  • to-do://<YOUR-AUTH0-DOMAIN>/android/com.auth0.todo/callback

Note: You will have to replace <YOUR-AUTH0-DOMAIN> with the value available on the Domain property of your new Auth0 Application (you can find it at the top of the page). Also, if you have chosen to use a package name different than com.auth0.todo, you will have to change that too.

The authentication process at Auth0 happens on a hosted login page. That is, when your app starts the authentication process, it will open the login page in a browser and, after the authentication takes place, will redirect your user back to your app. This redirection works because your app will register a deep link (the one you are adding to the Allowed Callback URLs field) in the Android device. Then, as you will configure Auth0 to call this deep link, the device will know that it has to open your app again (passing any information that the deep link provides).

Therefore, after updating the Allowed Callback URLs field, click on the Save Changes button at the bottom of the page and leave the page open. You will need to copy some values from it in a bit.

Adding and configuring the Auth0 dependency

Having created your Auth0 Application, the next thing you will do is to add and configure the library that will let you secure your Android app with Auth0. To install it, open the build.gradle file and update it as follows:

// ./app/build.gradle

// ... apply plugin ...

android {

    // ... sdk version ...

    defaultConfig {

        // ... other properties ...

        manifestPlaceholders = [auth0Domain: "@string/com_auth0_domain", auth0Scheme: "to-do"]
    }
    // ... buildTypes ...
}

dependencies {
    // ... other dependencies ...
    implementation 'com.auth0.android:auth0:1.15.1'
}

Then, click on the Sync Now link that the IDE shows to download this dependency.

Note: The manifestPlaceholders you added above helps the Auth0 dependency register an intent-filter to capture the result of the authentication process. In other words, when Auth0 redirect your users back to your app (through the deep link you configured previously), the library will intercept this call and do its job.

Now, to configure this dependency with your Auth0 Application properties, you will open and update the strings.xml file. While you are manipulating this file, you can also add two labels that you will use soon:

<!-- ./app/src/main/res/values/strings.xml -->

<resources>
    <!-- ... other resources ... -->
    <string name="login">Log In</string>
    <string name="logout">Log Out</string>
    <string name="com_auth0_client_id"><YOUR_AUTH0_APP_ID></string>
    <string name="com_auth0_domain"><YOUR_AUTH0_DOMAIN></string>
</resources>

As you can see, this change is defining the login and logout labels and two Auth0 properties: com_auth0_client_id and com_auth0_domain. You will need to replace the values you are passing to these properties with the ones that the Auth0 Application exposes. More specifically, you will need to copy Client ID and Domain from your Auth0 Application to replace <YOUR_AUTH0_APP_ID> and <YOUR_AUTH0_DOMAIN> respectively.

Adding a menu to your app

Now that you configured the Auth0 library, the next thing you will do is to add a menu for your app. This will be a dynamic menu that will allow users to either log in or log out (depending, of course, on their current status).

To start defining this menu, create a new directory called menu inside ./app/src/main/res/. Then, create a file called menu.xml inside menu and add the following code to it:

<!-- ./app/src/main/res/menu/menu.xml -->

<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/first_action"
        android:title="" />
</menu>

This menu is quite simple. It contains a single item just with an id and an empty title. You will define the title of the menu, along with its behavior, programmatically.

Now, as the menu will only cover two scenarios (login and logout), you will define two menu listeners: LoginListener and LogoutListener. When your users click on the menu, your app will call one of these listeners to either start the authentication process or end the user session.

To define the first listener, create a class called LoginListener inside the com.auth0.todo.util package and add the following code to it:

// ./app/src/main/java/com/auth0/todo/util/LoginListener.java

package com.auth0.todo.util;

import android.view.MenuItem;

import com.auth0.todo.identity.AuthenticationHandler;

public class LoginListener implements MenuItem.OnMenuItemClickListener {
    private AuthenticationHandler authenticationHandler;

    public LoginListener(AuthenticationHandler authenticationHandler) {
        this.authenticationHandler = authenticationHandler;
    }

    @Override
    public boolean onMenuItemClick(MenuItem item) {
        authenticationHandler.startAuthenticationProcess();
        return true;
    }
}

As you can see, this class implements an interface called MenuItem.OnMenuItemClickListener. It also defines a constructor that receives an instance of a class called AuthenticationHandler. You will create this class soon. Lastly, it defines that the onMenuItemClick will trigger a method called startAuthenticationProcess. As its name suggests, this method will start the authentication process.

After defining the login listener, you will create the LogoutListener inside the same package and add the following code to it:

// ./app/src/main/java/com/auth0/todo/util/LogoutListener.java

package com.auth0.todo.util;

import android.view.MenuItem;

import com.auth0.todo.identity.AuthenticationHandler;

public class LogoutListener implements MenuItem.OnMenuItemClickListener {
    private AuthenticationHandler authenticationHandler;

    public LogoutListener(AuthenticationHandler authenticationHandler) {
        this.authenticationHandler = authenticationHandler;
    }

    @Override
    public boolean onMenuItemClick(MenuItem item) {
        authenticationHandler.logout();
        return true;
    }
}

The behavior of this class is quite similar to the other one. The difference is that, instead of starting the authentication process, this class will log the user out.

Handling the authentication process

After defining some important pieces of your menu, the next thing you will do is to create the two central pieces of the identity management solution of your app. For starters, create a package called identity inside com.auth0.todo. Then, inside this package, create a class called AuthenticationHandler with the following code:

// ./app/src/main/java/com/auth0/todo/identity/AuthenticationHandler.java

package com.auth0.todo.identity;

import android.app.Dialog;
import android.content.Intent;
import android.widget.Toast;

import com.auth0.android.Auth0;
import com.auth0.android.authentication.AuthenticationAPIClient;
import com.auth0.android.authentication.AuthenticationException;
import com.auth0.android.authentication.storage.CredentialsManagerException;
import com.auth0.android.authentication.storage.SecureCredentialsManager;
import com.auth0.android.authentication.storage.SharedPreferencesStorage;
import com.auth0.android.callback.BaseCallback;
import com.auth0.android.provider.AuthCallback;
import com.auth0.android.provider.WebAuthProvider;
import com.auth0.android.result.Credentials;
import com.auth0.todo.MainActivity;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;

public class AuthenticationHandler implements AuthCallback, BaseCallback<Credentials, CredentialsManagerException> {
    private Auth0 auth0;
    private AuthAwareActivity originalActivity;
    private Class<? extends AuthAwareActivity> nextActivity;
    private SecureCredentialsManager credentialsManager;
    private Credentials credentials;

    AuthenticationHandler(AuthAwareActivity originalActivity) {
        this.originalActivity = originalActivity;

        // configuring Auth0
        auth0 = new Auth0(originalActivity);
        auth0.setOIDCConformant(true);
        AuthenticationAPIClient client = new AuthenticationAPIClient(auth0);
        credentialsManager = new SecureCredentialsManager(originalActivity, client, new SharedPreferencesStorage(originalActivity));
    }

    public void startAuthenticationProcess() {
        WebAuthProvider.init(auth0)
                .withScheme("to-do")
                .withScope("openid profile email offline_access")
                .withAudience("https://to-dos-api")
                .start(originalActivity, this);
    }

    public void logout() {
        credentialsManager.clearCredentials();
        originalActivity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                String message = "See you soon!";
                Toast.makeText(originalActivity, message, Toast.LENGTH_SHORT).show();
                originalActivity.refreshMenu();

                if (originalActivity instanceof MainActivity) return;
                originalActivity.startActivity(new Intent(originalActivity, MainActivity.class));
            }
        });
    }

    void refreshCredentials(Class<? extends AuthAwareActivity> nextActivity) {
        this.nextActivity = nextActivity;
        credentialsManager.getCredentials(this);
    }

    public String getAccessToken() {
        return credentials.getAccessToken();
    }

    public boolean hasValidCredentials() {
        boolean hasValidCredentials = this.credentialsManager.hasValidCredentials();
        if (hasValidCredentials && credentials == null) {
            refreshCredentials(null);
        }
        return hasValidCredentials;
    }

    @Override
    public void onFailure(@NonNull final Dialog dialog) {
        originalActivity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                dialog.show();
            }
        });
    }

    @Override
    public void onFailure(AuthenticationException exception) {
        new AlertDialog.Builder(originalActivity)
                .setTitle("Authentication Error")
                .setMessage(exception.getMessage())
                .show();
    }

    @Override
    public void onSuccess(@NonNull Credentials credentials) {
        credentialsManager.saveCredentials(credentials);
        this.credentials = credentials;
        if (nextActivity == null) {
            originalActivity.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    originalActivity.refreshMenu();
                }
            });
            return;
        }
        originalActivity.startActivity(new Intent(originalActivity, nextActivity));
    }

    @Override
    public void onFailure(CredentialsManagerException error) {
        startAuthenticationProcess();
    }
}

Don't get scared by the verbosity of this class, nor by the errors that the IDE shows regarding not knowing what AuthAwareActivity stands for (you will create this class soon). The AuthenticationHandler class is a bit big, but it is easy to demystify. Here you can check a list with the most important details that you must be aware of:

  • This class implements the AuthCallback interface to be able to decide what to do in case of failure or success. If you take a close look, you will see that the interface defines (and this class implements) three methods: two onFailure and one onSuccess. You will read about them in a bit.

  • This class implements the BaseCallback interface. This is needed because you want this class to call the SecureCredentialsManager that Auth0 provides to get the credentials (if any) of the current user. This process is done inside the refreshCredentials method that the AuthAwareActivity implements.

  • This class defines a method called startAuthenticationProcess that initiates the authentication process. That is, this method opens the Auth0 hosted login page in a browser so users can choose how they want to sign in or sign up.

  • This class defines a method called logout that, when requested, clears user credentials and that makes sure they are sent back to the main activity (it wouldn't make sense letting unauthenticated users add new to-do items, right?).

  • This class defines a method called getAccessToken that returns an access token that the app can use to communicate with a backend API. You will use this method (and learn more about access tokens) on the second part of the tutorial while integrating the app with the backend.

  • This class defines a method called hasValidCredentials that verifies if the user has valid credentials or not. What is interesting here is that, if the user doesn't have valid credentials, this method calls the refreshCredentials to get new ones.

As you can see, this class will do the heavy lifting for you when it comes to authentication and identity management.

Now, you will create another important class: AuthAwareActivity. Create this class inside the com.auth0.todo.identity package and add the following code to it:

// ./app/src/main/java/com/auth0/todo/identity/AuthAwareActivity.java

package com.auth0.todo.identity;

import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;

import com.auth0.todo.R;
import com.auth0.todo.util.LoginListener;
import com.auth0.todo.util.LogoutListener;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

public abstract class AuthAwareActivity extends AppCompatActivity {
    protected AuthenticationHandler authenticationHandler;
    protected Menu menu;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        this.authenticationHandler = new AuthenticationHandler(this);

    }

    public void refreshMenu() {
        MenuItem firstOption = menu.findItem(R.id.first_action);

        // reconfiguring button
        if (!authenticationHandler.hasValidCredentials()) {
            firstOption.setTitle(R.string.login);
            firstOption.setOnMenuItemClickListener(new LoginListener(authenticationHandler));
        } else {
            firstOption.setTitle(R.string.logout);
            firstOption.setOnMenuItemClickListener(new LogoutListener(authenticationHandler));
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.menu, menu);

        this.menu = menu;
        refreshMenu();

        return true;
    }
}

The main purpose of creating this class is to define a common behavior for the activities of your Android app. In this case, you are defining that you want all the subclasses of this class (you will refactor your two activities soon to extends this one) to create an instance of AuthenticationHandler. Also, you are defining that these activities will have a menu that shows either a login or a logout button depending on the result of a call to hasValidCredentials.

Wrapping up the integration with Auth0

To wrap things up, the last thing you will have to do is to refactor your two activities to extend the AuthAwareActivity class. First, you can open the ToDoFormActivity class and update it as follows:

// ./app/src/main/java/com/auth0/todo/ToDoFormActivity.java

// ... package definition and other import statements ...

import com.auth0.todo.identity.AuthAwareActivity;

public class ToDoFormActivity extends AuthAwareActivity {
    // ... leave the body of the class untouched ...
}

Then, open the MainActivity class and change it as follows:

// ./app/src/main/java/com/auth0/todo/MainActivity.java

// ... package definition and other import statements ...

import com.auth0.todo.identity.AuthAwareActivity;

public class MainActivity extends AuthAwareActivity {

    // ... leave the rest untouched ...

    public void openToDoForm(View view) {
        if (authenticationHandler.hasValidCredentials()) {
            startActivityForResult(new Intent(this, ToDoFormActivity.class), 1);
        }
    }

    // ... leave the rest untouched ...

}

Here, besides changing the base class of the MainActivity one, you are also changing the behavior of the floating action button to block unauthenticated users from accessing the new item form.

Note: As you are replacing AppCompatActivity with AuthAwareActivity on the two classes above, you can also remove the import statement that references the former.

After making these final changes, you are ready to take your secured Android application for a spin. So, click on the green play button to load the new version of your app, then click on the login menu to start the authentication process. If you sign in successfully, you will be able to use the new item form again.

Securing Android app with Auth0.

"Securing Android apps from scratch is easy."

Recap and Next Steps

In the first part of this tutorial, you started by learning how to scaffold a simple hello-world Android application. After that, you enhanced this app to show a to-do list, and you added a form to enable users to update this list. In the end, you learned how to secure your application with Auth0.

This setup gives you a solid basis for the next part of this tutorial. There, you will learn how to integrate your app with a backend API to make things even more interesting. By doing that, your app will become more resilient (as the user data will stay live on the backend API), and you will learn cool things like issuing HTTP requests from your Android app.

Sounds interesting? If so, see you there!