Community Post

Build A Simple Blog App With Firebase in Android Studio

Peter Ekene
👁️ 0 views
💬 comments

Firebase is a Backend-as-a-Service — BaaS — that started as a YC11 startup and grew up into a next-generation app-development platform on Google Cloud Platform.

With Firebase developers won’t need to manage servers or write APIs (for less complex projects). Firebase is your server, your API and your datastore, all written so generically that you can modify it to suit most needs. You’ll occasionally need to use other bits of the Google Cloud technologies for your advanced applications. Firebase can’t be everything to everybody. But it gets pretty close. However we won't be focusing on the features of Firebase in this post, if you'd like to know more about it, how it works and what it offers, you can checkout this post here or visit the docs.

What we'll be building

In this post we'll be building a Mobile Blog App with Android Studio, it's going to be a basic CRUD Application with Firebase as the backend database. Users will be able to write a post (i.e upload a post Image from gallery, write a Title for the post and provide the Content (Description) of the post and then post it to Firebase). On succesfully posting to Firebase, the post will be rendered on the homepage where other users can read the post.

Features

By the virtue of this post, you'll learn a few other thinhgs like

  1. Creating users with email and password
  2. Registering and Signing in Users
  3. Logging out signed-in Users
  4. Writing to and Reading from database
  5. Deleting data from Firebase within the app
  6. Creating User Profiles and Croping images on upload
  7. Associating Posts with respective owners.

Here's a screenshot of the app running on my android device, this should give you a visual representation of what we are trying to achieve while we go on ahead and build our App.

Create the project

So create a new Android Studio project called Blogzone (if you can’t or still want to learn how to create a new project in Android Studio, see my article on Parsing remote JSON, I explained how to do it in details). While creating this project, use the Basic Activity Template or anyone else you are most comfortable with. Next, connect your app to Firebase, there are more than one way to do this but i’ll stick with the way i prefer amongst others and that is using the firebase assistant in Android Studio (For Android Studio 2.2 or later). If you're using an older version of Android Studio or have a more complex project configuration, you can still manually add Firebase to your app

Connecting via the Assistant, Here's how :

  1. Click on the tools tab in Android Studio
  2. Click on firebase
  3. Click Authentication and
  4. Click Connect to firebase

After Android Studio has created the Firebase project, Click on “Add firebase Authentication to your project” Note: your system has to be online for this part. If you’ve not done this before, it’ll probably ask for your firebase credentials so supply it and proceed. Once your projected is connected to firebase, you’ll get a prompt to allow Firebase make changes to your gradle files, allow it and it’ll automatic add the neccesary firebase dependencies to your build.gradle file and Boooom!! Your firebase app is ready, congrats!! If that's still not clear enough for you, you can find more detailed information here.

Layout

We'll be creating layouts as the need arises, this is because we'll have a number of them, it's not right to create all the required layouts first before building. Heck if i hadn't already built this app i wouldn't even know how many layouts i'll be needing. Creating the project with the Basic Template had already generated two layout files for us [activity_main.xml and content_main.xml] so we go right ahead and start writing the layout codes. Open the activity_main.xml resource file and type in this code.

<?xml version="1.0" encoding="utf-8"?>

This is basically how your activity_main.xml file will look like if you used the basic activity template so i've not done anything new apart from set a custom background image. What we have here is a simple Cordinator Layout with AppBar widgets.

Activities

Just like layouts, we'll be creating Activities as the need arises, however we'll be creating a new Activity right now called PostActivity. Go ahead and create a new Activity with this progression

Here we'll let users select an image from their device gallery, type a title and description for the post and send. So let's design the layout of this Activity (activity_post.xml) to capture the neccesary view objects (ImageButton, EditText, and a Button).

<?xml version="1.0" encoding="utf-8"?>

This Layout will look like this in your xml visualizer

Here we have an ImageButton that allows the user pick images from the gallery, two EditText fields to define the post title and description and a Button to post the page contents to Firebase. Fairly simple right ? now lets hook up the PostActivity Class to handle all the functionalities we want.

PostActivity.java

Here we'll get instances of the view objects we created in the activity_post.xml layout file, read values from them and with the help of the Post Button, send all the data to our Firebase database.

