Tutorial

Android Capture Image from Camera and Gallery

Published on August 3, 2022
author

Anupam Chugh

Android Capture Image from Camera and Gallery

In this tutorial we’ll develop an application that picks an image from camera or gallery and display that in an ImageView. Note: The below code works fine for pre-Android Nougat versions. For the latest working example check out the [updated article](http://Note: Google has taken a detour from the Android Alphabetical versions. Android Q has been renamed to Android 10. Since this tutorial was written before Google decided to do this, you’ll see Android Q at some places in the article.).

Android Capture Image Overview

With the commence of Android Marshmallow, runtime permissions need to be implemented forefront. Add the following permissions in the Android Manifest.xml file, above the application tag.

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

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

By adding android.hardware.camera, Play Store detects and prevents installing the application on devices with no camera. Intent is the standard way to delegate actions to another application. To start the native camera the Intent requires android.provider.MediaStore.ACTION_IMAGE_CAPTURE. To choose an image from gallery, the Intent requires the following argument : Intent.ACTION_GET_CONTENT. In this tutorial we’ll be invoking an image picker, that lets us select an image from camera or gallery and displays the image in a circular image view and a normal image view. Add the following dependency inside the build.gradle file. compile 'de.hdodenhof:circleimageview:2.1.0'

Android Image Capture Project Structure

android capture image from camera and gallery picker project

Android Capture Image Code

The layout for the activity_main.xml stays the same barring the icon change for the FAB button to @android:drawable/ic_menu_camera. The content_main.xml is given below:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:app="https://schemas.android.com/apk/res-auto"
    xmlns:tools="https://schemas.android.com/tools"
    android:id="@+id/content_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:background="#000000"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context="com.journaldev.imagepicker.MainActivity"
    tools:showIn="@layout/activity_main">


    <RelativeLayout
        android:layout_width="250dp"
        android:layout_height="250dp"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:background="@drawable/image_border"
        android:clickable="true"
        android:orientation="vertical">


        <ImageView
            android:id="@+id/imageView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:adjustViewBounds="true"
            android:scaleType="centerCrop" />

    </RelativeLayout>

    <de.hdodenhof.circleimageview.CircleImageView
        android:id="@+id/img_profile"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_gravity="center_horizontal"
        android:src="@drawable/profile"
        app:civ_border_width="5dp"
        app:civ_border_color="#FFFFFF"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true" />

</RelativeLayout>

The code for the MainActivity.java is given below

public class MainActivity extends AppCompatActivity {

    Bitmap myBitmap;
    Uri picUri;


    private ArrayList permissionsToRequest;
    private ArrayList permissionsRejected = new ArrayList();
    private ArrayList permissions = new ArrayList();

    private final static int ALL_PERMISSIONS_RESULT = 107;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                startActivityForResult(getPickImageChooserIntent(), 200);
            }
        });


        permissions.add(CAMERA);
        permissionsToRequest = findUnAskedPermissions(permissions);
        //get the permissions we have asked for before but are not granted..
        //we will store this in a global list to access later.


        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {


            if (permissionsToRequest.size() > 0)
                requestPermissions(permissionsToRequest.toArray(new String[permissionsToRequest.size()]), ALL_PERMISSIONS_RESULT);
        }

    }

    @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. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

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

        return super.onOptionsItemSelected(item);
    }


    /**
     * Create a chooser intent to select the source to get image from.<br />
     * The source can be camera's (ACTION_IMAGE_CAPTURE) or gallery's (ACTION_GET_CONTENT).<br />
     * All possible sources are added to the intent chooser.
     */
    public Intent getPickImageChooserIntent() {

        // Determine Uri of camera image to save.
        Uri outputFileUri = getCaptureImageOutputUri();

        List allIntents = new ArrayList();
        PackageManager packageManager = getPackageManager();

        // collect all camera intents
        Intent captureIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
        List listCam = packageManager.queryIntentActivities(captureIntent, 0);
        for (ResolveInfo res : listCam) {
            Intent intent = new Intent(captureIntent);
            intent.setComponent(new ComponentName(res.activityInfo.packageName, res.activityInfo.name));
            intent.setPackage(res.activityInfo.packageName);
            if (outputFileUri != null) {
                intent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri);
            }
            allIntents.add(intent);
        }

        // collect all gallery intents
        Intent galleryIntent = new Intent(Intent.ACTION_GET_CONTENT);
        galleryIntent.setType("image/*");
        List listGallery = packageManager.queryIntentActivities(galleryIntent, 0);
        for (ResolveInfo res : listGallery) {
            Intent intent = new Intent(galleryIntent);
            intent.setComponent(new ComponentName(res.activityInfo.packageName, res.activityInfo.name));
            intent.setPackage(res.activityInfo.packageName);
            allIntents.add(intent);
        }

        // the main intent is the last in the list (fucking android) so pickup the useless one
        Intent mainIntent = allIntents.get(allIntents.size() - 1);
        for (Intent intent : allIntents) {
            if (intent.getComponent().getClassName().equals("com.android.documentsui.DocumentsActivity")) {
                mainIntent = intent;
                break;
            }
        }
        allIntents.remove(mainIntent);

        // Create a chooser from the main intent
        Intent chooserIntent = Intent.createChooser(mainIntent, "Select source");

        // Add all other intents
        chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, allIntents.toArray(new Parcelable[allIntents.size()]));

        return chooserIntent;
    }


    /**
     * Get URI to image received from capture by camera.
     */
    private Uri getCaptureImageOutputUri() {
        Uri outputFileUri = null;
        File getImage = getExternalCacheDir();
        if (getImage != null) {
            outputFileUri = Uri.fromFile(new File(getImage.getPath(), "profile.png"));
        }
        return outputFileUri;
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {

        Bitmap bitmap;
        if (resultCode == Activity.RESULT_OK) {

            ImageView imageView = (ImageView) findViewById(R.id.imageView);

            if (getPickImageResultUri(data) != null) {
                picUri = getPickImageResultUri(data);

                try {
                    myBitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), picUri);
                    myBitmap = rotateImageIfRequired(myBitmap, picUri);
                    myBitmap = getResizedBitmap(myBitmap, 500);

                    CircleImageView croppedImageView = (CircleImageView) findViewById(R.id.img_profile);
                    croppedImageView.setImageBitmap(myBitmap);
                    imageView.setImageBitmap(myBitmap);

                } catch (IOException e) {
                    e.printStackTrace();
                }


            } else {


                bitmap = (Bitmap) data.getExtras().get("data");

                myBitmap = bitmap;
                CircleImageView croppedImageView = (CircleImageView) findViewById(R.id.img_profile);
                if (croppedImageView != null) {
                    croppedImageView.setImageBitmap(myBitmap);
                }

                imageView.setImageBitmap(myBitmap);

            }

        }

    }

    private static Bitmap rotateImageIfRequired(Bitmap img, Uri selectedImage) throws IOException {

        ExifInterface ei = new ExifInterface(selectedImage.getPath());
        int orientation = ei.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);

        switch (orientation) {
            case ExifInterface.ORIENTATION_ROTATE_90:
                return rotateImage(img, 90);
            case ExifInterface.ORIENTATION_ROTATE_180:
                return rotateImage(img, 180);
            case ExifInterface.ORIENTATION_ROTATE_270:
                return rotateImage(img, 270);
            default:
                return img;
        }
    }

    private static Bitmap rotateImage(Bitmap img, int degree) {
        Matrix matrix = new Matrix();
        matrix.postRotate(degree);
        Bitmap rotatedImg = Bitmap.createBitmap(img, 0, 0, img.getWidth(), img.getHeight(), matrix, true);
        img.recycle();
        return rotatedImg;
    }

    public Bitmap getResizedBitmap(Bitmap image, int maxSize) {
        int width = image.getWidth();
        int height = image.getHeight();

        float bitmapRatio = (float) width / (float) height;
        if (bitmapRatio > 0) {
            width = maxSize;
            height = (int) (width / bitmapRatio);
        } else {
            height = maxSize;
            width = (int) (height * bitmapRatio);
        }
        return Bitmap.createScaledBitmap(image, width, height, true);
    }


    /**
     * Get the URI of the selected image from {@link #getPickImageChooserIntent()}.<br />
     * Will return the correct URI for camera and gallery image.
     *
     * @param data the returned data of the activity result
     */
    public Uri getPickImageResultUri(Intent data) {
        boolean isCamera = true;
        if (data != null) {
            String action = data.getAction();
            isCamera = action != null && action.equals(MediaStore.ACTION_IMAGE_CAPTURE);
        }


        return isCamera ? getCaptureImageOutputUri() : data.getData();
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);

        // save file url in bundle as it will be null on scren orientation
        // changes
        outState.putParcelable("pic_uri", picUri);
    }

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);

        // get the file url
        picUri = savedInstanceState.getParcelable("pic_uri");
    }

    private ArrayList findUnAskedPermissions(ArrayList wanted) {
        ArrayList result = new ArrayList();

        for (String perm : wanted) {
            if (!hasPermission(perm)) {
                result.add(perm);
            }
        }

        return result;
    }

    private boolean hasPermission(String permission) {
        if (canMakeSmores()) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                return (checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED);
            }
        }
        return true;
    }

    private void showMessageOKCancel(String message, DialogInterface.OnClickListener okListener) {
        new AlertDialog.Builder(this)
                .setMessage(message)
                .setPositiveButton("OK", okListener)
                .setNegativeButton("Cancel", null)
                .create()
                .show();
    }

    private boolean canMakeSmores() {
        return (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1);
    }

    @TargetApi(Build.VERSION_CODES.M)
    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {

        switch (requestCode) {

            case ALL_PERMISSIONS_RESULT:
                for (String perms : permissionsToRequest) {
                    if (hasPermission(perms)) {

                    } else {

                        permissionsRejected.add(perms);
                    }
                }

                if (permissionsRejected.size() > 0) {


                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                        if (shouldShowRequestPermissionRationale(permissionsRejected.get(0))) {
                            showMessageOKCancel("These permissions are mandatory for the application. Please allow access.",
                                    new DialogInterface.OnClickListener() {
                                        @Override
                                        public void onClick(DialogInterface dialog, int which) {
                                            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {

                                                //Log.d("API123", "permisionrejected " + permissionsRejected.size());

                                                requestPermissions(permissionsRejected.toArray(new String[permissionsRejected.size()]), ALL_PERMISSIONS_RESULT);
                                            }
                                        }
                                    });
                            return;
                        }
                    }

                }

                break;
        }

    }
}

