In this tutorial we will learn using Firebase in Android app using Android Studio. We will:
✓Create Android Studio Project
✓Create Firebase Project and connect our Android Studio Project to that Firebase Project
✓Store Images to FirebaseStorage manually
✓Add data in FirebaseDatabase
✓Retrieve these data(Title, Image, Description) in RecyclerView. To retrieve Images we will use Picasso library.
✓Implement SearchView
✓Handle RecyclerView Item Clicks, When an item is clicked it will be opened in new Activity.
✓Sort Firebase Data displayed in RecyclerView.
✓Save Image, Share Image & Text, Set Image as Wallpaper
Step 1: Create a new project OR Open your project
Step 2: Choose Empty Activity & name it as “PostsListActivity”
Step 3: Create menu.xml under res>menu folder, to search item.
Step 4: In menu bar click Tools > Firebase. Firebase Assistent will open at right side
Step 5: Scroll and click Realtime Database. Click Save & Retrieve data.
1: Connect your app to Firebase
Click Connect to Firebase button
Connect to Firebase window will open.
Select Create new Firebase project and enter project name FirebaseProject.
Choose country e.g. India. Click Connect to Firebase button
2: Add the Realtime database to your app
Click Add the Realtime database to your app button
Click Accept Changes button
Step 6: Open Firebase
Link: https://console.firebase.google.com
Step 6.1 Click Storage from menuClick
Files tabClick Upload file and upload some images
Click Rules tabDefine Rules as follows:service firebase.storage { match /b/{bucket}/o { match /{allPaths=**} { allow read, write: if true; } }}
Step 6.2 Click Database from menuClick Data tab
Note: To get URL of image click the uploaded image, under File location click Download Url, it will be copied
Add data as follows in Data tab of Database.
Step 7: build.gradle(Module app):
apply plugin: 'com.android.application'
android {
compileSdkVersion 27
defaultConfig {
applicationId "com.jigopost.firebaseproject"
minSdkVersion 16
targetSdkVersion 27
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:27.1.1'
//card view
implementation 'com.android.support:cardview-v7:27.1.1'
//recyclerView
implementation 'com.android.support:recyclerview-v7:27.1.1'
//picasso library to retrive images
implementation 'com.squareup.picasso:picasso:2.71828'
implementation 'com.android.support.constraint:constraint-layout:1.1.0'
implementation 'com.google.firebase:firebase-database:11.8.0'
implementation 'com.firebaseui:firebase-ui-database:0.4.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
apply plugin: 'com.google.gms.google-services'
Step 8: Create class Model.java
package com.jigopost.firebaseproject;
public class Model {
String title, image, description;
//constructor
public Model(){}
//getter and setters press Alt+Insert
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getImage() {
return image;
}
public void setImage(String image) {
this.image = image;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
Step 9: Create class ViewHolder.java
package com.jigopost.firebaseproject;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import com.squareup.picasso.Picasso;
public class ViewHolder extends RecyclerView.ViewHolder {
View mView;
public ViewHolder(View itemView) {
super(itemView);
mView = itemView;
//item click
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mClickListener.onItemClick(view, getAdapterPosition());
}
});
//item long click
itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
mClickListener.onItemLongClick(view, getAdapterPosition());
return true;
}
});
}
//set details to recycler view row
public void setDetails(Context ctx, String title, String description, String image){
//Views
TextView mTitleTv = mView.findViewById(R.id.rTitleTv);
TextView mDetailTv = mView.findViewById(R.id.rDescriptionTv);
ImageView mImageIv = mView.findViewById(R.id.rImageView);
//set data to views
mTitleTv.setText(title);
mDetailTv.setText(description);
Picasso.get().load(image).into(mImageIv);
}
private ViewHolder.ClickListener mClickListener;
//interface to send callbacks
public interface ClickListener{
void onItemClick(View view, int position);
void onItemLongClick(View view, int position);
}
public void setOnClickListener(ViewHolder.ClickListener clickListener){
mClickListener = clickListener;
}
}
Step 10: menu.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_search"
android:icon="@drawable/ic_action_search"
android:title="Search"
app:actionViewClass="android.support.v7.widget.SearchView"
app:showAsAction="always" />
<item
android:id="@+id/action_sort"
android:title="Sort"
app:showAsAction="never" />
</menu>.
Step 11: Add new resource layout in res folder and name it as row.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
app:cardBackgroundColor="#fff"
app:cardCornerRadius="3dp"
app:cardElevation="3dp"
app:contentPadding="5dp"
app:cardUseCompatPadding="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/rTitleTv"
android:text="Title"
android:textColor="#000"
android:textSize="22sp"
android:textStyle="bold"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<ImageView
android:id="@+id/rImageView"
android:layout_width="match_parent"
android:layout_height="200dp"
android:scaleType="centerCrop"
android:adjustViewBounds="true"
android:background="@drawable/loadingimage"/>
<TextView
android:id="@+id/rDescriptionTv"
android:text="This will be the description of the post"
android:textSize="20sp"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</android.support.v7.widget.CardView>
Step 12: activity_posts_list.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".PostsListActivity"
android:orientation="vertical">
<!--RecyclerView-->
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
Step 13: PostsListActivity.java
package com.jigopost.firebaseproject;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.support.v4.view.MenuItemCompat;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.SearchView;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import com.firebase.ui.database.FirebaseRecyclerAdapter;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
import com.google.firebase.database.Query;
import java.io.ByteArrayOutputStream;
public class PostsListActivity extends AppCompatActivity {
LinearLayoutManager mLayoutManager; //for sorting
SharedPreferences mSharedPref; //for saving sort settings
RecyclerView mRecyclerView;
FirebaseDatabase mFirebaseDatabase;
DatabaseReference mRef;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_posts_list);
//Actionbar
ActionBar actionBar = getSupportActionBar();
//set title
mSharedPref = getSharedPreferences("SortSettings", MODE_PRIVATE);
String mSorting = mSharedPref.getString("Sort", "newest"); //where if no settingsis selected newest will be default
if (mSorting.equals("newest")) {
mLayoutManager = new LinearLayoutManager(this);
//this will load the items from bottom means newest first
mLayoutManager.setReverseLayout(true);
mLayoutManager.setStackFromEnd(true);
} else if (mSorting.equals("oldest")) {
mLayoutManager = new LinearLayoutManager(this);
//this will load the items from bottom means oldest first
mLayoutManager.setReverseLayout(false);
mLayoutManager.setStackFromEnd(false);
}
//RecyclerView
mRecyclerView = findViewById(R.id.recyclerView);
mRecyclerView.setHasFixedSize(true);
//set layout as LinearLayout
mRecyclerView.setLayoutManager(mLayoutManager);
//send Query to FirebaseDatabase
mFirebaseDatabase = FirebaseDatabase.getInstance();
mRef = mFirebaseDatabase.getReference("Data");
}
//search data
private void firebaseSearch(String searchText) {
//convert string entered in SearchView to lowercase
String query = searchText.toLowerCase();
Query firebaseSearchQuery = mRef.orderByChild("search").startAt(query).endAt(query + "\uf8ff");
FirebaseRecyclerAdapter<Model, ViewHolder> firebaseRecyclerAdapter =
new FirebaseRecyclerAdapter<Model, ViewHolder>(
Model.class,
R.layout.row,
ViewHolder.class,
firebaseSearchQuery
) {
@Override
protected void populateViewHolder(ViewHolder viewHolder, Model model, int position) {
viewHolder.setDetails(getApplicationContext(), model.getTitle(), model.getDescription(), model.getImage());
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
ViewHolder viewHolder = super.onCreateViewHolder(parent, viewType);
viewHolder.setOnClickListener(new ViewHolder.ClickListener() {
@Override
public void onItemClick(View view, int position) {
//Views
TextView mTitleTv = view.findViewById(R.id.rTitleTv);
TextView mDescTv = view.findViewById(R.id.rDescriptionTv);
ImageView mImageView = view.findViewById(R.id.rImageView);
//get data from views
String mTitle = mTitleTv.getText().toString();
String mDesc = mDescTv.getText().toString();
Drawable mDrawable = mImageView.getDrawable();
Bitmap mBitmap = ((BitmapDrawable) mDrawable).getBitmap();
//pass this data to new activity
Intent intent = new Intent(view.getContext(), PostDetailActivity.class);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
mBitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
byte[] bytes = stream.toByteArray();
intent.putExtra("image", bytes); //put bitmap image as array of bytes
intent.putExtra("title", mTitle); // put title
intent.putExtra("description", mDesc); //put description
startActivity(intent); //start activity
}
@Override
public void onItemLongClick(View view, int position) {
//TODO do your own implementaion on long item click
}
});
return viewHolder;
}
};
//set adapter to recyclerview
mRecyclerView.setAdapter(firebaseRecyclerAdapter);
}
//load data into recycler view onStart
@Override
protected void onStart() {
super.onStart();
FirebaseRecyclerAdapter<Model, ViewHolder> firebaseRecyclerAdapter =
new FirebaseRecyclerAdapter<Model, ViewHolder>(
Model.class,
R.layout.row,
ViewHolder.class,
mRef
) {
@Override
protected void populateViewHolder(ViewHolder viewHolder, Model model, int position) {
viewHolder.setDetails(getApplicationContext(), model.getTitle(), model.getDescription(), model.getImage());
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
ViewHolder viewHolder = super.onCreateViewHolder(parent, viewType);
viewHolder.setOnClickListener(new ViewHolder.ClickListener() {
@Override
public void onItemClick(View view, int position) {
//Views
TextView mTitleTv = view.findViewById(R.id.rTitleTv);
TextView mDescTv = view.findViewById(R.id.rDescriptionTv);
ImageView mImageView = view.findViewById(R.id.rImageView);
//get data from views
String mTitle = mTitleTv.getText().toString();
String mDesc = mDescTv.getText().toString();
Drawable mDrawable = mImageView.getDrawable();
Bitmap mBitmap = ((BitmapDrawable) mDrawable).getBitmap();
//pass this data to new activity
Intent intent = new Intent(view.getContext(), PostDetailActivity.class);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
mBitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
byte[] bytes = stream.toByteArray();
intent.putExtra("image", bytes); //put bitmap image as array of bytes
intent.putExtra("title", mTitle); // put title
intent.putExtra("description", mDesc); //put description
startActivity(intent); //start activity
}
@Override
public void onItemLongClick(View view, int position) {
//TODO do your own implementaion on long item click
}
});
return viewHolder;
}
};
//set adapter to recyclerview
mRecyclerView.setAdapter(firebaseRecyclerAdapter);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
//inflate the menu; this adds items to the action bar if it present
getMenuInflater().inflate(R.menu.menu, menu);
MenuItem item = menu.findItem(R.id.action_search);
SearchView searchView = (SearchView) MenuItemCompat.getActionView(item);
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
firebaseSearch(query);
return false;
}
@Override
public boolean onQueryTextChange(String newText) {
//Filter as you type
firebaseSearch(newText);
return false;
}
});
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
//handle other action bar item clicks here
if (id == R.id.action_sort) {
//display alert dialog to choose sorting
showSortDialog();
return true;
}
return super.onOptionsItemSelected(item);
}
private void showSortDialog() {
//options to display in dialog
String[] sortOptions = {" Newest", " Oldest"};
//create alert dialog
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Sort by") //set title
.setIcon(R.drawable.ic_action_sort) //set icon
.setItems(sortOptions, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// The 'which' argument contains the index position of the selected item
// 0 means "Newest" and 1 means "oldest"
if (which == 0) {
//sort by newest
//Edit our shared preferences
SharedPreferences.Editor editor = mSharedPref.edit();
editor.putString("Sort", "newest"); //where 'Sort' is key & 'newest' is value
editor.apply(); // apply/save the value in our shared preferences
recreate(); //restart activity to take effect
} else if (which == 1) {
{
//sort by oldest
//Edit our shared preferences
SharedPreferences.Editor editor = mSharedPref.edit();
editor.putString("Sort", "oldest"); //where 'Sort' is key & 'oldest' is value
editor.apply(); // apply/save the value in our shared preferences
recreate(); //restart activity to take effect
}
}
}
});
builder.show();
}
}
Step 14: Create new Activity(Empty Activity) and name it as PostDetailActivity.java
package com.jigopost.firebaseproject;
import android.app.WallpaperManager;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.support.annotation.NonNull;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.Manifest;
import android.widget.Toast;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Locale;
public class PostDetailActivity extends AppCompatActivity {
TextView mTitleTv, mDetailTv;
ImageView mImageIv;
Bitmap bitmap;
Button mSaveBtn, mShareBtn, mWallBtn;
private static final int WRITE_EXTERNAL_STORAGE_CODE = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_post_detail);
//Action bar
ActionBar actionBar = getSupportActionBar();
//Actionbar title
actionBar.setTitle("Post Detail");
//set back button in action bar
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setDisplayShowHomeEnabled(true);
//initialize views
mTitleTv = findViewById(R.id.titleTv);
mDetailTv = findViewById(R.id.descriptionTv);
mImageIv = findViewById(R.id.imageView);
mSaveBtn = findViewById(R.id.saveBtn);
mShareBtn = findViewById(R.id.shareBtn);
mWallBtn = findViewById(R.id.wallBtn);
//get data from intent
byte[] bytes = getIntent().getByteArrayExtra("image");
String title = getIntent().getStringExtra("title");
String desc = getIntent().getStringExtra("description");
Bitmap bmp = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
//set data to views
mTitleTv.setText(title);
mDetailTv.setText(desc);
mImageIv.setImageBitmap(bmp);
//get image from imageview as bitmap
bitmap = ((BitmapDrawable)mImageIv.getDrawable()).getBitmap();
//save btn click handle
mSaveBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//if os is >= marshmallow we need runtime permission to save image
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) ==
PackageManager.PERMISSION_DENIED){
String[] permission = {Manifest.permission.WRITE_EXTERNAL_STORAGE};
//show popup to grant permission
requestPermissions(permission, WRITE_EXTERNAL_STORAGE_CODE);
}
else {
//permission already granted, save image
saveImage();
}
}
else {
//System os is < marshmallow, save image
saveImage();
}
}
});
//share btn click handle
mShareBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
shareImage();
}
});
//set wallpaper btn click handle
mWallBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
setImgWallpaper();
}
});
}
private void setImgWallpaper() {
WallpaperManager myWallManager = WallpaperManager.getInstance(getApplicationContext());
try {
myWallManager.setBitmap(bitmap);
Toast.makeText(this, "Wallpaper set...", Toast.LENGTH_SHORT).show();
}
catch (Exception e){
Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT).show();
}
}
private void shareImage() {
try {
//get title and description and save in string s
String s = mTitleTv.getText().toString() + "\n" + mDetailTv.getText().toString();
File file = new File(getExternalCacheDir(), "sample.png");
FileOutputStream fOut = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, fOut);
fOut.flush();
fOut.close();
file.setReadable(true,false);
//intent to share image and text
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(Intent.EXTRA_TEXT, s); // put the text
intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(file));
intent.setType("image/png");
startActivity(Intent.createChooser(intent, "Share via"));
}
catch (Exception e){
Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT).show();
}
}
private void saveImage() {
//time stamp, for image name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss",
Locale.getDefault()).format(System.currentTimeMillis());
//path to external storage
File path = Environment.getExternalStorageDirectory();
//create folder named "Firebase"
File dir = new File(path+"/Firebase/");
dir.mkdirs();
//image name
String imageName = timeStamp + ".PNG";
File file = new File(dir, imageName);
OutputStream out;
try {
out = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
out.flush();
out.close();
Toast.makeText(this, imageName+" saved to"+ dir, Toast.LENGTH_SHORT).show();
}
catch (Exception e){
//failed saving image
Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT).show();
}
}
//handle onBackPressed(go to previous activity)
@Override
public boolean onSupportNavigateUp() {
onBackPressed();
return true;
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode){
case WRITE_EXTERNAL_STORAGE_CODE:{
//if request code is cancelled the result arrays are empty
if (grantResults.length > 0 && grantResults[0] ==
PackageManager.PERMISSION_GRANTED){
//permission is granted, save image
saveImage();
}
else {
//permission denied
Toast.makeText(this, "enable permission to save image", Toast.LENGTH_SHORT).show();
}
}
}
}
}
Step 15: activity_post_detail.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView 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="wrap_content"
app:cardBackgroundColor="#fff"
app:cardCornerRadius="3dp"
app:cardElevation="3dp"
app:cardUseCompatPadding="true"
app:contentPadding="5dp"
tools:context=".PostDetailActivity">
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/titleTv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Title"
android:textColor="#000"
android:textSize="22sp"
android:textStyle="bold" />
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:background="@drawable/loadingimage"
android:scaleType="fitXY" />
<TextView
android:id="@+id/descriptionTv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="This will be the description of the post"
android:textSize="20sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/saveBtn"
android:layout_weight="1"
android:text="Save"
android:drawableLeft="@drawable/ic_action_save"
style="@style/Base.Widget.AppCompat.Button.Colored"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:id="@+id/shareBtn"
android:layout_weight="1"
android:text="Share"
android:drawableLeft="@drawable/ic_action_share"
style="@style/Base.Widget.AppCompat.Button.Colored"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:id="@+id/wallBtn"
android:layout_weight="1"
android:text="Wall"
android:drawableLeft="@drawable/ic_action_wall"
style="@style/Base.Widget.AppCompat.Button.Colored"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout>
</ScrollView>
</android.support.v7.widget.CardView>
Step 16: AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.jigopost.firebaseproject">
<!-- add internet permission -->
<uses-permission android:name="android.permission.INTERNET" />
<!--External storage permission-->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<!--Wallpaper permission-->
<uses-permission android:name="android.permission.SET_WALLPAPER"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".PostsListActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".PostDetailActivity"></activity>
</application>
</manifest>
Step 17: Run Project
Output