Android & Kotlin

Getting images from Camera & Gallery using MVP

Pinterest LinkedIn Tumblr

My previous tutorials explains how to capture image Camera & Gallery using FileProvider. Most people comment to give this solution in MVP. So In this article, I am going to explain how to getting image from Gallery/Camera using FileProvider in MVP (Modal View Presenter) design pattern. Using this tutorial we prepare a sample app that captures image from Camera and gets the image from gallery using MVP.

Prerequisite

In this article, Getting images from Camera & Gallery using MVP, You need to have knowledge camera and FileProvider module. I recommend you go through my previous tutorials that give you an idea about camera integration into your apps using standard design patterns. Let’s start getting image Camera & Gallery tutorial

Create a New Project

Simply, Open Android Studio , Choose a Create a New Project – > Fill Project Details -> Select Min SDK Version – > Select BasicActivity template -> Finish

Open string.xml from res=>Value and add below String Values

<resources>
    <string name="app_name">ImagePicker</string>
    <string name="action_settings">Settings</string>
    <string name="error_message_no_more_space">
        Your storage is unavailable.</string>
    <string name="error_message_insufficient_space">
        You have few space available, please make sure your have more than 5MB on disk to
        take pictures!</string>
    <string name="ok">Ok</string>
    <string name="error_message">Error occurred!</string>
    <string name="message_need_permission">Need Permissions</string>
    <string name="message_grant_permission">This app needs permission to use this feature. You can grant them in app settings.</string>
    <string name="label_setting">GOTO SETTINGS</string>
    <string name="cancel">Cancel</string>
    <string name="take_photo">Take Photo</string>
    <string name="choose_gallery">Choose from Gallery</string>
    <string name="url_androidwave">https://androidwave.com/</string>
    <string name="url_androidwave_android">https://androidwave.com/category/android/</string>
</resources>

Create a new package folder names picker is picker for separating other’s files.

Simple right-click on src folder and choose package furthermore put name is the picker

Create MVP Contract

As you know are following MVP design pattern in this article. To create an MVP Contract for declare View and Presenter. Create a new class inside src => picker => with ImageContract name.

package com.androidwave.imagepicker.picker;

import android.net.Uri;

import java.io.File;

/**
 * Created on : Jan 06, 2019
 * Author     : AndroidWave
 * Website    : https://androidwave.com/
 */
public class ImageContract {
    public interface View {

        boolean checkPermission();

        void showPermissionDialog();

        File getFilePath();

        void openSettings();

        void startCamera(File file);

        void chooseGallery();

        void showNoSpaceDialog();

        int availableDisk();

        File newFile();

        void showErrorDialog();

        void displayImagePreview(String mFilePath);

        void displayImagePreview(Uri mFileUri);

        String getRealPathFromUri(Uri contentUri);
    }

    interface Presenter {

        void cameraClick();

        void ChooseGalleryClick();

        void saveImage(Uri uri);

        void permissionDenied();

        void showPreview(String mFilePath);

        void showPreview(Uri mFileUri);
    }
}

Now create a new class with name ImagePresenter.java

For implementing Presenter Contract create a new class ImagePresenter

package com.androidwave.imagepicker.picker;

import android.net.Uri;

import java.io.File;

/**
 * Created on : Jan 06, 2019
 * Author     : AndroidWave
 * Website    : https://androidwave.com/
 */
public class ImagePresenter implements ImageContract.Presenter {

    private final ImageContract.View view;

    public ImagePresenter(ImageContract.View view) {
        this.view = view;
    }


    @Override
    public void cameraClick() {
        if (!view.checkPermission()) {
            view.showPermissionDialog();
            return;
        }
        if (view.availableDisk() <= 5) {
            view.showNoSpaceDialog();
            return;
        }

        File file = view.newFile();

        if (file == null) {
            view.showErrorDialog();
            return;
        }

        view.startCamera(file);
    }

    @Override
    public void ChooseGalleryClick() {
        if (!view.checkPermission()) {
            view.showPermissionDialog();
            return;
        }

        view.chooseGallery();
    }

    @Override
    public void saveImage(Uri uri) {

    }

    @Override
    public void permissionDenied() {
        view.showPermissionDialog();
    }

    @Override
    public void showPreview(String mFilePath) {
        view.displayImagePreview(mFilePath);
    }