There are a lot of inferences to be drawn from the code above.

  • We need to ask for the Camera runtime permissions when the user starts the activity.
  • As we are starting the intent to get some result back, we need to call startActivityForResult with the relevant arguments
  • Instead of using a dialog to separately call the Intents for Camera and Gallery, we’ve used a method getPickImageChooserIntent() that creates a single chooser intent for all the camera and gallery intents(note the documents intent). Intent.EXTRA_INITIAL_INTENTS is used to add the multiple application intents at one place
  • For the camera intent, MediaStore.EXTRA_OUTPUT is passed as an extra to specify the image storage path. Without this you’ll be returned only a small resolution image.
  • The URI path for the image returned by camera is fetched inside the method getCaptureImageOutputUri().
  • The onActivityResult essentially returns a URI to the image. Some devices do return the bitmap as data.getExtras().get("data");.
  • When an image is clicked, the camera screen while returning restarts the activity thereby causing the URI stored from the method getCaptureImageOutputUri() to become null. Hence it’s essential that we store and restore that URI using onSaveInstanceState() and onRestoreInstanceState().
  • The bitmap is retrieved from the URI in the following line of code. myBitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), picUri);
  • Devices like Samsung galaxy are known to capture the image in landscape orientation. Retrieving the image and displaying as it is can cause it to be displayed in the wrong orientation. Hence we’ve called the method rotateImageIfRequired(myBitmap, picUri);
  • ExifInterface is a class for reading and writing Exif tags in a JPEG file or a RAW image file.
  • In the end we call the method getResizedBitmap() to scale the bitmap by width or height(whichever is larger) and set the image to the image view using setImageBitmap.

