Android & Kotlin

Capture Image from Camera & Gallery

Pinterest LinkedIn Tumblr

Nowadays the camera is a very common feature that lots of apps provide. Most of the required image capture feature in own application. eg. profile image, creating a post with an image, every food or social media app required camera feature in own application. Basically, Image capturing is very simple task, even if you are using custom. In this article, we are going to see how to capture image from camera and gallery using FileProvider in your Android app.

Google introduces FileProvider in version 22.1.0. FileProvider class is a derived class from ContentProvider. In other words you can say, FileProvider provide facilitates to secure file sharing in android via  content://Uri instead of a file:///Uri

This article includes the following topics:

  1. Initial project setup with including dependency
  2. Defining a FileProvider
  3. Granting Permissions to a URI, Storage & Camera
  4. Image Capture using Camera
  5. Pick Image from Gallery
  6. Manage Result and Image Compression
  7. Show the final image over the ImageView

After following the above step we will prepare final output like video

1. Initial project setup with including dependency

Go to the File menu -> New -> New Project -> Fill details Project Name & Package Details -> Next -> Select Empty Activity from templates -> click on Finish. Now open build.gradle and add below dependency.

dependencies {
  implementation fileTree(include: ['*.jar'], dir: 'libs')
  implementation 'androidx.appcompat:appcompat:1.0.2'
  implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
  implementation 'androidx.cardview:cardview:1.0.0'
  implementation 'com.google.android.material:material:1.0.0'

  // reactive
  implementation "io.reactivex.rxjava2:rxjava:$rootProject.rxjava2Version"
  implementation "io.reactivex.rxjava2:rxandroid:$rootProject.rxandroidVersion"
  implementation('com.github.bumptech.glide:glide:4.7.1@aar') {
    transitive = true
  }
  /**
   * For managing request permission
   */
  implementation 'com.karumi:dexter:4.2.0'

  // code generator for view
  implementation "com.jakewharton:butterknife:$rootProject.butterKnifeVersion"
  annotationProcessor "com.jakewharton:butterknife-compiler:$rootProject.butterKnifeVersion"

  testImplementation 'junit:junit:4.12'
  androidTestImplementation 'androidx.test:runner:1.2.0'
  androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}

Open project build.gradle define version extension

// Define versions in a single place
ext {
  // App dependencies
  rxjava2Version = '2.1.9'
  rxandroidVersion = '2.1.1'
  butterKnifeVersion = '10.1.0'
}

2. Defining a FileProvider

2.1 In the res folder create a resource directory named xml. Inside the xml folder create a new resource file file_provider_paths.xml

<?xml version="1.0" encoding="utf-8"?>
<paths>
  <external-path
      name="my_images"
      path="Android/data/com.androidwave.filepicker/files/Pictures"/>
  <!-- replace com.androidwave.filepicker with your package name -->
</paths>

Note – Replace com.androidwave.filepicker with your package name

2.2 In AndroidMenifest.xml you have to specify FileProvider component by adding an element<provider> like below

<provider
        android:name="androidx.core.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>

3. Granting Permissions to a URI, Storage & Camera

3.1 We have to need to declare uses-permission in Manifest file for using device camera and storage. Open AndroidMenifest.xml  and add Camera and Storage permission inside <manifest> tag

  <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" />