package com.example.ekene.blogzone;
public class PostActivity extends AppCompatActivity {
    // imports
    private ImageButton imageBtn; private static final int GALLERY_REQUEST_CODE = 2;
    private Uri uri = null; private EditText textTitle; private EditText textDesc;
    private Button postBtn; private StorageReference storage; private FirebaseDatabase database;
    private DatabaseReference databaseRef; private FirebaseAuth mAuth;
    private DatabaseReference mDatabaseUsers; private FirebaseUser mCurrentUser;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_post);
        // initializing objects
        postBtn = (Button)findViewById(R.id.postBtn);
        textDesc = (EditText)findViewById(R.id.textDesc);
        textTitle = (EditText)findViewById(R.id.textTitle);
        storage = FirebaseStorage.getInstance().getReference();
        databaseRef = database.getInstance().getReference().child("Blogzone");
        mAuth = FirebaseAuth.getInstance();
        mCurrentUser = mAuth.getCurrentUser();
        mDatabaseUsers = FirebaseDatabase.getInstance().getReference().child("Users").child(mCurrentUser.getUid());
        imageBtn = (ImageButton)findViewById(R.id.imageBtn);
        //picking image from gallery
        imageBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent galleryIntent = new Intent(Intent.ACTION_GET_CONTENT);
                galleryIntent.setType("image/*");
                startActivityForResult(galleryIntent, GALLERY_REQUEST_CODE);
            }        });
        // posting to Firebase
        postBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Toast.makeText(PostActivity.this, "POSTING...", Toast.LENGTH_LONG).show();
                final String PostTitle = textTitle.getText().toString().trim();
                final String PostDesc = textDesc.getText().toString().trim();
                // do a check for empty fields
                if (!TextUtils.isEmpty(PostDesc) && !TextUtils.isEmpty(PostTitle)){
                    StorageReference filepath = storage.child("post_images").child(uri.getLastPathSegment());
                    filepath.putFile(uri).addOnSuccessListener(new OnSuccessListener() {
                        @Override
                        public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {                    
                            @SuppressWarnings("VisibleForTests")
                            //getting the post image download url
                            final Uri downloadUrl = taskSnapshot.getDownloadUrl();
                            Toast.makeText(getApplicationContext(), "Succesfully Uploaded", Toast.LENGTH_SHORT).show();
                            final DatabaseReference newPost = databaseRef.push();
                            //adding post contents to database reference
                            mDatabaseUsers.addValueEventListener(new ValueEventListener() {
                                @Override
                                public void onDataChange(DataSnapshot dataSnapshot) {
                                    newPost.child("title").setValue(PostTitle);
                                    newPost.child("desc").setValue(PostDesc);
                                    newPost.child("imageUrl").setValue(downloadUrl.toString());
                                    newPost.child("uid").setValue(mCurrentUser.getUid());
                                    newPost.child("username").setValue(dataSnapshot.child("name").getValue())
                                            .addOnCompleteListener(new OnCompleteListener() {
                                                @Override
                                                public void onComplete(@NonNull Task task) {
                                                    if (task.isSuccessful()){
                                                        Intent intent = new Intent(PostActivity.this, MainActivity.class);
                                                        startActivity(intent);
                                                    }}});}
                                @Override
                                public void onCancelled(DatabaseError databaseError) {
                                } }); } }); }}});    }
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
    //image from gallery result
        if (requestCode == GALLERY_REQUEST_CODE && resultCode == RESULT_OK){
            uri = data.getData();
            imageBtn.setImageURI(uri);
        }}}

So i'll do a brief breakdown of what's going on inside this class to provide more clarity. Inside the onCreate() method, we initialized all the view and Firebase Objects that will be relevant for handling the required task in this Class. Then we set up the image button to access the users device gallery and pick a desired image for the post using an intent. For the purpose of accessing the users external storage, we'll have to ask for the users permission to access their device storage by adding this line of code into the application manifest.xml file.

Then we proceed to set up the post button such that upon click, it should store the page contents to Firebase. To achieve this, first we "got" the values coming from the EditText fields and "stored" them into String variables, then did a check to make certain that none of the fields are empty afterwhich we then called an instance of the Firebase StorageReference where we specified the path to store the post images. Then to store the post Title and Description into our database, we called an instance of Firebase DatabaseReference and added a child to it (new post) where we'll then store all the values.

Since we have not authenticated users, you'll need to log into your firebase console and open the RULES tab under Database and Storage and set the Read and Write rules to null so that you can post data to it.