    @Override
    public void showPreview(Uri mFileUri) {
        view.displayImagePreview(mFileUri);
    }
}

Open AndroidManifest.xml add below uses permission

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

  <uses-feature android:name="android.hardware.camera" />
  <uses-feature android:name="android.hardware.camera.autofocus" />
  <uses-feature android:name="android.hardware.camera.flash" />

Define a FileProvider in AndroidManifest.xml

We are using FileProvider in this example. So open AndroidManifest.xml and put below code inside the <application>

    <provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="${applicationId}.provider"
        android:exported="false"
        android:grantUriPermissions="true">
      <meta-data
          android:name="android.support.FILE_PROVIDER_PATHS"
          android:resource="@xml/file_provider_paths" />
    </provider>

Go to res directory and create a file inside xml folder

Note make sure, you have to replace com.androidwave.imagepicker with your package name

    <paths>
      <external-path
          name="my_images"
          path="Android/data/com.androidwave.imagepicker/files/Pictures" />
      <!-- replace com.androidwave.imagepicker with your package name -->
    </paths>

Add some views in activity_main.xml

In Android Studio, If you choose the BasicActivity template while creating a project. below file will automatically create

  • MainActivity.java
  • activity_main.xml
  • content_main.xml
Open activity_main.xml  and add following view
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            android:visibility="gone"
            app:popupTheme="@style/AppTheme.PopupOverlay" />

    </android.support.design.widget.AppBarLayout>

    <include layout="@layout/content_main" />


</android.support.design.widget.CoordinatorLayout>
Now open content_main.xml add below code
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <ImageView
            android:id="@+id/header_cover_image"
            android:layout_width="match_parent"
            android:layout_height="200dp"
            android:scaleType="centerCrop"
            android:src="@drawable/nav_menu_header_bg" />

        <ImageButton
            android:id="@+id/user_profile_photo"
            android:layout_width="120dp"
            android:layout_height="120dp"
            android:layout_below="@+id/header_cover_image"
            android:layout_centerHorizontal="true"
            android:layout_marginTop="-60dp"
            android:background="@drawable/profile_imageview_bg"
            android:elevation="5dp"
            android:scaleType="centerCrop"
            android:src="@drawable/profile_pic" />

        <RelativeLayout
            android:id="@+id/profile_layout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@+id/header_cover_image"
            android:background="#ebca0707"
            android:elevation="4dp"
            android:paddingBottom="24dp">


            <ImageView
                android:id="@+id/add_friend"
                android:layout_width="30dp"
                android:layout_height="30dp"
                android:layout_alignParentRight="true"
                android:layout_marginLeft="16dp"
                android:layout_marginRight="16dp"
                android:layout_marginTop="16dp"
                android:src="@android:drawable/ic_menu_add" />


            <TextView
                android:id="@+id/user_profile_name"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerHorizontal="true"
                android:layout_marginTop="76dp"
                android:text="AndroidWave "
                android:textColor="#fff"
                android:textSize="24sp"
                android:textStyle="bold" />

            <TextView
                android:id="@+id/user_profile_short_bio"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_below="@+id/user_profile_name"
                android:layout_centerHorizontal="true"
                android:layout_marginTop="12dp"
                android:text="Android & iOS Developer Blog"
                android:textColor="#fff"
                android:textSize="14sp" />
        </RelativeLayout>


        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_below="@+id/profile_layout"
            android:layout_marginTop="5dp"
            android:orientation="vertical">

            <TextView
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:layout_margin="5dp"
                android:background="#fff"
                android:clickable="true"
                android:elevation="4dp"
                android:padding="20dp"
                android:text="Android ExoPlayer Tutorial" />

            <TextView
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:layout_margin="5dp"
                android:layout_marginBottom="3dp"
                android:layout_marginTop="3dp"
                android:background="#fff"
                android:clickable="true"
                android:elevation="4dp"
                android:padding="20dp"
                android:text="Android Camera2 API Tutorial" />

            <TextView
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:layout_margin="5dp"
                android:background="#fff"
                android:clickable="true"
                android:elevation="4dp"
                android:padding="20dp"
                android:text="Android RecyclerView UI " />

            <TextView
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:layout_margin="5dp"
                android:layout_marginBottom="3dp"
                android:layout_marginTop="3dp"
                android:background="#fff"
                android:clickable="true"
                android:elevation="4dp"
                android:padding="20dp"
                android:text="Android Kotlin Development" />

            <TextView
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:layout_margin="5dp"
                android:background="#fff"
                android:clickable="true"
                android:elevation="4dp"
                android:padding="20dp"
                android:text="iOS  UI Design" />
        </LinearLayout>
    </RelativeLayout>
