Camera Tutorial With Example In Android Studio [Step by Step]

In Android, Camera is a hardware device that allows capturing pictures and videos in your applications. Follow this tutorial to easily understand how to use a camera in your own Android App.

Camera Tutorial Example In Android Studio

The Android framework provides the facility of working with Camera in two ways:

1.By using existing camera application
2.By using Camera Api

#1 Using Camera By Using Camera Application

We can capture pictures without using the instance of Camera class. Here you will use an intent action type of MediaStore.ACTION_IMAGE_CAPTURE to launch an existing Camera application on your phone. In Android MediaStore is a type of DataBase which stores pictures and videos in android.

Intent cameraIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);

#2 Using Camera By using Camera Api

This class is used for controlling device cameras. It can be used to take pictures when you are building a camera application.
Camera API works in following ways:

1.Camera Manager: This is used to get all the cameras available in the device like front camera back camera each having the camera id.

2.CameraDevice: You can get it from Camera Manager class by its id.

3.CaptureRequest: You can create a capture request from camera device to capture images.

4.CameraCaptureSession: To get capture request’s from Camera Device create a CameraCaptureSession.

5.CameraCaptureSession.CaptureCallback: This is going to provide the Capture session results.

Camera Permission Declarations In Manifest

First, you should declare the Camera requirement in your Manifest file if Camera is compulsory for your application and you don’t want your application to be installed on a device that does not support Camera.

Before you start development on your application you have to make sure that your Manifest has appropriate declarations in it that will allow you to use Camera feature in your Application.

<uses-permission android:name="android.permission.CAMERA"/>

Camera Example in Android Studio By Using Camera Intent and storing the camera Image in Local DataBase

In this Camera in Android example, I will show you how to capture the image from device camera using Intent and store that camera image in the local database and display a toast when an image is stored successfully or when some error occurred while storing an image.

Camera Tutorial Example In Android Studio

Below you can download code, see final output and step by step explanation of example:

Download Camera Example in Android Code From Github

Step 1: Create a new project in Android Studio and name it CameraCodeExample

Step 2:Open res -> layout -> xml or (activity_main.xml) and add following code
Here I have used frame layout to load my fragments.

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:orientation="vertical"
   android:layout_width="fill_parent"
   android:layout_height="fill_parent">
<FrameLayout
       android:id="@+id/frameLayout"
       android:layout_width="match_parent"
       android:layout_height="match_parent"/>
</RelativeLayout>

Step 3: Open src -> package -> MainActivity.java
Here I am loading my default fragment into frame layout in MainActivity:

import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import com.example.cameracodeexample.Fragments.CameraFragment;

public class MainActivity extends AppCompatActivity {

   FrameLayout frameLayout;

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

       loadFragment(new CameraFragment(), false);

   }

   public void loadFragment(Fragment fragment, Boolean bool) {
       FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
       transaction.replace(R.id.frameLayout, fragment);
       if (bool)
           transaction.addToBackStack(null);
       transaction.commit();
   }

}

Step 4:Open res -> layout -> xml or (camera_fragment.xml)
Here I have used two TextViews, on the one you can click an image and save it to DataBase and another is used to view that stored image.

<TextView
   android:id="@+id/text"
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:text="Save Image"
   android:layout_gravity="center"
   android:layout_margin="@dimen/_12sdp"
   android:textSize="@dimen/_18sdp"
   android:textAlignment="center"
   android:background="#EBEBEB"/>
TextView
   <android:id="@+id/text1"
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:text="View Image"
   android:layout_gravity="center"
   android:layout_margin="@dimen/_12sdp"
   android:textSize="@dimen/_18sdp"
   android:textAlignment="center"
   android:background="#EBEBEB"
   android:layout_below="@id/text"/>

Step 5: Now I will explain the methods used In CameraFragment:

Checking Camera Permissions:

For the first we have used the Function requestPermissions(new String[]{Manifest.permission.CAMERA}, MY_CAMERA_PERMISSION_CODE);
We will check whether the user has given permissions or not if not then first we will ask the user for permissions if a user is using Android version above Android Marshmallow(API 23) because From Android Marshmallow(API 23) and above by default all dangerous permission disabled. When the App opens for the first time after installation then you have to grant permissions. But if the android version is below Marshmallow then this function won’t be called.

requestPermissions(newString[]{Manifest.permission.CAMERA},MY_CAMERA_PERMISSION_CODE)

