Android FirebaseUI with Custom FirebaseRecyclerAdapter Tutorial

In my last android tutorial, I discussed about using FirebaseUI for user authentication. One of my blog readers request that I should write a new android tutorial on Android Firebase with Custom FirebaseRecycerAdapter.

If you have not heard or use Android FirebaseUI, it is an integrated Android Firebase UI library that has access to Firebase database.

This tutorial will work you through how to create a simple Food recipe application in Android by using android FirebaseUI and RecyclerView.

We are going to make use of FirebaseUI adapter FirebaseRecyclerAdapter which extends from the default Android RecyclerViewAdapter.

You must override the populateViewHolder() method of the adapter class.

 

What we will be working on in this application

1. Intro Splash Screen Page

2. Navigation Drawer Page that will contain three Fragments (Recipe Categories, Favorite and Shopping list)

 

Android application screen-shot

android recipe

 

Setup Firebase Project

Follow the steps below to register and integrate Firebase SDK to your android project.

Go the Firebase webite and register if you don’t have an account already. The registration is very simple. After registration, go to the console dashboard and click on create new project button.

In the window modal, enter the name of your project and country as shown in the image below

Android firebase

Click on the create project button to continue. In the console, click the button add Firebase to your android app as shown below

Android firebase

In the next window, add the Package name of your application and click the Add App button as shown below.

Android firebase

Click the continue button to download a Json configuration file which will be added in the root App directory of your project. See the image below

Android firebase

Following the instructions on how to add the Google Services plugin and the Firebase SDK in build.gradle as shown below.

Android firebase

We have successfully added Firebase to our project. We will now focus on our android project.

 

Firebase Console

Sign into your Firebase console and add data in the following format. The images for the recipe categories is stored in Firebase Storage and the url is stored in the database as shown in the images below.

 

Firebase Database Console

firebase console

 

Firebase Storage Console

firebase storage console

 

CREATE ANDROID CLIENT APPLICATION

Lets start to soil our hands in code. Start up your IDE. For this tutorial, I am using the following tools and environment, feel free to use what works for you.

Windows 10

Android Studio

Sony Xperia ZL

Min SDK 14

Target SDK 23

To create a new android application project, follow the steps as stipulated below.

Go to File menu

Click on New menu

Click on Android Application

Enter Project name: AndroidFirebaseUIRecyclerView

Package: com.inducesmile.androidfirebaseuirecyclerview

Select Blank Activity

Name your activity : MainActivity

Keep other default selections

Continue to click on next button until Finish button is active, then click on Finish Button.

 

BUILD.GRADLE

We are going to complete Firebase integration in our android project by modifying the project build.gradle file.

Open the Project build.gradle file and add a classpath for google services as shown below.

classpath 'com.android.tools.build:gradle:2.1.2'
buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.1.2'
        classpath 'com.google.gms:google-services:3.0.0'
    }
}
allprojects {
    repositories {
        jcenter()
    }
}
task clean(type: Delete) {
    delete rootProject.buildDir
}

Open the app build.gradle, we are going to use some third party android library for network calls, dimensions, Firebase and much more. The updated version of the app level build.gradle is shown below

apply plugin: 'com.android.application'
android {
    compileSdkVersion 24
    buildToolsVersion "23.0.3"
    defaultConfig {
        applicationId "com.inducesmile.androidfirebaseuirecyclerview"
        minSdkVersion 16
        targetSdkVersion 24
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}
dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:24.0.0'
    compile 'com.android.support:design:24.0.0'
    compile 'com.intuit.sdp:sdp-android:1.0.3'
    compile 'de.hdodenhof:circleimageview:1.2.1'
    compile 'com.android.support:recyclerview-v7:24.0.0'
    compile 'com.android.support:support-v4:24.0.0'
    compile 'com.google.firebase:firebase-storage:9.2.0'
    compile 'com.google.firebase:firebase-database:9.2.0'
    compile 'com.firebaseui:firebase-ui:0.4.3'
    compile 'com.github.bumptech.glide:glide:3.7.0'
}
apply plugin: 'com.google.gms.google-services'

As you can see from the dependencies section of the gradle.build file that we will make use of many libraries.

 

MANIFEST.XML

There is no major modification in Manifest.xml. The code code is shown below.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.inducesmile.androidfirebaseuirecyclerview">
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name=".MainRecipeActivity"
            android:label="@string/title_activity_main_recipe"
            android:theme="@style/AppTheme.NoActionBar"> </activity>
    </application>
</manifest>

 

STRINGS.XML