This is because by default, it is set to false (no one can post to it, yeah not even you) however, we'll soon be authenticating users so we'll eventually set it back to false so that only authenticated users can read or write to our database.

Rendering

At this point we can post to Firebase, next tasl is to have this post appear on our homepage from where we can view them. First off we need to create a layout file that will hold the data coming from Firebase, we'll do this with CardViews as it'll be the best to handle the task given the nature of the App. So head on over to layout in resources and create a new layout resource file

Then name the new Layout file as you please, in my case i'll call it card_items as it'll hold the contents of the blog post coming from Firebase.

Next we design the card_items.xml file we just created to hold the post (image, title, description and username associated with the owner of the particular post). As you can already guess, we'll be needing an ImageView and three TextView objects here.

<?xml version="1.0" encoding="utf-8"?>

This Layout will look like this in your xml visualizer

Having created the layout to hold the individual posts coming from Firebase, it's time to create our Recyclerview Layout in the content_main.xml. This view will hold the Cards we just created in the layout above. So open your content_main.xml file (came with the basic activity template) and create your Recyclerview

<?xml version="1.0" encoding="utf-8"?>

This is the Layout that renders on the activity_main.xml.

Recap: What we have just done is create a Cardview layout that will contain the contents of the blog post made by the user for the purpose of showing it to other users. To achieve this, we also created a Recyclerview layout where we'll be posting the individual Cards (populating the recyclerview layout with the cardview layout). Now let's proceed to the MainActivity.java file and write the code that will acheieve this desired functionality (to render the post from our database to the defined view objects).

Before that we need to create a model class that will act like an Adapter to help us bind the data from the server down to the respective view objects, hence, create a new java class called "Blogzone". This will be our model adapter class. Inside this Blogzone class that we just created, define four String variables, generate a constructor for them and also generate their respective "setters" and "getters"

package com.example.ekene.blogzone;
public class Blogzone {
// define four String variables
    private String title, desc, imageUrl, username;
    // generate their respective constructors
    public Blogzone(String title, String desc, String imageUrl, String username) {
        this.title = title;
        this.desc = desc;
        this.imageUrl=imageUrl;
        this.username = username;
    }
    // create an empty constructor
    public Blogzone() {
    }
    public void setImageUrl(String imageUrl) {
        this.imageUrl = imageUrl;
    } 
    public void setUsername(String username) {
        this.username = username;
    }
    public void setTitle(String title) {
        this.title = title;
    }
    public void setDesc(String desc) {
        this.desc = desc;
    }
    public String getImageUrl() {
        return imageUrl;
    }
    public String getTitle() {
        return title;
    }
    public String getDesc() {
        return desc;
    }
   public String getUsername() {
        return username;
    }
}

Note: we also generated an empty constructor.

MainActivity.java

package com.example.ekene.blogzone;
public class MainActivity extends AppCompatActivity {
    private RecyclerView recyclerView;
    private DatabaseReference mDatabase;
    private FirebaseAuth mAuth;
    private FirebaseAuth.AuthStateListener mAuthListener;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        //initialize recyclerview and FIrebase objects
        recyclerView = (RecyclerView)findViewById(R.id.recyclerview);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        recyclerView.setHasFixedSize(true);
        mDatabase = FirebaseDatabase.getInstance().getReference().child("Blogzone");
        mAuth = FirebaseAuth.getInstance();
        mAuthListener = new FirebaseAuth.AuthStateListener() {
            @Override
            public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) {
                if (mAuth.getCurrentUser()==null){
                    Intent loginIntent = new Intent(MainActivity.this, RegisterActivity.class);
                    loginIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);startActivity(loginIntent);
                }}};}
    @Override
    protected void onStart() {
        super.onStart();
        mAuth.addAuthStateListener(mAuthListener);
        FirebaseRecyclerAdapter FBRA = new FirebaseRecyclerAdapter(
                Blogzone.class,
                R.layout.card_items,
                BlogzoneViewHolder.class,
                mDatabase
        ) {
            @Override
            protected void populateViewHolder(BlogzoneViewHolder viewHolder, Blogzone model, int position) {
                final String post_key = getRef(position).getKey().toString();
                viewHolder.setTitle(model.getTitle());
                viewHolder.setDesc(model.getDesc());
                viewHolder.setImageUrl(getApplicationContext(), model.getImageUrl());
                viewHolder.setUserName(model.getUsername());
                viewHolder.mView.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View view) {
                        Intent singleActivity = new Intent(MainActivity.this, SinglePostActivity.class);
                        singleActivity.putExtra("PostID", post_key);
                        startActivity(singleActivity);
                    } });}};
        recyclerView.setAdapter(FBRA);
    }
    public static class BlogzoneViewHolder extends RecyclerView.ViewHolder{
        View mView;
        public BlogzoneViewHolder(View itemView) {
            super(itemView);
            mView = itemView;
        }        public void setTitle(String title){
            TextView post_title = mView.findViewById(R.id.post_title_txtview);
            post_title.setText(title);
        }        public void setDesc(String desc){
            TextView post_desc = mView.findViewById(R.id.post_desc_txtview);
            post_desc.setText(desc);
        }        public void setImageUrl(Context ctx, String imageUrl){
            ImageView post_image = mView.findViewById(R.id.post_image);
            Picasso.with(ctx).load(imageUrl).into(post_image);
        }        public void setUserName(String userName){
            TextView postUserName = mView.findViewById(R.id.post_user);
            postUserName.setText(userName);
        }    }
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            return true;
        }       else if (id == R.id.action_add) {
            startActivity(new Intent(MainActivity.this, PostActivity.class));
        } else if (id == R.id.logout){
            mAuth.signOut();
            Intent logouIntent = new Intent(MainActivity.this, RegisterActivity.class);
            logouIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
            startActivity(logouIntent);
        }        return super.onOptionsItemSelected(item);
    }}