startActivityForResult(cameraIntent, CAMERA_REQUEST) Method:

Now we will call Function startActivityForResult(cameraIntent, CAMERA_REQUEST);
When you start an activity for the result, it request Camera to take a photo then return it to your calling activity, you pass it a unique integer value or anything you have not used already in that class. The requestCode helps you to identify from which Intent you came back.
So, that CAMERA_REQUEST could be any value you define in your class like this:

private static final int CAMERA_REQUEST = 1888
 
@Override
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
   if (requestCode == CAMERA_REQUEST && resultCode == Activity.RESULT_OK)
   {
      theImage = (Bitmap) data.getExtras().get("data");
     photo=getEncodedString(theImage);
           setDataToDataBase();
   }
}

getEncodedString(Bitmap bitmap) Method:

Since now I am able to click an image from the camera but that image is in the form of Bitmap but I want to store it as a string in the database, so I am going to encode it using ByteArrayOutputStream Class. This Class holds a copy of data and forwards it to multiple streams.

private String getEncodedString(Bitmap bitmap){

   ByteArrayOutputStream os = new ByteArrayOutputStream();

          bitmap.compress(Bitmap.CompressFormat.JPEG,100, os);

  /* or use below if you want 32 bit images

   bitmap.compress(Bitmap.CompressFormat.PNG, 100, os);*/

   byte[] imageArr = os.toByteArray();

   return Base64.encodeToString(imageArr, Base64.URL_SAFE);

}

setDataToDataBase() Method:

Now I am storing data(Image) to a database(here I have used SQLite). Here the ContentValue class helps to put information inside an object in the form of Key-Value pairs for columns. To Insert or Update your WritableDatabase The object can then be passed to an instance of the SQLiteDatabase class.

private void setDataToDataBase() {
       db = databaseHandler.getWritableDatabase();
       ContentValues cv = new ContentValues();
       cv.put(databaseHandler.KEY_IMG_URL,photo);

       long id = db.insert(databaseHandler.TABLE_NAME, null, cv);
       if (id < 0) {
           Toast.makeText(getContext(), "Something went wrong. Please try again later...", Toast.LENGTH_LONG).show();
       } else {
           Toast.makeText(getContext(), "Add successful", Toast.LENGTH_LONG).show();
       }
   }

Step 6 :Open src -> package -> CameraFragment.java

In this step we open CameraFragment and add the code to initiate the camera here we perform a set of operation to click a picture from camera and store that in database

import android.Manifest;
import android.app.Activity;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.Bitmap;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
import android.support.v4.app.Fragment;
import android.util.Base64;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
import com.example.cameracodeexample.MainActivity;
import com.example.cameracodeexample.R;
import com.example.cameracodeexample.utils.DataBaseHandler;
import java.io.ByteArrayOutputStream;

public class CameraFragment extends Fragment {
   private static final int CAMERA_REQUEST = 1888;
   TextView text,text1;
   private static final int MY_CAMERA_PERMISSION_CODE = 100;
   //Bitmap photo;
   String photo;
   DataBaseHandler databaseHandler;
   private SQLiteDatabase db;
   Bitmap theImage;
   @Nullable
   @Override
   public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
      View view = inflater.inflate(R.layout.camera_fragment,container,false);


      // imageView =view. findViewById(R.id.imageView1);
       text = view.findViewById(R.id.text);
       text1 = view.findViewById(R.id.text1);
       databaseHandler = new DataBaseHandler(getContext());
       text.setOnClickListener(new View.OnClickListener() {
           @RequiresApi(api = Build.VERSION_CODES.M)
           @Override
           public void onClick(View v) {
               if (getActivity().checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED)
               {
                   requestPermissions(new String[]{Manifest.permission.CAMERA}, MY_CAMERA_PERMISSION_CODE);
               }
               else
               {
                   Intent cameraIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
                   startActivityForResult(cameraIntent, CAMERA_REQUEST);
               }
           }
       });