3.2 In Android 6, it contain a permission system for certain tasks. Each application can request the required permission on runtime. So let’s open Activity class add below code for requesting runtime permission

  /**
   * Requesting multiple permissions (storage and camera) at once
   * This uses multiple permission model from dexter
   * On permanent denial opens settings dialog
   */
  private void requestStoragePermission(boolean isCamera) {
    Dexter.withActivity(this)
        .withPermissions(Manifest.permission.READ_EXTERNAL_STORAGE,
            Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA)
        .withListener(new MultiplePermissionsListener() {
          @Override
          public void onPermissionsChecked(MultiplePermissionsReport report) {
            // check if all permissions are granted
            if (report.areAllPermissionsGranted()) {
              if (isCamera) {
                dispatchTakePictureIntent();
              } else {
                dispatchGalleryIntent();
              }
            }
            // 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 -> Toast.makeText(getApplicationContext(), "Error occurred! ", Toast.LENGTH_SHORT)
                .show())
        .onSameThread()
        .check();
  }

  /**
   * Showing Alert Dialog with Settings option
   * Navigates user to app settings
   * NOTE: Keep proper title and message depending on your app
   */
  private void showSettingsDialog() {
    AlertDialog.Builder builder = new AlertDialog.Builder(this);
    builder.setTitle("Need Permissions");
    builder.setMessage(
        "This app needs permission to use this feature. You can grant them in app settings.");
    builder.setPositiveButton("GOTO SETTINGS", (dialog, which) -> {
      dialog.cancel();
      openSettings();
    });
    builder.setNegativeButton("Cancel", (dialog, which) -> dialog.cancel());
    builder.show();
  }

  // navigating user to app settings
  private void openSettings() {
    Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
    Uri uri = Uri.fromParts("package", getPackageName(), null);
    intent.setData(uri);
    startActivityForResult(intent, 101);
  }

4. Image Capture using Camera

Forgoing forward we have to define two things.

  • We need to create an Image file
  • Create a new Intent with FileProvider URI

4.1 Now I’m creating a method that creates and return an empty file inside Picture Directory

  /**
   * Create file with current timestamp name
   * @throws IOException
   */
  private File createImageFile() throws IOException {
    // Create an image file name
    String timeStamp = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
    String mFileName = "JPEG_" + timeStamp + "_";
    File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
    File mFile = File.createTempFile(mFileName, ".jpg", storageDir);
    return mFile;
  }

4.2 Create a new Intent with FileProvider URI

 /**
   * Capture image from camera
   */
  private void dispatchTakePictureIntent() {
    Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
      // Create the File where the photo should go
      File photoFile = null;
      try {
        photoFile = createImageFile();
      } catch (IOException ex) {
        ex.printStackTrace();
        // Error occurred while creating the File
      }
      if (photoFile != null) {
        Uri photoURI = FileProvider.getUriForFile(this,
            BuildConfig.APPLICATION_ID + ".provider",
            photoFile);

        mPhotoFile = photoFile;
        takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
        startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO);
      }
    }
  }

Similarly, create an intent for pick image from gallery and add FLAG_GRANT_READ_URI_PERMISSION flag for temporary flag

  /**
   * Select image fro gallery
   */
  private void dispatchGalleryIntent() {
    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);
  }

6. Manage Result and Image Compression

After action performing, we will manage the result on onActivityResult by using ResultCode and RequestCode.  In onActivityResult we have to manage three things.

  • Get actual file path or file from result intent
  • Compress file using FileCompressor utility
  • Finally set final output over the ImageView in Android
  @Override
  protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (resultCode == RESULT_OK) {
      if (requestCode == REQUEST_TAKE_PHOTO) {
        try {
          mPhotoFile = mCompressor.compressToFile(mPhotoFile);
        } catch (IOException e) {
          e.printStackTrace();
        }
        Glide.with(MainActivity.this)
            .load(mPhotoFile)
            .apply(new RequestOptions().centerCrop()
                .circleCrop()
                .placeholder(R.drawable.profile_pic_place_holder))
            .into(imageViewProfilePic);
      } else if (requestCode == REQUEST_GALLERY_PHOTO) {
        Uri selectedImage = data.getData();
        try {
          mPhotoFile = mCompressor.compressToFile(new File(getRealPathFromUri(selectedImage)));
        } catch (IOException e) {
          e.printStackTrace();
        }
        Glide.with(MainActivity.this)
            .load(mPhotoFile)
            .apply(new RequestOptions().centerCrop()
                .circleCrop()
                .placeholder(R.drawable.profile_pic_place_holder))
            .into(imageViewProfilePic);
      }
    }
  }

In case of Gallery Intent we get result back as a URI. But we need to actual file path from compressing the image. So fetch the actual path from URI by using below code

  /**
   * Get real file path from URI
   */
  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 column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
      cursor.moveToFirst();
      return cursor.getString(column_index);
    } finally {
      if (cursor != null) {
        cursor.close();
      }
    }
  }

