[Android Example] Pick Image from Gallery or Camera

Nowadays, images are a important part of almost every mobile app. Some of these apps have their own camera and gallery to capture or pick image. This is mostly common for apps which are used for image editing(Instagram). But not every app might have the same use case. Not every app needs to design a camera/gallery to allow user to pick image. Android provides us a way to use the system Camera and Gallery app to select image. This is possible with the help of Intent.

Intent in itself is very useful concept in Android. If you are not sure of how they work read this.

In this example we will develop an application to pick an image from Gallery or capture an Image using the system Camera app. 

Pick Image from Gallery Example

Before beginning you need to make sure your app has READ_EXTERNAL_STORAGE permission. To declare this you need to add the following in your AndroidManifest.xml .

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

For devices on or above Marshamallow version, make sure you request the permission at runtime as shown here

Step 1: Create an Intent

Intent is how android components contact each other. Therefore to open the Gallery app you need to create an Intent describing the task and launch it. For launching Gallery app action used will be ACTION_PICK.

private void pickFromGallery(){
       //Create an Intent with action as ACTION_PICK
       Intent intent=new Intent(Intent.ACTION_PICK);
       // Sets the type as image/*. This ensures only components of type image are selected
       intent.setType("image/*");
       //We pass an extra array with the accepted mime types. This will ensure only components with these MIME types as targeted.
       String[] mimeTypes = {"image/jpeg", "image/png"};
       intent.putExtra(Intent.EXTRA_MIME_TYPES,mimeTypes);
       // Launching the Intent 
       startActivityForResult(intent,GALLERY_REQUEST_CODE);

   }
  • Set the action to ACTION_PICK and type to “image/*” this ensures only components which can handle picking of images are targeted
  • You must have noticed that we pass an extra array of MIME types even after setting the type as image/*. This ensures that only components with jpeg and png images should be activated with this Intent.
  • Also we launch the Intent with startActivityForResult. This will ensure that your activity receives a callback once the image is selected. Integer request code needs to be provided when launching the Intent. This acts as an identifier when the target(Gallery app) sends back the image.
  • Apart from the system Gallery app, user might have some other third party apps installed which meet the Intent description. In this case Android launches a chooser. If no such apps are installed directly Gallery app is launched.

    pick_image
    Chooser when multiple components satisfy the Intent

Step 2: Receiving the selected Image

To receive the image selected from Gallery you need to override the method onActivityResult() as shown below

public void onActivityResult(int requestCode,int resultCode,Intent data){

      // Result code is RESULT_OK only if the user selects an Image
      if (resultCode == Activity.RESULT_OK)
      switch (requestCode){
          case GALLERY_REQUEST_CODE:
              //data.getData returns the content URI for the selected Image
              Uri selectedImage = data.getData();
              imageView.setImageURI(selectedImage);
              break;
      }

  }
  • You receive the information about the selected image wrapped in an Intent in the onActivityResult() callback.
  • In the above code snippet we retrieve the content URI of the image by calling getData() on the returned Intent object. We use the same URI to display the image in your ImageView.
  • But content URI is not same as absolute file path. You can retrieve the absolute file path from the content URI as shown in code snippet below
public void onActivityResult(int requestCode,int resultCode,Intent data){

    // Result code is RESULT_OK only if the user selects an Image
    if (resultCode == Activity.RESULT_OK)
    switch (requestCode){
        case GALLERY_REQUEST_CODE:
            //data.getData return the content URI for the selected Image
            Uri selectedImage = data.getData();

            String[] filePathColumn = { MediaStore.Images.Media.DATA };
            // Get the cursor
            Cursor cursor = getContentResolver().query(selectedImage, filePathColumn, null, null, null);
            // Move to first row
            cursor.moveToFirst();
            //Get the column index of MediaStore.Images.Media.DATA
            int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
            //Gets the String value in the column
            String imgDecodableString = cursor.getString(columnIndex);

            cursor.close();
            // Set the Image in ImageView after decoding the String
            imageView.setImageBitmap(BitmapFactory.decodeFile(imgDecodableString));

            break;
      
    }

}

Capturing an Image with Camera

In this section instead of selecting photo from gallery app we will allow users to click a photo using camera app and display it in your application.

Since we are clicking a new photo and saving it we will need to add WRITE_EXTERNAL_STORAGE permission in the AndroidManifest.xml

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

For devices running on and above Android Marshmallow version, we need to ask this permission at runtime as shown here

Nougot

Before Nougot was released we were creating and passing a file path to the camera app to save the captured image. Since Nougot version Android has restricted apps to share file URIs with scheme “file://”. Basically you cannot pass a file path with “file://” along with the Intent. You are only allowed to share URIs with content:// scheme. This is where FileProvider comes into picture

FileProvider is a special subclass of ContentProvider that facilitates secure sharing of files associated with an app by creating a content:// Uri for a file instead of a file:/// Uri.

In this example we will be implementing the same

Step 1: Create file_paths.xml

First we should create a XML file to specify the location of the files for which you will be requesting content URIs. Your XML file should have a  <paths> element with some child elements. Each child elements needs to have two attributes “name” and “path”. The path attribute is to mention the sub-directory location  and name attribute will replace the sub-directory name in the content URI shared. Some child element examples are below

  • <external-path name=namepath=path/>
     external-path child element represents that you intend to share files from the device public external storage.
  • <external-files-path name=namepath=path/>
     external-files-path child element represents that you intend to share files from the app’s private external storage. You can read more about this here

For this example we create a file by the name file_paths.xml in xml directory and add the following code in it.

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="external_files" path="."/>
</paths>

The above code snippet tells FileProvider that you intend to request content URIs for the root folder (path=”.”) of your public external storage with the name  external_files. The <paths> element can have multiple child elements

Step 2:

To implement FileProvider in your app you need to add the provider tag in your AndroidManifest.xml as shown below. We provide the path for the file created in step one for attribute resource.

<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_paths">
         </meta-data>
     </provider>

Set the android:nameattribute to android.support.v4.content.FileProvider. Set the android:authorities attribute to a URI authority based on a domain you control; for example, if you control the domain mydomain.com you should use the authority com.mydomain.fileprovider. Set the android:exported attribute to false; the FileProvider does not need to be public. Set the android:grantUriPermissions attribute to true, to allow you to grant temporary access to files

Step 3: Create file path to store Image

Since camera will be capturing a new image we need to provide it with a file path to save that image. In this step we have a method createImageFile() which returns a file  created in DCIM/Camera folder of external storage.

private String cameraFilePath;

private File createImageFile() throws IOException {
        // Create an image file name
        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
        String imageFileName = "JPEG_" + timeStamp + "_";
        //This is the directory in which the file will be created. This is the default location of Camera photos
        File storageDir = new File(Environment.getExternalStoragePublicDirectory(
                Environment.DIRECTORY_DCIM), "Camera");
        File image = File.createTempFile(
                imageFileName,  /* prefix */
                ".jpg",         /* suffix */
                storageDir      /* directory */
        );
        // Save a file: path for using again
        cameraFilePath = "file://" + image.getAbsolutePath();
        return image;
    }
  • We declare a global string variable cameraFilePath. This is to store the path  of the file created in this method. This variable will be used in next step.
  • File name is generated at random using the current date
  • The parent  folder for this file is DCIM/Camera. This is the default location for all the Images clicked via Camera

