1. Simple Authentication As a user, When I open the app, If there’s an existing account, Then log in with that account, Else create a new account, Then log in with that account

Display rooms screen

Display a message if a new account has been created

Display a message if user has logged in

Before we proceed to how we will implement authentication feature, we need to learn some basic jargons first in NoSQL land.

collection – A collection is what you call a table in a relational database

document – A document is what you call a row in a relational database

They are not exactly the same but the concept is somehow similar.

Table of Contents

1. Enabling Cloud Firestore in Project Console

1. In your project console, click Database

2. Look for Cloud Firestore Beta card and click Get Started

3. Select Start in test mode then click Enable

2. Creating our class for handling authentication

What we are going to do is a pseudo-authentication. The process is similar to how real authentication works but we won’t have any logic. Here’s a brief explanation on how the process will look like:

Note: Every time you insert a document in Firestore, it will have a key generated by Firestore. Just think of document as a row in traditional relational database.

When we open the app, we check if a key exist in our Shared Preferences. If no key is found, we create a new user in Firestore and store the key in our Shared Preferences. If a key exist in our SharedPreferences, we check first if that key exist in Firestore If the key exists and returns a user, use that user. If the key doesn’t exist, then do step 2 (This is just for some unknown/weird reason your user disappeared)

1. Create a new class called AuthenticationRepository

import android.support.annotation.NonNull; import com.google.android.gms.tasks.OnCompleteListener; import com.google.android.gms.tasks.OnFailureListener; import com.google.android.gms.tasks.OnSuccessListener; import com.google.android.gms.tasks.Task; import com.google.firebase.firestore.DocumentReference; import com.google.firebase.firestore.DocumentSnapshot; import com.google.firebase.firestore.FirebaseFirestore; import java.util.HashMap; import java.util.Map; public class AuthenticationRepository { private static final String TAG = "AuthenticationRepo"; private FirebaseFirestore db; public AuthenticationRepository(FirebaseFirestore db) { this.db = db; } public void login(String key, final OnSuccessListener<DocumentReference> successCallback, final OnFailureListener failureCallback) { DocumentReference userRef = db.collection("users").document(key); userRef.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() { @Override public void onComplete(@NonNull Task<DocumentSnapshot> task) { if (task.isSuccessful()) { DocumentSnapshot userSnapshot = task.getResult(); if (userSnapshot != null && userSnapshot.exists()) { successCallback.onSuccess(userSnapshot.getReference()); } else { createNewUser(successCallback, failureCallback); } } else { failureCallback.onFailure(task.getException()); } } }); } public void createNewUser(final OnSuccessListener<DocumentReference> successCallback, final OnFailureListener failureCallback) { Map<String, Object> user = new HashMap<>(); user.put("name", "Arth Limchiu"); db.collection("users") .add(user) .addOnSuccessListener(new OnSuccessListener<DocumentReference>() { @Override public void onSuccess(DocumentReference documentReference) { successCallback.onSuccess(documentReference); } }) .addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { failureCallback.onFailure(e); } }); } }

You can implement your own callback classes also but for this one we will just reuse OnSuccessListener<..> and OnFailureListener classes.

login(…) – takes care of fetching the user in Firestore. If a user is not found it will call createNewUser(…) passing in the callbacks.

– takes care of the user in Firestore. If a user is not found it will call passing in the callbacks. createNewUser(…) – takes care of creating a new user in Firestore and passes a DocumentReference(which is the user) in successCallback .

Firestore takes care of creating a collection if it doesn’t exist like our users collection.

3. Setting up our Shared Preferences

1. In your MainActivity.class declare a static variable

private static final String CURRENT_USER_KEY = "CURRENT_USER_KEY";

2. Create two new methods in your MainActivity.class

private String getCurrentUserKey() { SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); return preferences.getString(CURRENT_USER_KEY, ""); } private void saveCurrentUserKey(String key) { SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); SharedPreferences.Editor editor = preferences.edit(); editor.putString(CURRENT_USER_KEY, key); editor.apply(); }

4. Implement our authentication feature

1. In your MainActivity.class, declare an AuthenticationRepository class and initialize it in onCreate()

public class MainActivity extends AppCompatActivity { ... AuthenticationRepository authentication; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); authentication = new AuthenticationRepository(FirebaseFirestore.getInstance()); } }

2. In your MainActivity.class, create a new function authenticate() and call it after authentication is initialized.

public class MainActivity extends AppCompatActivity { ... AuthenticationRepository authentication; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); authentication = new AuthenticationRepository(FirebaseFirestore.getInstance()); authenticate(); } private void authenticate() { String currentUserKey = getCurrentUserKey(); if (currentUserKey.isEmpty()) { authentication.createNewUser( new OnSuccessListener<DocumentReference>() { @Override public void onSuccess(DocumentReference documentReference) { saveCurrentUserKey(documentReference.getId()); Toast.makeText(MainActivity.this, "New user created", Toast.LENGTH_SHORT).show(); } }, new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { Toast.makeText( MainActivity.this, "Error creating user. Check your internet connection", Toast.LENGTH_SHORT) .show(); } } ); } else { authentication.login( currentUserKey, new OnSuccessListener<DocumentReference>() { @Override public void onSuccess(DocumentReference documentReference) { Toast.makeText(MainActivity.this, "Logged in", Toast.LENGTH_SHORT).show(); } }, new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { Toast.makeText( MainActivity.this, "Error signing in. Check your internet connection", Toast.LENGTH_SHORT) .show(); } } ); } } ... }

5. Let’s test it out!

1. Run the app. You should see a Toast message or a different one if you implemented it differently.

2. Kill your app by swiping it off from your recent applications menu. Then open it again and you should see a different message.

6. Review

1. Simple Authentication As a user, When I open the app, If there’s an existing account, Then log in with that account, Else create a new account, Then log in with that account

Display rooms screen

Display a message if a new account has been created

Display a message if user has logged in

✓ Authentication logic

✓ Display rooms screen (our MainActivity IS our rooms screen. Just imagine it for now 😂)

✓ Display a message if a new account has been created

✓ Display a message if user has logged in

Remember that you can always implement this differently like show the name in a TextView or set the title of your MainActivity to the user’s name. As long as you achieved the goal.