As you using I’m using a FileCompressor utility for compress resulted image file. Now I’m Creating FileCompressor.java file.

package com.androidwave.filepicker.utils;

import android.content.Context;
import android.graphics.Bitmap;
import io.reactivex.Flowable;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.Callable;


  public class FileCompressor {
    //max width and height values of the compressed image is taken as 612x816
    private int maxWidth = 612;
    private int maxHeight = 816;
    private Bitmap.CompressFormat compressFormat = Bitmap.CompressFormat.JPEG;
    private int quality = 80;
    private String destinationDirectoryPath;

    public FileCompressor(Context context) {
      destinationDirectoryPath = context.getCacheDir().getPath() + File.separator + "images";
    }

    public FileCompressor setMaxWidth(int maxWidth) {
      this.maxWidth = maxWidth;
      return this;
    }

    public FileCompressor setMaxHeight(int maxHeight) {
      this.maxHeight = maxHeight;
      return this;
    }

    public FileCompressor setCompressFormat(Bitmap.CompressFormat compressFormat) {
      this.compressFormat = compressFormat;
      return this;
    }

    public FileCompressor setQuality(int quality) {
      this.quality = quality;
      return this;
    }

    public FileCompressor setDestinationDirectoryPath(String destinationDirectoryPath) {
      this.destinationDirectoryPath = destinationDirectoryPath;
      return this;
    }

    public File compressToFile(File imageFile) throws IOException {
      return compressToFile(imageFile, imageFile.getName());
    }

    public File compressToFile(File imageFile, String compressedFileName) throws IOException {
      return ImageUtil.compressImage(imageFile, maxWidth, maxHeight, compressFormat, quality,
          destinationDirectoryPath + File.separator + compressedFileName);
    }

    public Bitmap compressToBitmap(File imageFile) throws IOException {
      return ImageUtil.decodeSampledBitmapFromFile(imageFile, maxWidth, maxHeight);
    }

    public Flowable<File> compressToFileAsFlowable(final File imageFile) {
      return compressToFileAsFlowable(imageFile, imageFile.getName());
    }

    public Flowable<File> compressToFileAsFlowable(final File imageFile,
        final String compressedFileName) {
      return Flowable.defer(new Callable<Flowable<File>>() {
        @Override
        public Flowable<File> call() {
          try {
            return Flowable.just(compressToFile(imageFile, compressedFileName));
          } catch (IOException e) {
            return Flowable.error(e);
          }
        }
      });
    }

    public Flowable<Bitmap> compressToBitmapAsFlowable(final File imageFile) {
      return Flowable.defer(new Callable<Flowable<Bitmap>>() {
        @Override
        public Flowable<Bitmap> call() {
          try {
            return Flowable.just(compressToBitmap(imageFile));
          } catch (IOException e) {
            return Flowable.error(e);
          }
        }
      });
    }
  }

One more utility using here is ImageUtitlies. similarly create a util file name is ImageUtil and add below code.

package com.androidwave.filepicker.utils;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.media.ExifInterface;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

public class ImageUtil {
  private ImageUtil() {

  }

  static File compressImage(File imageFile, int reqWidth, int reqHeight,
      Bitmap.CompressFormat compressFormat, int quality, String destinationPath)
      throws IOException {
    FileOutputStream fileOutputStream = null;
    File file = new File(destinationPath).getParentFile();
    if (!file.exists()) {
      file.mkdirs();
    }
    try {
      fileOutputStream = new FileOutputStream(destinationPath);
      // write the compressed bitmap at the destination specified by destinationPath.
      decodeSampledBitmapFromFile(imageFile, reqWidth, reqHeight).compress(compressFormat, quality,
          fileOutputStream);
    } finally {
      if (fileOutputStream != null) {
        fileOutputStream.flush();
        fileOutputStream.close();
      }
    }

    return new File(destinationPath);
  }

