Android Working With Firebase Realtime Database and Storage

Firebase

A fully managed platform for building iOS, Android, and web apps that provides automatic data synchronization, authentication services, messaging, file storage, analytics, and more.

All the data is stored in JSON format and any changes in data, reflects immediately by performing a sync across all the platforms & devices. This allows us to build more flexible realtime apps easily with minimal effort.

This article covers basics integration of firebase Realtime Database and  Storage. The other concepts like performs data validations, firebase access rules also covered. If you are new to firebase, then this article helps you a lot in understanding the basic knowledge of working with firebase.

 

1. How the Data is Stored – JSON Structured

Firebase realtime database is a schemaless database in which the data is stored in JSON format. Basically the entire database is a big JSON tree with multiple nodes. So when you plan your database, you need to prepare the json structure in way that the data is accessible in easier way by avoiding nesting of child nodes.

Here is an example of storing list of message in json tree. You can go through firebase  Structure Your Database  guide to learn the best practices while defining the database structure.

 

 

2. Offline Data

Firebase provides great support when comes to offline data. It automatically stores the data offline when there is no internet connection. When the device connects to internet, all the data will be pushed to realtime database. However enabling disk persistence stores the data offline even though app restarts. Disk persistence can be enabled by calling below one line code. Here is complete guide about firebase  offline capabilities.

//supports offline data
FirebaseDatabase.getInstance().setPersistenceEnabled(true);

3. Inserting Data

The realtime database accepts multiple data types StringLongDoubleBooleanMap<String, Object>List<Object> to store the data. You can also use custom java objects to store the data which is very helpful when storing model class directly in database.

Let’s say you want to store message details in the database. First you need to create  model with an empty constructor and other properties.

public class FriendlyMessage {

    private String text;
    private String name;
    private String photoUrl;


// Default constructor required for calls to
// DataSnapshot.getValue(FriendlyMessage.class)
 public FriendlyMessage() {
 }

 public FriendlyMessage(String text, String name, String photoUrl) {
 this.text = text;
 this.name = name;
 this.photoUrl = photoUrl;
 }

}

As every FriendlyMessage needs a unique Id, you can generate one by calling push() method which creates an empty node with unique key. Then get the reference to ‘message’ node using child() method. Finally use setValue() method to store the FriendlyMessage data.

 

DatabaseReference mMessagesDatabaseRef;
//database reference
mMessagesDatabaseRef = FirebaseDatabase.getInstance().getReference().child("message");
// creating FriendlyMessage object
FriendlyMessage message = new FriendlyMessage(text, mUsername, null);
// pushing message to 'message' node using the userId
//push() will automatically generate a uniqueId
mMessagesDatabaseRef.push().setValue(message);

By running the above code, a new user node will be inserted in database with a unique key value.

4. Reading Data

To read the data, you need to attach the ChildEventListener() to the database reference. This event will be triggered  for all existing childs and also for the added one.

mChildEventListener = new ChildEventListener() {
    @Override
    public void onChildAdded(DataSnapshot dataSnapshot, String s) {
        FriendlyMessage friendlyMessage = dataSnapshot.getValue(FriendlyMessage.class);

        mMessageAdapter.add(friendlyMessage);
    }

    @Override
    public void onChildChanged(DataSnapshot dataSnapshot, String s) {

    }

    @Override
    public void onChildRemoved(DataSnapshot dataSnapshot) {

    }

    @Override
    public void onChildMoved(DataSnapshot dataSnapshot, String s) {

    }

    @Override
    public void onCancelled(DatabaseError databaseError) {

    }
};

//ChildEventListener triggered for each child in message and also for the new child added
mMessagesDatabaseRef.addChildEventListener(mChildEventListener);

5. Firebase Storage

Cloud Storage is built for app developers who need to store and serve user-generated content, such as photos or videos.

  1. 1 Uploading Files to Firebase Storage
Uri selectedImageUri = data.getData();