The output of the application in action is given below. Note: To capture and display an image from camera you’ll need to run the application on a smartphone for obvious reasons. android capture image This brings an end to this tutorial. You can download the Android project for Image Capture from the link below.

Download Android Capture Image from Camera Project

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Learn more about our products

About the authors
Default avatar
Anupam Chugh

author

While we believe that this content benefits our community, we have not yet thoroughly reviewed it. If you have any suggestions for improvements, please let us know by clicking the “report an issue“ button at the bottom of the tutorial.

Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
 
JournalDev
DigitalOcean Employee
DigitalOcean Employee badge
March 21, 2021

Dear Sir , Can you share working code to support android 11 ® and above for capturing image

- Narayan Semwal

    JournalDev
    DigitalOcean Employee
    DigitalOcean Employee badge
    November 21, 2020

    Can you share working code to support android 10 and above for capturing image and video ? The above codde is not working Android 10 and above

    - Shrini jaiswal

      JournalDev
      DigitalOcean Employee
      DigitalOcean Employee badge
      September 22, 2020

      hi, image is rotating in 0ne plus phone.

      - kshitija

        JournalDev
        DigitalOcean Employee
        DigitalOcean Employee badge
        September 8, 2020

        App stuck in Android 10 any solution do you have please update it

        - Karan

          JournalDev
          DigitalOcean Employee
          DigitalOcean Employee badge
          August 18, 2020

          Nice documentation. but I am looking for help to access SD card photos using Android 10(API 29). I have tried a lot but didn’t get success. Each time I got the path ‘Document/6AE7-160C:download/5528-i-love-you-poems.jpg’. Will you please help me to solve this?

          - Shailesh Vishwase

            JournalDev
            DigitalOcean Employee
            DigitalOcean Employee badge
            June 19, 2020

            Not save image

            - MuayinKei

              JournalDev
              DigitalOcean Employee
              DigitalOcean Employee badge
              July 5, 2019

              Dear Sir, Android Version 9 (Pie) Device Camera Not Working in this Code Can’t Get onActivityResult Debug area Pls Help me

              - Panneer

                JournalDev
                DigitalOcean Employee
                DigitalOcean Employee badge
                December 4, 2018

                OnActivityResult , camera is always false even though a picture is clicked. Intent action does not match MediaStore.ACTION_IMAGE_CAPTURE

                - Meghana Shetty

                  JournalDev
                  DigitalOcean Employee
                  DigitalOcean Employee badge
                  July 11, 2018

                  Camera image is not saving in external storage. and Camera image is not set to CircleImageView

                  - Harish

                    JournalDev
                    DigitalOcean Employee
                    DigitalOcean Employee badge
                    May 14, 2018

                    on select image from gallery it gives the error in logcat as 05-14 16:45:20.578 com.example.android.abc E/picUri is: content://com.miui.gallery.open/raw/%2Fstorage%2Femulated%2F0%2FDCIM%2FCamera%2FIMG_20180511_121705.jpg 05-14 16:45:20.856 com.example.android.abc W/ExifInterface: Invalid image. java.io.FileNotFoundException: /raw/storage/emulated/0/DCIM/Camera/IMG_20180511_121705.jpg: open failed: ENOENT (No such file or directory) The problem is i want to upload image to server,on select image from camera or gallery intent ,image uploaded successfully on camera intent but image select from gallery did not work.

                    - sunil sharma

                      Try DigitalOcean for free

                      Click below to sign up and get $200 of credit to try our products over 60 days!

                      Sign up

                      Join the Tech Talk
                      Success! Thank you! Please check your email for further details.

                      Please complete your information!

                      Become a contributor for community

                      Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

                      DigitalOcean Documentation

                      Full documentation for every DigitalOcean product.

                      Resources for startups and SMBs

                      The Wave has everything you need to know about building a business, from raising funding to marketing your product.

                      Get our newsletter

                      Stay up to date by signing up for DigitalOcean’s Infrastructure as a Newsletter.

                      New accounts only. By submitting your email you agree to our Privacy Policy

                      The developer cloud

                      Scale up as you grow — whether you're running one virtual machine or ten thousand.

                      Get started for free

                      Sign up and get $200 in credit for your first 60 days with DigitalOcean.*

                      *This promotional offer applies to new accounts only.