Here, we initialize the neccesary Firebase Objects and the Recyclerview. Then handle the user login which we'll soon get into but in the meantime, observe the code and understand how simple it is to login users with Firebase. Next we create a static inner class that extends Recyclerview.ViewHolder. This is primarily where we'll "set" the data coming from the server to it's respect view object using the Blogzone model class we had earlier created.

...................................................................................

Then we override the onStart() method and add authStateListener to our Authentication instance afterwhich we then fire up the FirebaseRecyclerAdapter that will take in two parameter (the Blogzone model class and the BlogzoneViewholder static innner class that we just created). ...................................................................................

A new instance of the FirebaseRecyclerAdapter will then take in all the four parameters which are basically the components we'll need to successfully bind the in-coming data to the viewholders ( i.e the display layout, the Blogzone class, the ViewHolder class and the database instance). The FirebaseRecyclerAdapter will implement a protected method called populateViewHolder() which takes in three parameters as well (the Blogzone model class, the ViewHolder class and an integer variable called position). With these, we can go ahead and populate our viewHolder class.

In the populateViewHolder() method, we then call the set() method on the viewHolder instance to populate it with values from the Blogzone model class. we also use a final String variable to store the post_key that will allow us identify a particular post and associate it with it's corresponding position on the recyclerview. this will come in handy while clicking on a particular card to open it's contents on a different acitivity (SinglePostActivity) as we just did with the onClickListener set on the viewHolder instance. Then finally to define an adpater for our recyclerview, we do

recyclerView.setAdapter(FBRA);

Wow..... that was alot of code yeah ? i agree, however if you did everything as you should, then on successfully posting to Firebase, your post should immediately render on your homepage (activity_main.xml) as it appeared in the app image i posted earlier.

Authentication

Now it's time to create our Registeration and Login Activities to properly authenticate athe users posting to our database. We'll start off with the Register Activity. Before a user should gain access to our App, it is wise to sign them up properly via the Register Activity and track their activities as they navigate the app using their unique id's. So lets go ahead and create the Registeration Activity.

RegisterActivity

You can refer to the Activities session of this post to review how to create and name an Activity, just incase you need to refresh that memory. So having created this activity and named it RegisterActivity, you should now have a layout file (activity_register.xml) open this layout file and create the registeration fields. for the sake of neatness i won't be pasting xml codeblocks again just to decongest the amount of code in this post, i feel it's starting to bore you by now, however i'll provide a link to the repo incase you want to have access to it for some reason. Here's a screenshot of this layout in the xml visualizer

Then we go right ahead into the RegisterActivity java class and create all the functionalities we want