//get a reference to store file at chat_photos/<FILENAME>
StorageReference photoRef = mChatPhotosStorageRef.child(selectedImageUri.getLastPathSegment());

//Upload file to firebase storage
photoRef.putFile(selectedImageUri).addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
    @Override
    public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {

        Uri downloadUrl = taskSnapshot.getDownloadUrl();
       
    }
});

 

  1. 2 Retrieving Uploaded Files from Firebase Storage
Uri selectedImageUri = data.getData();

//get a reference to store file at chat_photos/<FILENAME>
StorageReference photoRef = mChatPhotosStorageRef.child(selectedImageUri.getLastPathSegment());

//Upload file to firebase storage
photoRef.putFile(selectedImageUri).addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
    @Override
    public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {

        Uri downloadUrl = taskSnapshot.getDownloadUrl();

//Here i am getting the upload file uri(downloadUrl) and setting it into FirebaseDatabase object
 //(mMessagesDatabaseRef) using model class object. 
        FriendlyMessage friendlyMessage = new FriendlyMessage(null, mUsername, downloadUrl.toString());
        mMessagesDatabaseRef.push().setValue(friendlyMessage);
    }
});

Here i am getting the FriendlyMessage object using dataSnapshot.getValue(FriendlyMessage.class) and adding it into listView using mMessageAdapter.

 

mChildEventListener = new ChildEventListener() {
    @Override
    public void onChildAdded(DataSnapshot dataSnapshot, String s) {
        FriendlyMessage friendlyMessage = dataSnapshot.getValue(FriendlyMessage.class);

        mMessageAdapter.add(friendlyMessage);
    }

    @Override
    public void onChildChanged(DataSnapshot dataSnapshot, String s) {

    }

    @Override
    public void onChildRemoved(DataSnapshot dataSnapshot) {

    }

    @Override
    public void onChildMoved(DataSnapshot dataSnapshot, String s) {

    }

    @Override
    public void onCancelled(DatabaseError databaseError) {

    }
};

//ChildEventListener triggered for each child in message and also for the new child added
mMessagesDatabaseRef.addChildEventListener(mChildEventListener);

 

6. Security & Rules

Firebase rules provides a way to identify user role while performing read and write operations. These rules will acts a security layer on the server before perform any CRUD operation. By default the rules allows user to perform read & write operation only after authentication.

The below rules allow authenticated users only to read or write data.

Below rules allows everyone to read & write data without authentication.

 

You can also use these rules to validate data before inserting into database. For example below rules validates the name to be less than 50 chars and text to be less than 200 chars.

Go through firebase security & rules guide to learn more about the security concepts.

 

Now we have enough knowledge to get started with an android project. Let’s create one and see how to integrate the realtime database and cloud storage with an FriendlyChatApp.

7. Creating Android Project

1. First thing you need to do is go to https://firebase.google.com/and make an account to gain access to their console. After you gain  access to the console you can start by creating your first project.

2. Give the package name of your project  in which you are going to integrate the Firebase. Here the google-services.json file will be downloaded when you press add app button.

 

3. Create a new project in Android Studio from File ⇒ New Project. While filling the project details, use the same package name which you gave in firebase console.

4. Paste the google-services.json file to your project’s app This step is very important as your project won’t build without this file.

5.  Now open the build.gradle located in project’s home directory and add google playstore dependency.

6. Open app/build.gradle and add firebase database  dependency.At the very bottom of the file, add apply plugin: ‘com.google.gms.google-services’

7. In order to store messages, we need a model class called FriendlyMessage.java. Create a class named FriendlyMessage.java and add below code.

package com.example.lenovo.friendlychatapp.model;


public class FriendlyMessage {

    private String text;
    private String name;
    private String photoUrl;

    // Default constructor required for calls to
    // DataSnapshot.getValue(FriendlyMessage.class)

    public FriendlyMessage() {
    }

    public FriendlyMessage(String text, String name, String photoUrl) {
        this.text = text;
        this.name = name;
        this.photoUrl = photoUrl;
    }

    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPhotoUrl() {
        return photoUrl;
    }