       text1.setOnClickListener(new View.OnClickListener() {
           @Override
           public void onClick(View v) {
               ((MainActivity) getActivity()).loadFragment(new LocalFragment(), true);
           }
       });
      return view;
   }

   private void setDataToDataBase() {
       db = databaseHandler.getWritableDatabase();
       ContentValues cv = new ContentValues();
       cv.put(databaseHandler.KEY_IMG_URL,photo);

       long id = db.insert(databaseHandler.TABLE_NAME, null, cv);
       if (id < 0) {
           Toast.makeText(getContext(), "Something went wrong. Please try again later...", Toast.LENGTH_LONG).show();
       } else {
           Toast.makeText(getContext(), "Add successful", Toast.LENGTH_LONG).show();
       }
   }

   /**
    * Reuqesting for premissons
    * @param requestCode
    * @param permissions
    * @param grantResults
    */

   @Override
   public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
       super.onRequestPermissionsResult(requestCode, permissions, grantResults);
       if (requestCode == MY_CAMERA_PERMISSION_CODE)
       {
           if (grantResults[0] == PackageManager.PERMISSION_GRANTED)
           {
               Toast.makeText(getActivity(), "camera permission granted", Toast.LENGTH_LONG).show();
               Intent cameraIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
               startActivityForResult(cameraIntent, CAMERA_REQUEST);
           }
           else
           {
               Toast.makeText(getActivity(), "camera permission denied", Toast.LENGTH_LONG).show();
           }
       }
   }

   /**
    * Start an activity for result
    * @param requestCode
    * @param resultCode
    * @param data
    */

   @Override
   public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
       if (requestCode == CAMERA_REQUEST && resultCode == Activity.RESULT_OK)
       {
          theImage = (Bitmap) data.getExtras().get("data");
          photo=getEncodedString(theImage);
          setDataToDataBase();
       }
   }


   private String getEncodedString(Bitmap bitmap){

              ByteArrayOutputStream os = new ByteArrayOutputStream();

              bitmap.compress(Bitmap.CompressFormat.JPEG,100, os);

      /* or use below if you want 32 bit images

       bitmap.compress(Bitmap.CompressFormat.PNG, (0–100 compression), os);*/
       byte[] imageArr = os.toByteArray();
       return Base64.encodeToString(imageArr, Base64.URL_SAFE);

   }
}

Step 7: Open src -> package -> DataBaseHandler Class.java

Here we define Schema i.e. DataBase name, version, table names, and column names. As this class extends SQLiteOpenHelper it says to implement two methods
1.onCreate() -this method is called by the framework, if the database has not been created yet but only accessed.
2.onUpgrade() – This method is called if you want to add something in database then you have to upgrade your database version. This method helps to update the current database schema or to drop the table or recreate it in via on Create method.
Method public void deleteEntry(long row)-This Method is used to delete an entry in DataBase based on uid.

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;


public class DataBaseHandler extends SQLiteOpenHelper {
   public Context context;
   public static final String DATABASE_NAME = "dataManager";

   public static final int DATABASE_VERSION = 1;
   public static final String TABLE_NAME = "data";
   public static final String KEY_ID = "id";
   public static final String KEY_IMG_URL = "ImgFavourite";

   public DataBaseHandler(Context context) {
       super(context, DATABASE_NAME, null, DATABASE_VERSION);
       this.context = context;
       //Toast.makeText(context, "Constructor called", Toast.LENGTH_LONG).show();
   }

   public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + "(" + KEY_ID +
           " INTEGER PRIMARY KEY AUTOINCREMENT," + KEY_IMG_URL + " TEXT" + ")";
   public static final String DROP_TABLE = "DROP TABLE IF EXISTS " + TABLE_NAME + "";

   @Override
   public void onCreate(SQLiteDatabase db) {
       db.execSQL(CREATE_TABLE);
   }

   @Override
   public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
       db.execSQL(DROP_TABLE);
       onCreate(db);
   }

   public void deleteEntry(long row) {
       SQLiteDatabase sqLiteDatabase = getWritableDatabase();
       sqLiteDatabase.delete(TABLE_NAME, KEY_ID + "=" + row, null);
   }

}

Step 8:Open res -> layout -> xml or (local_fragment.xml)
In this fragment, I have used Recycler view to show the data stored In the DataBase.

Local_fragment.xml

<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="match_parent"
   android:orientation="vertical"
   android:background="@color/color_white"/>
<android.support.v7.widget.RecyclerView
       android:id="@+id/recyclerview"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:layout_marginTop="@dimen/_5sdp"
       android:background="@color/color_white"
       android:padding="3dp"
       android:scrollbars="none"/>
</LinearLayout>

Step 8:Open src -> package -> LocalResponse.java