package com.example.ekene.blogzone;
public class RegisterActivity extends AppCompatActivity {
    private Button registerBtn;
    private EditText emailField, usernameField, passwordField;
    private FirebaseAuth mAuth;
    private DatabaseReference mDatabase;
    private TextView loginTxtView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_register);
        loginTxtView = (TextView)findViewById(R.id.loginTxtView);
        registerBtn = (Button)findViewById(R.id.registerBtn);
        emailField = (EditText)findViewById(R.id.emailField);
        usernameField = (EditText)findViewById(R.id.usernameField);
        passwordField = (EditText)findViewById(R.id.passwordField);
        mAuth = FirebaseAuth.getInstance();
        mDatabase = FirebaseDatabase.getInstance().getReference().child("Users");
        loginTxtView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                startActivity(new Intent(RegisterActivity.this, LoginActivity.class));
            }        });
        registerBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Toast.makeText(RegisterActivity.this, "LOADING...", Toast.LENGTH_LONG).show();
                final String username = usernameField.getText().toString().trim();
                final String email = emailField.getText().toString().trim();
                final String password = passwordField.getText().toString().trim();
                if (!TextUtils.isEmpty(email) && !TextUtils.isEmpty(username)&&!TextUtils.isEmpty(password)){
                    mAuth.createUserWithEmailAndPassword(email, password).addOnCompleteListener(new OnCompleteListener() {
                        @Override
                        public void onComplete(@NonNull Task task) {
                            String user_id = mAuth.getCurrentUser().getUid();
                            DatabaseReference current_user_db = mDatabase.child(user_id);
                            current_user_db.child("Username").setValue(username);
                            current_user_db.child("Image").setValue("Default");
                            Toast.makeText(RegisterActivity.this, "Registeration Succesful", Toast.LENGTH_SHORT).show();
                            Intent regIntent = new Intent(RegisterActivity.this, ProfileActivity.class);
                            regIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                            startActivity(regIntent);
                        }                    });
                }else {

                    Toast.makeText(RegisterActivity.this, "Complete all fields", Toast.LENGTH_SHORT).show();
                }            }        });    }}

So i'm going to summarize what's going on in this class, mostly it's the same as the other classes i already explained earlier so i'll focus on what's new here which is what happens when the registerBtn is clicked.

Creating User With Email and Password

So we set an onClickListener on the registerBtn to monitor for click events which will then register this user on our database (when clicked) with the details provided in the EditText fields. First we define String variables to store the values coming from the EditText fields and then do a check to make certain that none of the fields are empty. We then called the createUserWithEmailAndPassword() method on the FirebaseAuthentication instance.

This method takes in two arguments (Email and Password) which are primarily the String variables we used to store the users email and password fields from the EditText objects. Then we attach an onCompleteListener which will then implement the onComplete() method where we'll store this registered user on our database with respect to their unique id's. If this task is successful we go ahead and get the registered users id, attach the id to our database reference [mDatabase.child(user_id)] and then set the Username and Image on the users unique path (current_users_db). Then we make a Toast to show the user that they've been successfully registered and then launch the LoginActivity for them to login with their just registered details.

LoginActivity

Now it's time to create our Login Activity. Even after registeration, before a user should gain access to our App, it is wise to sign them in properly via the LoginActivity so we can achieve the functionality of tracking their activities as they navigate the app. So lets go ahead and create the Login Activity same way we did the last one. So here's a screenshot of my layout for this activity [activity_login.xml], if you can't build this layout on your own, the code will be available for you on github. Now we go over to the LoginActivity java class and write the code that logs the user in.

package com.example.ekene.blogzone;
public class LoginActivity extends AppCompatActivity {
    private EditText loginEmail, loginPass;
    private FirebaseAuth mAuth;
    private DatabaseReference mDatabase;
    private Button loginBtn;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);
        loginBtn = (Button)findViewById(R.id.loginBtn);
        loginEmail = (EditText)findViewById(R.id.login_email);
        loginPass = (EditText)findViewById(R.id.login_password);
        mAuth = FirebaseAuth.getInstance();
        mDatabase = FirebaseDatabase.getInstance().getReference().child("Users");
        loginBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Toast.makeText(LoginActivity.this, "PROCESSING....", Toast.LENGTH_LONG).show();
                String email = loginEmail.getText().toString().trim();
                String password = loginPass.getText().toString().trim();
                if (!TextUtils.isEmpty(email)&& !TextUtils.isEmpty(password)){
                    mAuth.signInWithEmailAndPassword(email,password).addOnCompleteListener(new OnCompleteListener() {
                        @Override
                        public void onComplete(@NonNull Task task) {
                            if (task.isSuccessful()){
                                checkUserExistence();
                            }else {
                                Toast.makeText(LoginActivity.this, "Couldn't login, User not found", Toast.LENGTH_SHORT).show();
                            }                        }                    });
                }else {
                    Toast.makeText(LoginActivity.this, "Complete all fields", Toast.LENGTH_SHORT).show();
                }            }        });    }
    public void checkUserExistence(){
        final String user_id = mAuth.getCurrentUser().getUid();
        mDatabase.addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                if (dataSnapshot.hasChild(user_id)){
                    startActivity(new Intent(LoginActivity.this, MainActivity.class));
                }else {
                    Toast.makeText(LoginActivity.this, "User not registered!", Toast.LENGTH_SHORT).show();
                }            }
            @Override
            public void onCancelled(DatabaseError databaseError) {
            }        });    }}