  static Bitmap decodeSampledBitmapFromFile(File imageFile, int reqWidth, int reqHeight)
      throws IOException {
    // First decode with inJustDecodeBounds=true to check dimensions
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeFile(imageFile.getAbsolutePath(), options);

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;

    Bitmap scaledBitmap = BitmapFactory.decodeFile(imageFile.getAbsolutePath(), options);

    //check the rotation of the image and display it properly
    ExifInterface exif;
    exif = new ExifInterface(imageFile.getAbsolutePath());
    int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 0);
    Matrix matrix = new Matrix();
    if (orientation == 6) {
      matrix.postRotate(90);
    } else if (orientation == 3) {
      matrix.postRotate(180);
    } else if (orientation == 8) {
      matrix.postRotate(270);
    }
    scaledBitmap =
        Bitmap.createBitmap(scaledBitmap, 0, 0, scaledBitmap.getWidth(), scaledBitmap.getHeight(),
            matrix, true);
    return scaledBitmap;
  }

  private static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth,
      int reqHeight) {
    // Raw height and width of image
    final int height = options.outHeight;
    final int width = options.outWidth;
    int inSampleSize = 1;

    if (height > reqHeight || width > reqWidth) {

      final int halfHeight = height / 2;
      final int halfWidth = width / 2;

      // Calculate the largest inSampleSize value that is a power of 2 and keeps both
      // height and width larger than the requested height and width.
      while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) {
        inSampleSize *= 2;
      }
    }

    return inSampleSize;
  }
}

Finally set the output image file over the ImageView using Glide Image library

  Glide.with(MainActivity.this)
            .load(mPhotoFile)
            .apply(new RequestOptions().centerCrop()
                .circleCrop()
                .placeholder(R.drawable.profile_pic_place_holder))
            .into(imageViewProfilePic);

7. Define some color value inside Open color.xml like below

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorPrimary">#039BE5</color>
    <color name="colorPrimaryDark">#0288D1</color>
    <color name="colorAccent">#FF4081</color>
    <color name="start_color">#29B6F6</color>
    <color name="end_color">#4FC3F7</color>
    <color name="white">#FFFFFF</color>
</resources>

8. Open activity_main.xml or add some views. I have try to build user profile look in this layout

<?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"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fillViewport="true"
    tools:context=".MainActivity">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <RelativeLayout
            android:id="@+id/relativeLayout"
            android:layout_width="match_parent"
            android:layout_height="405dp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="350dp"
                android:background="@drawable/gradient_bg"
                android:orientation="vertical">

                <ImageView
                    android:id="@+id/imageViewProfilePic"
                    android:layout_width="150dp"
                    android:layout_height="150dp"
                    android:layout_gravity="center_horizontal"
                    android:layout_marginTop="45dp"
                    android:src="@drawable/profile_picture" />

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_gravity="center_horizontal"
                    android:layout_marginTop="10dp"
                    android:text="Morris"
                    android:textColor="#fff"
                    android:textSize="21sp"
                    android:textStyle="bold" />
            </LinearLayout>

            <androidx.cardview.widget.CardView
                android:layout_width="400dp"
                android:layout_height="120dp"
                android:layout_centerHorizontal="true"
                android:layout_marginTop="275dp">

                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:orientation="horizontal"
                    android:weightSum="3">

                    <LinearLayout
                        android:layout_width="0dp"
                        android:layout_height="match_parent"
                        android:layout_weight="1"
                        android:gravity="center"
                        android:orientation="vertical">

                        <TextView
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:text="Photos"
                            android:textSize="20sp" />

                        <TextView
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:paddingTop="10dp"
                            android:text="125"
                            android:textColor="@color/start_color"
                            android:textSize="20sp"
                            android:textStyle="bold" />

                    </LinearLayout>

                    <LinearLayout
                        android:layout_width="0dp"
                        android:layout_height="match_parent"
                        android:layout_weight="1"
                        android:gravity="center"
                        android:orientation="vertical">

                        <TextView
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:text="Followers"
                            android:textSize="20sp" />

                        <TextView
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:paddingTop="10dp"
                            android:text="1205"
                            android:textColor="@color/start_color"
                            android:textSize="20sp"
                            android:textStyle="bold" />

                    </LinearLayout>

                    <LinearLayout
                        android:layout_width="0dp"
                        android:layout_height="match_parent"
                        android:layout_weight="1"
                        android:gravity="center"
                        android:orientation="vertical">

                        <TextView
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:text="Following"
                            android:textSize="20sp" />

                        <TextView
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:paddingTop="10dp"
                            android:text="360"
                            android:textColor="@color/start_color"
                            android:textSize="20sp"
                            android:textStyle="bold" />

                    </LinearLayout>
                </LinearLayout>

            </androidx.cardview.widget.CardView>
        </RelativeLayout>

        <LinearLayout
            android:id="@+id/linearLayout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_marginEnd="8dp"
            android:layout_marginLeft="8dp"
            android:layout_marginRight="8dp"
            android:layout_marginStart="8dp"
            android:orientation="vertical"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/relativeLayout">

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_gravity="center_vertical"
                android:padding="16dp"
                android:text="@string/email"
                android:textStyle="bold" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_gravity="center_vertical"
                android:padding="16dp"
                android:text="@string/contact_number"
                android:textStyle="bold" />

        </LinearLayout>

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:layout_marginBottom="16dp"
            android:layout_marginEnd="8dp"
            android:layout_marginLeft="8dp"
            android:layout_marginRight="8dp"
            android:layout_marginStart="8dp"
            android:layout_marginTop="16dp"
            android:background="@drawable/button_style"
            android:text="Follow Me"
            android:textColor="#fff"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/linearLayout" />


    </androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>