We are going to update our project strings.xml file located in the values folder inside the res folder. Open the file and add the code below to it.

<resources>
    <string name="app_name">Android Recipes</string>
    <string name="title_activity_main_recipe">Recipes</string>
    <string name="inducesmile"> Inducesmile Recipes</string>
    <string name="navigation_drawer_open">Open navigation drawer</string>
    <string name="navigation_drawer_close">Close navigation drawer</string>
    <string name="recipe_categories">Recipe Categories</string>
    <string name="home">Home</string>
    <string name="favorite">Favorite</string>
    <string name="shopping">Shopping list</string>
    <string name="action_settings">Settings</string>
    <string name="hello_blank_fragment">Hello blank fragment</string>
</resources>

 

COLORS.XML

Open the colors.xml file in the same location as the strings.xml file and add the code below to the file.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorPrimary">#704630</color>
    <color name="colorPrimaryDark">#402F26</color>
    <color name="colorAccent">#BC8264</color>
    <color name="colorWhite">#F0F1F5</color>
    <color name="colorHighlight">#A6BAAC</color>
</resources>

 

MainActivity.java and activity_main.xml

Now, lets move over to the MainActivity.java file and its corresponding layout file created for us by android studio when we created our project.

Since this activity page will serve as an intro splash screen, we will design a background image that we will attach to the root ViewGroup in the activity_main.xml.

Open the activity_main.xml file and add the code below. Please note that you are free to use any background image of your choice. The image used in the layout file is present in the downloadable project folder at the end of this tutorial.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/recipe"
    tools:context="com.inducesmile.androidfirebaseuirecyclerview.MainActivity">
    
</RelativeLayout>

For the MainActivity page, we will use a Handler and call its postDelay method and pass a waiting time of 3 seconds before we transits to another page.

Open the MainActivity.java file and add the code below.

import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.Handler;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.Window;
import android.view.WindowManager;
public class MainActivity extends AppCompatActivity {
    private static final String TAG = MainActivity.class.getSimpleName();
    private final int SPLASH_DISPLAY_LENGTH = 3000;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        ActionBar actionBar = getSupportActionBar();
        if(null != actionBar){
            actionBar.hide();
        }
        new Handler().postDelayed(new Runnable(){
            @Override
            public void run(){
                Intent startActivityIntent = new Intent(MainActivity.this, MainRecipeActivity.class);
                startActivity(startActivityIntent);
                MainActivity.this.finish();
            }
        }, SPLASH_DISPLAY_LENGTH);
    }
}

 

MainRecipeActivity.java and activity_main_recipe.xml

The MainRecipeActivity page is loading after the splash screen intro page. This page contain a Navigation drawer that will have access to three different Fragment class as menu options.

The following layout files are used when we create a Navigation Drawer Page using the template that Android Studio provides.

app_bar_main_recipe.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout 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"
    android:fitsSystemWindows="true"
    tools:context="com.inducesmile.androidfirebaseuirecyclerview.MainRecipeActivity">
    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">
        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/AppTheme.PopupOverlay" />
    </android.support.design.widget.AppBarLayout>
    <include layout="@layout/content_main_recipe" />
</android.support.design.widget.CoordinatorLayout>

 

content_main_recipe.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:id="@+id/main_container_wrapper"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context="com.inducesmile.androidfirebaseuirecyclerview.MainRecipeActivity"
    tools:showIn="@layout/app_bar_main_recipe">
</RelativeLayout>

 

nav_header_main_recipe.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="@dimen/nav_header_height"
    android:background="@color/colorPrimary"
    android:gravity="bottom"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:theme="@style/ThemeOverlay.AppCompat.Dark">
    <de.hdodenhof.circleimageview.CircleImageView
        android:layout_width="@dimen/_70sdp"
        android:layout_height="@dimen/_100sdp"
        android:layout_gravity="center"
        android:layout_marginTop="@dimen/_16sdp"
        android:src="@drawable/cook"
        android:id="@+id/circleView"/>
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:paddingTop="@dimen/_18sdp"
        android:textSize="@dimen/_14sdp"
        android:text="@string/inducesmile"
        android:layout_gravity="center"
        android:textAppearance="@style/TextAppearance.AppCompat.Body1" />
</LinearLayout>

 

recipe_category_list.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    <ImageView
        android:id="@+id/recipe_image"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:contentDescription="@string/app_name"
        android:src="@drawable/cake"
        android:adjustViewBounds="true"/>
    <TextView
        android:id="@+id/recipe_name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        android:padding="@dimen/_16sdp"
        android:background="@color/colorAccent"
        android:textColor="@color/colorWhite"
        android:textSize="@dimen/_16sdp"
        android:textStyle="bold"
        android:gravity="center"
        android:text="@string/hello_blank_fragment" />