Sign In User With Email And Password

We are doing basically the same thing we did in the RegisterActivity, only that this time instead of passing the createUserWithEmailAndPassword() method on the FirebaseAuth instance, we are calling the signInUserWithEmailAndPassword() method which also takes in two parameters (email and password). If this task is succesful, we then check if the user is registered (exists) on our database.

The method that checks if the user exists in our database simply gets the users unique id and check if it exists in our database by calling the hasChild() method on the database snapshot and passing in the user_id

if (dataSnapshot.hasChild(user_id)){
                    startActivity(new Intent(LoginActivity.this, MainActivity.class));
                }else {
                    Toast.makeText(LoginActivity.this, "User not registered!", Toast.LENGTH_SHORT).show();
                }

If the user is registered (exists in the database), we launch the homepage else, we create a Toast to let them know they are not registered.

Now that we have authenticated our users, go back to firebase console and change the Database and Storage Read and Write Rules to != null (not null) so that only signed in users can read or write to our database.

We are almost there, at this point we have a functional App. Users can register, login, make a post, and view the post.

SinglePostActivity

One more functionality we'd like to add is to have click events on the cards we used to populate the Recyclerview so that users can click on a particular post and have it open up in another activity where they can view all the post contents and even delete the post if they are the ones that posted it. So we'll go ahead and create another Activity called SinglePostActivity, i'll drop a screenshot of the layout file in the xml visualizer whilst pasting and explaining the code for the java class.

This layout simply has an ImageView to hold the post image, two TextViews to hold the post Title and Description and a delete Button to delete post.

The first thing we'll do will be to go back to our MainActivity class and extract the position of the clicked item. To achieve this we'll need to create a variable that'll store the post_key of every unique post on the recycler view object so open MainActivity.java class, inside the populateViewHolder() method define a String variable post_key to store the key

final String post_key = getRef(position).getKey().toString();

FYI this code already exists in your MainActivity (if you copied the MainActivity.java Class) i shared. Then set an onClickListener on the viewHolder instance inside the populateViewHolder() method and pass in the post_key with an intent so as to open it up in the SinglePostActivity with it's corresponding details.

  viewHolder.mView.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View view) {
                        Intent singleActivity = new Intent(MainActivity.this, SinglePostActivity.class);
                        singleActivity.putExtra("PostID", post_key);
                        startActivity(singleActivity);
                    }                });

So then lets setup the SinglePostActivity class to receive all the data coming in from the "clicked" Post on the recyclerview.

package com.example.ekene.blogzone;
public class SinglePostActivity extends AppCompatActivity {
    private ImageView singelImage;
    private TextView singleTitle, singleDesc;
    String post_key = null;
    private DatabaseReference mDatabase;
    private Button deleteBtn;
    private FirebaseAuth mAuth;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_single_post);
        singelImage = (ImageView)findViewById(R.id.singleImageview);
        singleTitle = (TextView)findViewById(R.id.singleTitle);
        singleDesc = (TextView)findViewById(R.id.singleDesc);
        mDatabase = FirebaseDatabase.getInstance().getReference().child("Blogzone");
        post_key = getIntent().getExtras().getString("PostID");
        deleteBtn = (Button)findViewById(R.id.deleteBtn);
        mAuth = FirebaseAuth.getInstance();
        deleteBtn.setVisibility(View.INVISIBLE);
        deleteBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mDatabase.child(post_key).removeValue();
                Intent mainintent = new Intent(SinglePostActivity.this, MainActivity.class);
                startActivity(mainintent);
            }        });
        mDatabase.child(post_key).addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                String post_title = (String) dataSnapshot.child("title").getValue();
                String post_desc = (String) dataSnapshot.child("desc").getValue();
                String post_image = (String) dataSnapshot.child("imageUrl").getValue();
                String post_uid = (String) dataSnapshot.child("uid").getValue();
                singleTitle.setText(post_title);
                singleDesc.setText(post_desc);
                Picasso.with(SinglePostActivity.this).load(post_image).into(singelImage);
                if (mAuth.getCurrentUser().getUid().equals(post_uid)){
                    deleteBtn.setVisibility(View.VISIBLE);
                }            }
            @Override
            public void onCancelled(DatabaseError databaseError) {
            }        });    }}