9. Finally, open your main activity (MainActivity.java) and modify the code as below.

  • Set ContentView activity_main.xml
  • Bind view with id using ButterKnife
  • Set onClick Listener over the ImageView
  • Create alert popup to the user for choose want to Capture a picture or pick from Gallery
  • Check permission using dexer

Add dispatch intent inside the onPermissionCheck methods

10. The final code looks as

package com.androidwave.filepicker;

import android.Manifest;
import android.app.AlertDialog;
import android.content.Intent;
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.widget.ImageView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.FileProvider;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import com.androidwave.filepicker.utils.FileCompressor;
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.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

public class MainActivity extends AppCompatActivity {
  static final int REQUEST_TAKE_PHOTO = 1;
  static final int REQUEST_GALLERY_PHOTO = 2;
  File mPhotoFile;
  FileCompressor mCompressor;
  @BindView(R.id.imageViewProfilePic)
  ImageView imageViewProfilePic;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    ButterKnife.bind(this);
    mCompressor = new FileCompressor(this);
  }

  /**
   * Alert dialog for capture or select from galley
   */
  private void selectImage() {
    final CharSequence[] items = {
        "Take Photo", "Choose from Library",
        "Cancel"
    };
    AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
    builder.setItems(items, (dialog, item) -> {
      if (items[item].equals("Take Photo")) {
        requestStoragePermission(true);
      } else if (items[item].equals("Choose from Library")) {
        requestStoragePermission(false);
      } else if (items[item].equals("Cancel")) {
        dialog.dismiss();
      }
    });
    builder.show();
  }

  /**
   * Capture image from camera
   */
  private void dispatchTakePictureIntent() {
    Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
      // Create the File where the photo should go
      File photoFile = null;
      try {
        photoFile = createImageFile();
      } catch (IOException ex) {
        ex.printStackTrace();
        // Error occurred while creating the File
      }
      if (photoFile != null) {
        Uri photoURI = FileProvider.getUriForFile(this,
            BuildConfig.APPLICATION_ID + ".provider",
            photoFile);

        mPhotoFile = photoFile;
        takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
        startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO);
      }
    }
  }

  /**
   * Select image fro gallery
   */
  private void dispatchGalleryIntent() {
    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, Intent data) {
    if (resultCode == RESULT_OK) {
      if (requestCode == REQUEST_TAKE_PHOTO) {
        try {
          mPhotoFile = mCompressor.compressToFile(mPhotoFile);
        } catch (IOException e) {
          e.printStackTrace();
        }
        Glide.with(MainActivity.this)
            .load(mPhotoFile)
            .apply(new RequestOptions().centerCrop()
                .circleCrop()
                .placeholder(R.drawable.profile_pic_place_holder))
            .into(imageViewProfilePic);
      } else if (requestCode == REQUEST_GALLERY_PHOTO) {
        Uri selectedImage = data.getData();
        try {
          mPhotoFile = mCompressor.compressToFile(new File(getRealPathFromUri(selectedImage)));
        } catch (IOException e) {
          e.printStackTrace();
        }
        Glide.with(MainActivity.this)
            .load(mPhotoFile)
            .apply(new RequestOptions().centerCrop()
                .circleCrop()
                .placeholder(R.drawable.profile_pic_place_holder))
            .into(imageViewProfilePic);
      }
    }
  }

  /**
   * Requesting multiple permissions (storage and camera) at once
   * This uses multiple permission model from dexter
   * On permanent denial opens settings dialog
   */
  private void requestStoragePermission(boolean isCamera) {
    Dexter.withActivity(this)
        .withPermissions(Manifest.permission.READ_EXTERNAL_STORAGE,
            Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA)
        .withListener(new MultiplePermissionsListener() {
          @Override
          public void onPermissionsChecked(MultiplePermissionsReport report) {
            // check if all permissions are granted
            if (report.areAllPermissionsGranted()) {
              if (isCamera) {
                dispatchTakePictureIntent();
              } else {
                dispatchGalleryIntent();
              }
            }
            // 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 -> Toast.makeText(getApplicationContext(), "Error occurred! ", Toast.LENGTH_SHORT)
                .show())
        .onSameThread()
        .check();
  }

  /**
   * Showing Alert Dialog with Settings option
   * Navigates user to app settings
   * NOTE: Keep proper title and message depending on your app
   */
  private void showSettingsDialog() {
    AlertDialog.Builder builder = new AlertDialog.Builder(this);
    builder.setTitle("Need Permissions");
    builder.setMessage(
        "This app needs permission to use this feature. You can grant them in app settings.");
    builder.setPositiveButton("GOTO SETTINGS", (dialog, which) -> {
      dialog.cancel();
      openSettings();
    });
    builder.setNegativeButton("Cancel", (dialog, which) -> dialog.cancel());
    builder.show();
  }

  // navigating user to app settings
  private void openSettings() {
    Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
    Uri uri = Uri.fromParts("package", getPackageName(), null);
    intent.setData(uri);
    startActivityForResult(intent, 101);
  }

  /**
   * Create file with current timestamp name
   *
   * @throws IOException
   */
  private File createImageFile() throws IOException {
    // Create an image file name
    String timeStamp = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
    String mFileName = "JPEG_" + timeStamp + "_";
    File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
    File mFile = File.createTempFile(mFileName, ".jpg", storageDir);
    return mFile;
  }

  /**
   * Get real file path from URI
   */
  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 column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
      cursor.moveToFirst();
      return cursor.getString(column_index);
    } finally {
      if (cursor != null) {
        cursor.close();
      }
    }
  }

  @OnClick(R.id.imageViewProfilePic)
  public void onViewClicked() {
    selectImage();
  }
}

Run the project and click user profile you will show an alert dialog for choosing camera and gallery after that choose action that you want. the final output will show on your screen. If you have any queries, feel free to ask them in the comment section below.

Source Code – Capture Image from Camera & Gallery


8 Comments

  1.  (java.lang.IllegalArgumentException: Couldn’t find meta-data for provider with authority com.karumi.dexter.provider)

    I got this error when to open Take Photo. why?

  2. I’d love to see an updated tutorial showing how this would work with scoped storage. Running your current example won’t work with API 29 unless your AndroidManifest.xml has android:requestLegacyExternalStorage=”true”.

  3. Hi, i need help please
    I am under Android and I try to integrate the function of capturing a photo for a profile,it works when I choose a photo from the gallery but when I try to take a picture from the camera it does not run the application,go back to the previous menu here is the error message.
    there is my error message/
    E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.company_name.nidhal, PID: 14052
    java.lang.IllegalArgumentException: Couldn’t find meta-data for provider with authority com.bumptech.glide.provider

    • oluwafemi

      change the contents of file_provider_paths.xml
      to this

Write A Comment