</FrameLayout>

 

For the MainRecipeActivity.java

import android.os.Bundle;
import android.support.design.widget.NavigationView;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
public class MainRecipeActivity extends AppCompatActivity {
    private static final String TAG = MainRecipeActivity.class.getSimpleName();
    private FragmentManager fragmentManager;
    private Fragment fragment;
    private FragmentTransaction fragmentTransaction;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main_recipe);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
        drawer.setDrawerListener(toggle);
        toggle.syncState();
        fragmentManager = getSupportFragmentManager();
        fragmentTransaction = fragmentManager.beginTransaction();
        fragment = new RecipeCategoriesFragment();
        fragmentTransaction.replace(R.id.main_container_wrapper, fragment);
        fragmentTransaction.commit();
        NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
        navigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
            @Override
            public boolean onNavigationItemSelected(MenuItem item) {
                int id = item.getItemId();
                if (id == R.id.nav_camera) {
                    fragment = new RecipeCategoriesFragment();
                } else if (id == R.id.nav_gallery) {
                    fragment = new FavoriteFragment();
                } else if (id == R.id.nav_slideshow) {
                    fragment = new ShoppingFragment();
                }
                FragmentTransaction transaction = fragmentManager.beginTransaction();
                transaction.replace(R.id.main_container_wrapper, fragment);
                transaction.commit();
                DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
                assert drawer != null;
                drawer.closeDrawer(GravityCompat.START);
                return true;
            }
        });
    }
    @Override
    public void onBackPressed() {
        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        if (drawer.isDrawerOpen(GravityCompat.START)) {
            drawer.closeDrawer(GravityCompat.START);
        } else {
            super.onBackPressed();
        }
    }
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main_recipe, menu);
        return true;
    }
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }
}

 

RecipeCategoriesFragment.java and fragment_recipe_categories.xml

We will create a new Fragment class with its corresponding layout file. This Fragment will load all our RecipeCategories from the Firebase Database. The Firebase database stores the recipe category name and a reference url to the recipe category image.

The fragment_recipe_categories.xml layout file contains a RecyclerView control. The View control will list all the recipe categories through the FirebaseRecyclerAdapter binding.

Open the fragment_recipe_categories.xml and add the code below to the file.

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.inducesmile.androidfirebaseuirecyclerview.RecipeCategoriesFragment">
    <android.support.v7.widget.RecyclerView
        android:id="@+id/recipe_categories"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:orientation="vertical"
        android:scrollbars="none"/>
</RelativeLayout>

For the RecipeCategoriesFragment class, we will get the instance of the RecyclerView control and call its setAdapter method to pass a custom FirebaseRecyclerAdapter as parameter.

Open the RecipeCategoriesFragment and add the code below to the file.

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
public class RecipeCategoriesFragment extends Fragment {
    private static final String TAG = RecipeCategoriesFragment.class.getSimpleName();
    private RecyclerView recipeRecyclerview;
    private LinearLayoutManager linearLayoutManager;
    private RecipeAdapter mRecipeAdapter;
    private DatabaseReference mDatabaseRef;
    private DatabaseReference childRef;
    public RecipeCategoriesFragment() {
    }
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_recipe_categories, container, false);
        getActivity().setTitle(getString(R.string.recipe_categories));
        linearLayoutManager = new LinearLayoutManager(getActivity());
        recipeRecyclerview = (RecyclerView)view.findViewById(R.id.recipe_categories);
        recipeRecyclerview.setHasFixedSize(true);
        mDatabaseRef = FirebaseDatabase.getInstance().getReference();
        childRef = mDatabaseRef.child("recipes");
        mRecipeAdapter = new RecipeAdapter(RecipeObject.class, R.layout.recipe_category_list, RecipeHolder.class, childRef, getContext());
        recipeRecyclerview.setLayoutManager(linearLayoutManager);
        recipeRecyclerview.setAdapter(mRecipeAdapter);
        return view;
    }
}

 

RecipeHolder.java

Since we are creating a custom FirebaseRecycerAdapter, we will create a simple java file which will make use of the ViewHolder pattern.

Create a new java file in your package folder. Name the file RecipeHolder and let the class extends RecyclerView.ViewHolder.

Open the file and add the code below to it