This is the model class that populates the data in adapter here String represents the encoded String(image) that was saved in the database and int represents the respective data that we will be populating.

public class LocalResponse {
   String image;
   int uid;

   public int getUid() {
       return uid;
   }

   public void setUid(int uid) {
       this.uid = uid;
   }

   public String getImage() {
       return image;
   }

   public void setImage(String image) {
       this.image = image;
   }

   public LocalResponse(String image, int uid) {
       this.image = image;
       this.uid = uid;
   }
}

Step 9:Open src -> package -> LocalFragment.java

In this fragment, I am getting the data out of the database using cursor. The cursor is an interface that contains the result set of Query made against DataBase in android. When we retrieve some data then the database will first create the Cursor object the pointer of this object id is pointing to 0th position. So with the help of Cursor, we are getting data out of database Since the cursor object points to the 0th index we will check it for till cursor is moving to next cursor.moveToNext() to get the next data out of DataBase after 0th position.

import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
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.example.cameracodeexample.utils.DataBaseHandler;
import com.example.cameracodeexample.utils.LocalDataBaseAdapter;
import com.example.cameracodeexample.utils.LocalResponse;
import java.util.ArrayList;

public class LocalFragment extends Fragment {
   RecyclerView recyclerView;
   private DataBaseHandler myDatabase;
   private SQLiteDatabase db;
   private ArrayList singleRowArrayList;
   private LocalResponse singleRow;
   String image;
   int uid;
   Cursor cursor;

   @Nullable
   @Override
   public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
       View view = inflater.inflate(R.layout.local_fragment,container,false);
       recyclerView = view.findViewById(R.id.recyclerview);
       myDatabase = new DataBaseHandler(getContext());
       db = myDatabase.getWritableDatabase();
       setData();
       return view;
   }

   private void setData() {
       db = myDatabase.getWritableDatabase();
       RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(getContext());
       recyclerView.setLayoutManager(layoutManager);
       singleRowArrayList = new ArrayList<>();
       String[] columns = {DataBaseHandler.KEY_ID, DataBaseHandler.KEY_IMG_URL};
       cursor = db.query(DataBaseHandler.TABLE_NAME, columns, null, null, null, null, null);
       while (cursor.moveToNext()) {

           int index1 = cursor.getColumnIndex(DataBaseHandler.KEY_ID);
           int index2 = cursor.getColumnIndex(DataBaseHandler.KEY_IMG_URL);

           uid = cursor.getInt(index1);
           image = cursor.getString(index2);

           singleRow = new LocalResponse(image,uid);
           singleRowArrayList.add(singleRow);
       }
       if (singleRowArrayList.size()==0){
           //empty.setVisibility(View.VISIBLE);
           recyclerView.setVisibility(View.GONE);
       }else {
           LocalDataBaseAdapter localDataBaseResponse = new LocalDataBaseAdapter(getContext(), singleRowArrayList, db, myDatabase);
           recyclerView.setAdapter(localDataBaseResponse);
       }


   }
}

Step 10: Open res -> layout -> xml or (Local_database_items.xml)

This is how the Local DataBase RecyclerView Items is going to look like here i have used a CardView in which i have used ImageView to show an image and we have also Used one more ImageView on click of which your image will be deleted from DataBase.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   xmlns:app="http://schemas.android.com/apk/res-auto"
   android:orientation="vertical">
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
       android:id="@+id/cardView"
       android:layout_width="match_parent"
       android:layout_height="120dp"
       android:layout_margin="5dp"
       android:background="?attr/selectableItemBackground"
       android:clickable="true"
       android:elevation="5dp"
       android:focusable="true"
       android:foreground="?android:attr/selectableItemBackground"
       app:cardCornerRadius="5dp"
       android:visibility="visible">
<RelativeLayout
           android:layout_width="match_parent"
           android:layout_height="120dp"
           android:background="@color/color_white">
<ImageView
               android:id="@+id/newsImage"
               android:layout_width="match_parent"
               android:layout_height="match_parent"
               android:scaleType="centerCrop"/>
<ImageView
               android:id="@+id/delete"
               android:layout_width="wrap_content"
               android:layout_height="@dimen/_35sdp"
               android:layout_alignParentRight="true"
               android:padding="@dimen/_4sdp"
               android:src="@drawable/delete_icon"
               android:tint="#C7C7C7"/>

</RelativeLayout>
</android.support.v7.widget.CardView>
</LinearLayout>