So in here what we did was first get the Intent we passed from the MainActivity class and retrieve the information into a String variable (post_key). At this point the post_key variable represents every post on our database.

So we get references to the view objects we created in the layout (activity_single_post.xml) and also get references to our Firebase objects. On the FirebaseDatabase reference, we pass the post_key as a child and add a valueEventListener which will implement the onDataChanged() method where we'll then store the individual values from our datasnapshots into String variables. We'll then set these values on the view objects of the [activity_single_post.xml] file and boom our post will successfully appear in the SinglePostActivity upon click from the recyclerview holder.

Delete Post

Next we hook up the delete button to delete the post from the database within the app. Since the post is associated with it's post_key and user_id, it becomes very simple to delete the post but only when the user_id of the currentUser matches the user_id of the post such that another user cannot delete someone else's post. So first things first, to delete the post we set an onClickListener on the deleteBtn and inside the onClick() method, we call the removeValue() method on the FirebaseAuth instance. then use an intent to move the user back to the MainActivity.

  deleteBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mDatabase.child(post_key).removeValue();
                Intent mainintent = new Intent(SinglePostActivity.this, MainActivity.class);
                startActivity(mainintent);
            }        });

Next we'd like to make sure that only the users who made that post will be able to see this Delete button so inside the SinglePostActivity, you may have noticed that we initially made the button invisible

deleteBtn.setVisibility(View.INVISIBLE);

To make the button visible to the post owner we then do a check to see if the current users unique id matches the post uid which we passed with the intent. Then, if the current users id matches with the post uid, we make the deleteBtn visible.

     if (mAuth.getCurrentUser().getUid().equals(post_uid)){
                    deleteBtn.setVisibility(View.VISIBLE);
                }            }

So we are done with the delete button. Next we simply add a button on the Title bar of the MainActivity to launch the PostActivity. And finally we also implement a sign out button to log out the user and return the App to the LoginActivity.

At this point we have a fully functional App. Users can register, login, make a post, view the post and even delete the post. So let's add the last bit of functionality to complete our app which is to signout the user and also implement a button that launches the post Activity.

To do this we'll create another resource directory called "menu" and inside it we create a menu resource file called "menu_main.xml". In the just created menu file, we'll have the items on the AppBar (the + icon and the drop down for settings and logout)

Then in the MainActivity.java file, we call the onCreateOptionsMenu method and inflate this layout afterwhich we Override the onOptionsItemSelected() method and define the onClick functionalities of the individual button.

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
      if (id == R.id.action_add) {
            startActivity(new Intent(MainActivity.this, PostActivity.class));
        } else (id == R.id.logout){
            mAuth.signOut();
            Intent logouIntent = new Intent(MainActivity.this, RegisterActivity.class);
            logouIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
            startActivity(logouIntent);
        }
        return super.onOptionsItemSelected(item);
    }

SignOut

As can be seen above, to SignOut a user can be very simple with Firebase. Here we just called the signOut() method on the FirebaseAuth instance and used an intent to return the user back to the RegisterActivity. it is just that simple.

Conclusion

And with that we've completed the Blog App. If you completed this project with us then you would have mastered the basics of Firebase CRUD operations and gained a handy skill building a Blog App as well as mastering the listed features of this post. There are many ways to extend this app in building your personal products or working on a different project, you can also apply the knowledge in developing your existing projects and contributing to open source projects.

Source Code

If you need the source code for this project for your personal use, or you have any improvements you want to make, i'm ready to work together with you to make it happen. Fork,Clone or Submit PR Here have fun !!

Peter Ekene

1 post

Peter Ekene is an Android Developer and an Ardent Firebase advocate building real world Applications with Firbase technologies. Organizer of the Android Nigeria Community and a technical writer.