import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
public class RecipeHolder extends RecyclerView.ViewHolder{
    private static final String TAG = RecipeHolder.class.getSimpleName();
    public TextView recipeName;
    public ImageView recipeImage;
    public RecipeHolder(View itemView) {
        super(itemView);
        recipeName = (TextView)itemView.findViewById(R.id.recipe_name);
        recipeImage = (ImageView)itemView.findViewById(R.id.recipe_image);
    }
}

 

RecipeAdapter.java

We will follow the same pattern as we did with RecipeHolder class but then we will name our new file RecipeAdapter.java.

This class will extends the FirebaseRecyclerAdapter and ovvrride the populateViewHolder(RecipeHolder viewHolder, RecipeObject model, int position) of the class.

This adapter class takes in a layout file as one of its parameter. Open the file and add the code below.

import android.content.Context;
import android.util.Log;
import com.bumptech.glide.Glide;
import com.firebase.ui.database.FirebaseRecyclerAdapter;
import com.google.firebase.database.DatabaseReference;
public class RecipeAdapter extends FirebaseRecyclerAdapter<RecipeObject, RecipeHolder>{
    private static final String TAG = RecipeAdapter.class.getSimpleName();
    private Context context;
    public RecipeAdapter(Class<RecipeObject> modelClass, int modelLayout, Class<RecipeHolder> viewHolderClass, DatabaseReference ref, Context context) {
        super(modelClass, modelLayout, viewHolderClass, ref);
        this.context = context;
    }
    @Override
    protected void populateViewHolder(RecipeHolder viewHolder, RecipeObject model, int position) {
        viewHolder.recipeName.setText(model.getRecipename());
        Glide.with(context).load(model.getRecipeImageUrl()).into(viewHolder.recipeImage);
    }
}

 

recipe_category_list.xml

As I mentioned in the RecipeAdapter class, its constructor will take in a layout file as part of its parameters.

We will create a new layout file inside the layout folder. Name this file recipe_category_list.xml. Open this file and copy the code below to it.

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    <ImageView
        android:id="@+id/recipe_image"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:contentDescription="@string/app_name"
        android:src="@drawable/cake"
        android:adjustViewBounds="true"/>
    <TextView
        android:id="@+id/recipe_name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        android:padding="@dimen/_16sdp"
        android:background="@color/colorAccent"
        android:textColor="@color/colorWhite"
        android:textSize="@dimen/_16sdp"
        android:textStyle="bold"
        android:gravity="center"
        android:text="@string/hello_blank_fragment" />
</FrameLayout>

 

FavoriteFragment.java and fragment_favorite.xml

We will create a new Fragment file and name it FavoriteFragment.java. The corresponding layout file – fragment_favorite.xml will contain an ImageView and a TextView controls.

Open the fragment_favorite.xml and add the code below to the file.

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.inducesmile.androidfirebaseuirecyclerview.FavoriteFragment">
    <ImageView
        android:id="@+id/cook_image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:contentDescription="@string/app_name"
        android:src="@drawable/cook"
        android:layout_marginTop="@dimen/_80sdp"
        android:layout_centerHorizontal="true"
        android:layout_alignParentTop="true"/>
    <TextView
        android:id="@+id/favorite"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_below="@+id/cook_image"
        android:layout_marginTop="@dimen/_12sdp"
        android:textSize="@dimen/_13sdp"
        android:padding="@dimen/_12sdp"
        android:textColor="@color/colorPrimaryDark"
        android:text="@string/favorite_info" />
</RelativeLayout>

For FavoriteFragment class, we will retain the default content. Open the file and add the code below to it.

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class FavoriteFragment extends Fragment {
    public FavoriteFragment() {
    }
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_favorite, container, false);
        getActivity().setTitle(getString(R.string.favorite));
        return view;
    }
}

 

Android FirebaseRecyclerAdapter populateViewHolder() never get called

After I finished this tutorial, I updated my Google Play Services and Google Repository and I realized that the FirebaseRecyclerAdapter populateViewHolder() method is never get called sometimes.

I don’t know if this issues is from Android SDK or Firebase. If you found a solution to it, kindly leave a comment and share your solution.

 

Conclusion

This brings us to the end of this tutorial. I hope that you have learn something. Run your app and see for yourself.

You can download the code for this tutorial below. If you are having hard time downloading the tutorial, kindly contact me.

Remember to subscribe with your email address so that you will be among the first to receive my new android blog post once it is published.

Please if you love this tutorial, kindly download my android app – Complete Mathematics – in Google Play Store and let me know what you think about it.

7 Comments

    • Inducesmile

Add a Comment