Step 11: Open src -> package -> LocalDataBaseAdapter.java

Here I have used two methods one is getBitmapFromEncodedString(String encodedString) used to decode Base64 class into byte array and then we use this byte array to convert Bitmap decodedByte. Second is deletedata(final int position, final ArrayList singleRowArrayList) this method is used to delete data from a particular position in the DataBase. We can delete selected rows from the database by using the WHERE clause with DELETE query, otherwise, all the records would be deleted.

import android.content.Context;
import android.content.DialogInterface;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.support.annotation.NonNull;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.RecyclerView;
import android.util.Base64;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import com.squareup.picasso.Picasso;

import java.util.ArrayList;

public class LocalDataBaseAdapter extends RecyclerView.Adapter {
   Context context;
   ArrayList singleRowArrayList;
   SQLiteDatabase db;
   DataBaseHandler myDatabase;
   public LocalDataBaseAdapter(Context context, ArrayList singleRowArrayList, SQLiteDatabase db, DataBaseHandler myDatabase) {
       this.context = context;
       this.singleRowArrayList = singleRowArrayList;
       this.db = db;
       this.myDatabase = myDatabase;
   }

   @NonNull
   @Override
   public MyViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
       View view = LayoutInflater.from(context).inflate(R.layout.local_database_items, null);
       MyViewHolder myViewHolder = new MyViewHolder(view);
       return myViewHolder;
   }

   @Override
   public void onBindViewHolder(@NonNull MyViewHolder myViewHolder, final int i) {
       myViewHolder.newsImage.setImageBitmap(getBitmapFromEncodedString(singleRowArrayList.get(i).image));
       myViewHolder.delete.setOnClickListener(new View.OnClickListener() {
           @Override
           public void onClick(View v) {
               deletedata(i,singleRowArrayList);
           }
       });
   }

   @Override
   public int getItemCount() {
       return singleRowArrayList.size();
   }

   class MyViewHolder extends RecyclerView.ViewHolder {
       ImageView newsImage,delete;

       public MyViewHolder(@NonNull View itemView) {
           super(itemView);
           newsImage = (ImageView) itemView.findViewById(R.id.newsImage);
           delete = (ImageView) itemView.findViewById(R.id.delete);
       }
   }

   public void deletedata(final int position, final ArrayList singleRowArrayList){
       new AlertDialog.Builder(context)
               .setIcon(R.drawable.defaultimage)
               .setTitle("Delete result")
               .setMessage("Are you sure you want delete this result?")
               .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
                   @Override
                   public void onClick(DialogInterface dialog, int which) {
                       /* This is where deletions should be handled */
                       myDatabase.deleteEntry(singleRowArrayList.get(position).getUid());
                       singleRowArrayList.remove(position);
                       notifyItemRemoved(position);
                       notifyDataSetChanged();
                       myDatabase.close();
                       //((MainActivity) context).loadFragment(new LocalFragment(), true);

                   }

               })
               .setNegativeButton("No", null)
               .show();
   }

   private Bitmap getBitmapFromEncodedString(String encodedString){

       byte[] arr = Base64.decode(encodedString, Base64.URL_SAFE);

       Bitmap img = BitmapFactory.decodeByteArray(arr, 0, arr.length);

       return img;

   }
}

Output:

Now run the App and you will be able to click the image with your device by clicking on SaveImage and you will able to see them later by clicking on ViewImage another you can also delete the images that you don’t want to store.

Camera Tutorial Example In Android Studio

DOWNLOAD THIS FREE eBook!

This free eBook will help you master the learning of Android App Development in Android Studio!

One thought on “Camera Tutorial With Example In Android Studio [Step by Step]”

  1. Hi, do you have a repository for the camera app? I really need the user interface code for my school project. I hope you can reply as soon as possible. Thank you

Leave a Reply

Your email address will not be published. Required fields are marked *

Download Free - Master Android App Development Sidebar

DOWNLOAD THIS FREE eBook!

This free eBook will help you master the learning of Android App Development in Android Studio!
close-link

Android Developer Facebook Group Free

Premium Project Source Code:




DOWNLOAD THIS FREE eBook!

This free eBook will help you master the learning of Android App Development in Android Studio!
close-link

With a very poor revenue from selling source code files or using Google AdSense, we need your help to survive this website. Please contribute any amount you can afford
Pay
close-link