</ScrollView>

Open MainActivity.java and do following operation

package com.ripleee.helper;

import android.Manifest;
import android.app.AlertDialog;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.provider.Settings;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.FileProvider;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ImageButton;
import android.widget.TextView;
import android.widget.Toast;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import com.androidwave.imagepicker.picker.ImageContract;
import com.androidwave.imagepicker.picker.ImagePresenter;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import com.karumi.dexter.Dexter;
import com.karumi.dexter.MultiplePermissionsReport;
import com.karumi.dexter.PermissionToken;
import com.karumi.dexter.listener.PermissionRequest;
import com.karumi.dexter.listener.multi.MultiplePermissionsListener;
import java.io.File;
import java.io.IOException;
import java.util.Calendar;
import java.util.List;

package com.androidwave.imagepicker;

public class MainActivity extends AppCompatActivity implements ImageContract.View {

  static final int REQUEST_TAKE_PHOTO = 101;
  static final int REQUEST_GALLERY_PHOTO = 102;
  static String[] permissions = new String[] {
      Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE
  };
  @BindView(R.id.toolbar)
  Toolbar toolbar;
  @BindView(R.id.user_profile_photo)
  ImageButton userProfilePhoto;
  @BindView(R.id.user_profile_name)
  TextView userProfileName;
  @BindView(R.id.user_profile_short_bio)
  TextView userProfileShortBio;
  private ImagePresenter mPresenter;
  Uri photoURI;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    setSupportActionBar(toolbar);
    ButterKnife.bind(this);
    mPresenter = new ImagePresenter(this);
  }

  @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) {
    // Handle action bar item clicks here.
    int id = item.getItemId();

    //noinspection SimplifiableIfStatement
    if (id == R.id.action_settings) {
      return true;
    }

    return super.onOptionsItemSelected(item);
  }

  @Override
  public void startCamera(File file) {
    Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
      if (file != null) {
        photoURI = FileProvider.getUriForFile(this,
            BuildConfig.APPLICATION_ID + ".provider", file);
        takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
        startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO);
      }
    }
  }

  @Override
  public void chooseGallery() {
    Intent pickPhoto = new Intent(Intent.ACTION_PICK,
        MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
    pickPhoto.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    startActivityForResult(pickPhoto, REQUEST_GALLERY_PHOTO);
  }

  @Override
  protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (resultCode == RESULT_OK) {
      if (requestCode == REQUEST_TAKE_PHOTO) {
        mPresenter.showPreview(photoURI);
      } else if (requestCode == REQUEST_GALLERY_PHOTO) {
        Uri selectedImage = data.getData();
        String mPhotoPath = getRealPathFromUri(selectedImage);
        mPresenter.showPreview(mPhotoPath);
      }
    }
  }

  @Override
  public boolean checkPermission() {
    for (String mPermission : permissions) {
      int result = ActivityCompat.checkSelfPermission(this, mPermission);
      if (result == PackageManager.PERMISSION_DENIED) return false;
    }
    return true;
  }

  @Override
  public void showPermissionDialog() {
    Dexter.withActivity(this).withPermissions(permissions)
        .withListener(new MultiplePermissionsListener() {
          @Override
          public void onPermissionsChecked(MultiplePermissionsReport report) {
            // check if all permissions are granted
            if (report.areAllPermissionsGranted()) {

            }
            // check for permanent denial of any permission
            if (report.isAnyPermissionPermanentlyDenied()) {
              // show alert dialog navigating to Settings
              showSettingsDialog();
            }
          }

          @Override
          public void onPermissionRationaleShouldBeShown(List<PermissionRequest> permissions,
              PermissionToken token) {
            token.continuePermissionRequest();
          }
        }).withErrorListener(error -> showErrorDialog())
        .onSameThread()
        .check();
  }

  @Override
  public File getFilePath() {
    return getExternalFilesDir(Environment.DIRECTORY_PICTURES);
  }

  public void showSettingsDialog() {
    AlertDialog.Builder builder = new AlertDialog.Builder(this);
    builder.setTitle(getString(R.string.message_need_permission));
    builder.setMessage(getString(R.string.message_grant_permission));
    builder.setPositiveButton(getString(R.string.label_setting), (dialog, which) -> {
      dialog.cancel();
      openSettings();
    });
    builder.setNegativeButton(getString(R.string.cancel), (dialog, which) -> dialog.cancel());
    builder.show();
  }

  @Override
  public void openSettings() {
    Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
    Uri uri = Uri.fromParts("package", getPackageName(), null);
    intent.setData(uri);
    startActivityForResult(intent, 101);
  }

  @Override
  public void showNoSpaceDialog() {
    AlertDialog.Builder builder = new AlertDialog.Builder(this);
    builder.setTitle(getString(R.string.error_message_no_more_space));
    builder.setMessage(getString(R.string.error_message_insufficient_space));
    builder.setPositiveButton(getString(R.string.ok), (dialog, which) -> dialog.cancel());
    builder.show();
  }

  @Override
  public int availableDisk() {
    File mFilePath = getFilePath();
    long freeSpace = mFilePath.getFreeSpace();
    return Math.round(freeSpace / 1048576);
  }

  @Override
  public File newFile() {
    Calendar cal = Calendar.getInstance();
    long timeInMillis = cal.getTimeInMillis();
    String mFileName = String.valueOf(timeInMillis) + ".jpeg";
    File mFilePath = getFilePath();
    try {
      File newFile = new File(mFilePath.getAbsolutePath(), mFileName);
      newFile.createNewFile();
      return newFile;
    } catch (IOException e) {
      e.printStackTrace();
    }

    return null;
  }

  @Override
  public void showErrorDialog() {
    Toast.makeText(getApplicationContext(), getString(R.string.error_message), Toast.LENGTH_SHORT)
        .show();
  }

  @Override
  public void displayImagePreview(String mFilePath) {
    Glide.with(MainActivity.this)
        .load(mFilePath)
        .apply(new RequestOptions().centerCrop().circleCrop().placeholder(R.drawable.profile_pic))
        .into(userProfilePhoto);
  }

  @Override
  public void displayImagePreview(Uri mFileUri) {
    Glide.with(MainActivity.this)
        .load(mFileUri)
        .apply(new RequestOptions().centerCrop().circleCrop().placeholder(R.drawable.profile_pic))
        .into(userProfilePhoto);
  }

  /**
   * Get real file path from URI
   */
  @Override
  public String getRealPathFromUri(Uri contentUri) {
    Cursor cursor = null;
    try {
      String[] proj = { MediaStore.Images.Media.DATA };
      cursor = getContentResolver().query(contentUri, proj, null, null, null);
      assert cursor != null;
      int columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
      cursor.moveToFirst();
      return cursor.getString(columnIndex);
    } finally {
      if (cursor != null) {
        cursor.close();
      }
    }
  }

  private void selectImage() {
    final CharSequence[] items = {
        getString(R.string.take_photo), getString(R.string.choose_gallery),
        getString(R.string.cancel)
    };
    AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
    builder.setItems(items, (dialog, item) -> {
      if (items[item].equals("Take Photo")) {
        mPresenter.cameraClick();
      } else if (items[item].equals("Choose from Gallery")) {
        mPresenter.ChooseGalleryClick();
      } else if (items[item].equals("Cancel")) {
        dialog.dismiss();
      }
    });
    builder.show();
  }

  @OnClick({ R.id.user_profile_photo, R.id.user_profile_name, R.id.user_profile_short_bio })
  public void onViewClicked(View view) {
    switch (view.getId()) {
      case R.id.user_profile_photo:
        selectImage();
        break;
      case R.id.user_profile_name:
        Intent browserIntent =
            new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.url_androidwave)));
        startActivity(browserIntent);
        break;
      case R.id.user_profile_short_bio:
        Intent mIntent =
            new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.url_androidwave_android)));
        startActivity(mIntent);
        break;
    }
  }
}

In this post we learned how to capture image camera & gallery in android using MVP design patterns. Happy Coding 😁

2 Comments

Write A Comment