    public void setPhotoUrl(String photoUrl) {
        this.photoUrl = photoUrl;
    }
}

8.  Open the activity_main.xml and add the below code.

activity_main.xml

<?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"

    tools:context="com.example.lenovo.friendlychatapp.MainActivity">

    <ListView
        android:id="@+id/messageListView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@+id/linearLayout"
        android:divider="@android:color/transparent"
        android:stackFromBottom="true"
        android:transcriptMode="alwaysScroll"
        tools:listitem="@layout/item_message" />


    <LinearLayout
        android:id="@+id/linearLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:orientation="horizontal">

        <ImageButton
            android:id="@+id/photoPickerButton"
            android:layout_width="36dp"
            android:layout_height="36dp"
            android:background="@android:drawable/ic_menu_gallery" />

        <EditText
            android:id="@+id/messageEditText"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:layout_weight="1" />

        <Button
            android:id="@+id/sendButton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom"
            android:enabled="false"
            android:text="send" />

    </LinearLayout>


</RelativeLayout>

9.Open item_message.xml and add the below code.This layout defines one single item of listview.

item_message.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <ImageView
        android:id="@+id/photoImageView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:adjustViewBounds="true" />

    <TextView
        android:id="@+id/messageTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="0"
        android:textAppearance="?android:attr/textAppearanceLarge"
        tools:text="Message" />

    <TextView
        android:id="@+id/nameTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="0"
        android:textAppearance="?android:attr/textAppearanceSmall"
        tools:text="Name" />

</LinearLayout>

10. Open MessageAdapter.Java and add the below code. This adapter will  show list of messages retrieve from database in listview.

MessageAdapter.Java

package com.example.lenovo.friendlychatapp;

import android.app.Activity;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.example.lenovo.friendlychatapp.model.FriendlyMessage;
import java.util.List;

public class MessageAdapter extends ArrayAdapter<FriendlyMessage> {

    public MessageAdapter(Context context, int resource, List<FriendlyMessage> objects) {
        super(context, resource, objects);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        if (convertView == null) {
            convertView = ((Activity) getContext()).getLayoutInflater().inflate(R.layout.item_message, parent, false);
        }

        ImageView photoImageView = (ImageView) convertView.findViewById(R.id.photoImageView);
        TextView messageTextView = (TextView) convertView.findViewById(R.id.messageTextView);
        TextView authorTextView = (TextView) convertView.findViewById(R.id.nameTextView);

        FriendlyMessage message = getItem(position);

        boolean isPhoto = message.getPhotoUrl() != null;
        if (isPhoto) {
            messageTextView.setVisibility(View.GONE);
            photoImageView.setVisibility(View.VISIBLE);
            Glide.with(photoImageView.getContext())
                    .load(message.getPhotoUrl())
                    .into(photoImageView);
        } else {
            messageTextView.setVisibility(View.VISIBLE);
            photoImageView.setVisibility(View.GONE);
            messageTextView.setText(message.getText());
        }
        authorTextView.setText(message.getName());

        return convertView;
    }
}

11. Open MainActivity.Java and add the below code. The code is very simple and easily understandable.

MainActivity.Java

package com.example.lenovo.friendlychatapp;

