Android Architecture

Getting images from Camera & Gallery using MVP

In my previous tutorials explains how to capture image Camera & Gallery using FileProvider. Most people are 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

As this article, Getting images from Camera & Gallery using MVP, You need to have knowledge camera and FileProvider module. I recommend you got through my previous tutorials that give you an idea about camera integrating into your apps using standard design pattern.

Getting images from Camera & Gallery Sample App

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 file.

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

Create MVP Contract

As you know are following MVP design pattern in this article. So 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 withs 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

<?xml version="1.0" encoding="utf-8"?>
<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 BasicActivity template while creating 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 &amp; 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.androidwave.imagepicker;

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 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;

import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;

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
     *
     * @param contentUri
     * @return
     */
    @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;
        }
    }
}

After following all above just RUN the project and use app, If you have any queries, feel free to ask them in the comment section below. Happy Coding 🙂

Download Sample Project – Getting images from Gallery/Camera using MVP

1
Leave a Reply

avatar
1 Comment threads
0 Thread replies
0 Followers
 
Most reacted comment
Hottest comment thread
1 Comment authors
Josselin Recent comment authors
  Subscribe  
newest oldest most voted
Notify of
Josselin
Guest
Josselin

Nice job, man!