Step 3: Launch an Intent

To start the  camera app we need to create an Intent describing the task and launch it. Action used for opening Camera app is MediaStore.ACTION_IMAGE_CAPTURE.

private void captureFromCamera() {
       try {
           Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
           intent.putExtra(MediaStore.EXTRA_OUTPUT, FileProvider.getUriForFile(this, BuildConfig.APPLICATION_ID + ".provider", createImageFile()));
           startActivityForResult(intent, CAMERA_REQUEST_CODE);
       } catch (IOException ex) {
           ex.printStackTrace();
       }
   }
  • Note that we share the file path using FileProvider API getUriForFile(). It takes three parameter – Context, FileProvider authority and the new file created.  The getUriForFile() method returns a content URI which can be shared with other apps.
  •  We launch the Intent using startActivityForResult() and pass a unique integer identifier to identify this Intent.
  • The camera app then saves the new image into content URI  shared. Note that content URI actually points to the file created in createImageFile() method. We already have path for this file in our global variable cameraFilePath.

Step 4: Receiving the captured Image

Once the camera captures an image we receive a callback onActivityResult() in our activity. We can use the integer request code to match if the callback is invoked for camera Intent only.

public void onActivityResult(int requestCode,int resultCode,Intent data){

      // Result code is RESULT_OK only if the user captures an Image
      if (resultCode == Activity.RESULT_OK)
      switch (requestCode){
          case CAMERA_REQUEST_CODE:
              imageView.setImageURI(Uri.parse(cameraFilePath));
              break;
      }

  }
  • As already mentioned  cameraFilePath variable has the path of file where camera has saved the image. We can use the same file path to display that image.

Conclusion

I hope this tutorial was useful and you are now clear on how to pick image in Android. If yes do check some more android examples here. We regularly post new android examples every week.