import android.content.Intent;
import android.net.Uri;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ListView;
import com.example.lenovo.friendlychatapp.model.FriendlyMessage;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.firebase.database.ChildEventListener;
import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
import com.google.firebase.storage.FirebaseStorage;
import com.google.firebase.storage.StorageReference;
import com.google.firebase.storage.UploadTask;
import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    private static final int RC_PHOTO_PICKER = 2;


    private MessageAdapter mMessageAdapter;
    private ImageButton mPhotoPickerButton;
    private EditText mMessageEditText;
    private Button mSendButton;
    List<FriendlyMessage> friendlyMessages;
    ListView mMessageListView;
    private String mUsername;
    DatabaseReference mMessagesDatabaseRef;
    StorageReference mChatPhotosStorageRef;
    private ChildEventListener mChildEventListener;


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

        // you can give any name
        mUsername = "Arun Kumar";

        //supports offline data
        FirebaseDatabase.getInstance().setPersistenceEnabled(true);

        //database reference
        mMessagesDatabaseRef = FirebaseDatabase.getInstance().getReference().child("message");

        //storage reference
        mChatPhotosStorageRef = FirebaseStorage.getInstance().getReference().child("chat_photos");

        // Initialize references to views
        mMessageListView = (ListView) findViewById(R.id.messageListView);
        mPhotoPickerButton = (ImageButton) findViewById(R.id.photoPickerButton);
        mMessageEditText = (EditText) findViewById(R.id.messageEditText);
        mSendButton = (Button) findViewById(R.id.sendButton);

        // Initialize message ListView and its adapter
        friendlyMessages = new ArrayList<>();
        mMessageAdapter = new MessageAdapter(this, R.layout.item_message, friendlyMessages);
        mMessageListView.setAdapter(mMessageAdapter);


        // Enable Send button when there's text to send
        mMessageEditText.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
            }

            @Override
            public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
                if (charSequence.toString().trim().length() > 0) {
                    mSendButton.setEnabled(true);
                } else {
                    mSendButton.setEnabled(false);
                }
            }

            @Override
            public void afterTextChanged(Editable editable) {
            }
        });


        // Send button sends a message and clears the EditText
        mSendButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //  Send messages on click
                String text = mMessageEditText.getText().toString();

                // creating FriendlyMessage object
                FriendlyMessage message = new FriendlyMessage(text, mUsername, null);

                // pushing message to 'message' node using the userId
                //push() will automatically generate a uniqueId
                mMessagesDatabaseRef.push().setValue(message);

                // Clear input box
                mMessageEditText.setText("");
            }
        });


        mChildEventListener = new ChildEventListener() {
            @Override
            public void onChildAdded(DataSnapshot dataSnapshot, String s) {
                FriendlyMessage friendlyMessage = dataSnapshot.getValue(FriendlyMessage.class);

                mMessageAdapter.add(friendlyMessage);
            }

            @Override
            public void onChildChanged(DataSnapshot dataSnapshot, String s) {

            }

            @Override
            public void onChildRemoved(DataSnapshot dataSnapshot) {

            }

            @Override
            public void onChildMoved(DataSnapshot dataSnapshot, String s) {

            }

            @Override
            public void onCancelled(DatabaseError databaseError) {

            }
        };

        //ChildEventListener triggered for each child in message and also for the new child added
        mMessagesDatabaseRef.addChildEventListener(mChildEventListener);

        // ImagePickerButton shows an image picker to upload a image for a message
        mPhotoPickerButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                // ImagePickerButton shows an image picker to upload a image for a message

                Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
                intent.setType("image/jpeg");
                intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
                startActivityForResult(Intent.createChooser(intent, "Complete action using"), RC_PHOTO_PICKER);

            }
        });

    }


    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == RC_PHOTO_PICKER && resultCode == RESULT_OK) {

            Uri selectedImageUri = data.getData();

            //get a reference to store file at chat_photos/<FILENAME>
            StorageReference photoRef = mChatPhotosStorageRef.child(selectedImageUri.getLastPathSegment());

            //Upload file to firebase storage
            photoRef.putFile(selectedImageUri).addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
                @Override
                public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {

                    Uri downloadUrl = taskSnapshot.getDownloadUrl();
                    FriendlyMessage friendlyMessage = new FriendlyMessage(null, mUsername, downloadUrl.toString());
                    mMessagesDatabaseRef.push().setValue(friendlyMessage);
                }
            });

        }
    }


}

When you run the app it will look like this :

 

 

When you click on ImagePicker button it will ask Complete action Using.Using one select an image you want to send and then click ok.

 

          

 

 

Leave a Reply