Single intent to let user take picture OR pick image from gallery in Android

44,740

Solution 1

UPDATE: The other answer, using EXTRA_INITIAL_INTENTS, is a better one at this point. At the time I wrote my answer, EXTRA_INITIAL_INTENTS did not yet exist, as it was added in API Level 5.

So is there a way to combine both or am I going to have to offer a menu to do one or the other from within my application?

Write your own gallery that has the features you desire.

I would think a menu would be simpler.

Seems like it would be a common use case...surely I'm missing something?

The developer next to you will think the gallery should allow you to pick from the local gallery or else hop out to Flickr to make a selection from there. Another developer will think the camera should not only allow to "take a picture" via the camera but to "take a picture" via choosing something from the gallery, inverting things from the way you envision it. Yet another developer will think that the gallery should allow picking from the local gallery, or Flickr, or the camera, or a network-attached webcam. Still another developer will think that the gallery is stupid and users should just pick files via a file explorer. And so on.

All of this in an environment (mobile phones) where flash for the OS is at a premium.

Hence, IMHO, it is not completely shocking that the core Android team elected to provide building blocks for you to assemble as you see fit, rather than trying to accommodate every possible pattern.

Solution 2

You can try doing something like this:

// ...
// Within your enclosing Class
// ...
private static final int SELECT_PICTURE = 1;

// ... 

Intent pickIntent = new Intent();
pickIntent.setType("image/*");
pickIntent.setAction(Intent.ACTION_GET_CONTENT);

Intent takePhotoIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

String pickTitle = "Select or take a new Picture"; // Or get from strings.xml
Intent chooserIntent = Intent.createChooser(pickIntent, pickTitle);
chooserIntent.putExtra
(
  Intent.EXTRA_INITIAL_INTENTS, 
  new Intent[] { takePhotoIntent }
);

startActivityForResult(chooserIntent, SELECT_PICTURE);

To see how to handle the activitiy's result, please refer to this question


Note: a critical point is how to determine whether the camera or gallery was used. That is shown in this code example: https://stackoverflow.com/a/12347567/294884

Solution 3

You can proceed in this way in your Activity:

private static final int REQUEST_CODE_PICTURE= 1;

    /**
     * Click on View to change photo. Sets into View of your layout, android:onClick="clickOnPhoto"
     * @param view View
     */
    public void clickOnPhoto(View view) {
        Intent pickIntent = new Intent();
        pickIntent.setType("image/*");
        pickIntent.setAction(Intent.ACTION_GET_CONTENT);
        Intent takePhotoIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        String pickTitle = "Take or select a photo";
        Intent chooserIntent = Intent.createChooser(pickIntent, pickTitle);
        chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[] { takePhotoIntent });
        startActivityForResult(chooserIntent, REQUEST_CODE_PICTURE);
    }

Then, add always in your Activity the method onActivityResult:

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == REQUEST_CODE_PICTURE && resultCode == Activity.RESULT_OK) {
            if (data == null) {
                return;
            }
            try {
                InputStream inputStream = getContentResolver().openInputStream(data.getData());
                Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
                imgPhoto.setImageBitmap(bitmap);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }
    }

Solution 4

My Answer is almost identical to @Macarse solution but I also add an additional intent to show gallery apps (Ex: Google Photos) and is written in Kotlin:

val REQUEST_CODE_GET_IMAGE = 101

private fun addProfileImage() {
    val pickImageFileIntent = Intent()
    pickImageFileIntent.type = "image/*"
    pickImageFileIntent.action = Intent.ACTION_GET_CONTENT

    val pickGalleryImageIntent = Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI)

    val captureCameraImageIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)

    val pickTitle = "Capture from camera or Select from gallery the Profile photo"
    val chooserIntent = Intent.createChooser(pickImageFileIntent, pickTitle)
    chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, arrayOf(captureCameraImageIntent, pickGalleryImageIntent))
    startActivityForResult(chooserIntent, REQUEST_CODE_GET_IMAGE)
}

Extract image from result intent:

private var imageTempFile: File? = null
private var imageMimeType: String? = null

private fun extractImage(intent: Intent?) {
    val imageUri = intent?.data
    imageUri?.let {
        Glide.with(this)
                .load(imageUri)
                .into(profileImageCiv)
        imageTempFile = MediaUtils.copyContentFromUriToCacheFile(this, imageUri, Settings.DIRECTORY_CACHE_TEMP_PROFILE_IMAGE)
        imageMimeType = MediaUtils.getMimeType(this, imageUri)
    } ?: run {
        intent?.extras?.get("data")?.let { bitmap ->    // Bitmap was returned as raw bitmap
            Glide.with(this)
                    .load(bitmap)
                    .into(profileImageCiv)
            imageTempFile = MediaUtils.writeBitmapToCacheFile(this, bitmap as Bitmap, Settings.DIRECTORY_CACHE_TEMP_PROFILE_IMAGE)
            imageMimeType = "image/jpeg"    // The bitmap was compressed as JPEG format. The bitmap itself doesn't have any format associated to it
        } ?: run {
            imageTempFile = null
            imageMimeType = null
            Log.e("Intent data is null.")
            Log.d("Error during photo selection")
        }
    }
}
Share:
44,740
Damian
Author by

Damian

Updated on July 08, 2022

Comments

  • Damian
    Damian almost 2 years

    I'm developing an app for Android 2.1 upwards. I want to enable my users to select a profile picture within my app (I'm not using the contacts framework).

    The ideal solution would be to fire an intent that enables the user to select an image from the gallery, but if an appropriate image is not available then use the camera to take a picture (or vice-versa i.e. allow user to take picture but if they know they already have a suitable image already, let them drop into the gallery and pick said image).

    Currently I can do one or the other but not both.

    If I go directly into camera mode using MediaStore.ACTION_IMAGE_CAPTURE then there is no option to drop into the gallery.

    If I go directly to the gallery using Intent.ACTION_PICK then I can pick an image but if I click the camera button (in top right hand corner of gallery) then a new camera intent is fired. So, any picture that is taken is not returned directly to my application. (Sure you can press the back button to drop back into the gallery and select image from there but this is an extra unnecessary step and is not at all intuitive).

    So is there a way to combine both or am I going to have to offer a menu to do one or the other from within my application? Seems like it would be a common use case...surely I'm missing something?

  • Damian
    Damian about 14 years
    OK, thanks Mark. As an avid reader of your commonsware books, I'll take that as a definitive answer. For now, I'll implement a menu in my app and will keep my fingers crossed that G produce something for me as part of the anticipated Froyo release next month. The Gallery app is so nice on 2.1, I'd love to be able to reuse / customise it for this scenario.
  • CommonsWare
    CommonsWare about 14 years
    Yeah, your notion (ACTION_PICK followed by camera icon would take a picture, add it, and return the picture) certainly isn't crazy. You might want to poke a bit at the pre-Froyo code for Gallery and see what they're doing there.
  • hackbod
    hackbod about 14 years
    Generally for the case where you want to say "i want the user to select for me X kind of content of some type," you would use GET_CONTENT. For example here you would use GET_CONTENT with type image/*, which would first present the user a list of things from which they can select images (gallery, camera, and whatever other apps have images to offer).
  • Justin
    Justin over 12 years
    @hackbod... Except that doing that does not provide an option to use the camera. Taking a picture requires the MediaStore.ACTION_IMAGE_CAPTURE action and choosing an existing image requires the Intent.ACTION_PICK action... Thus they essentially become mutually exclusive without doing what CommonsWare suggested
  • retromuz
    retromuz over 10 years
    I think this should be the most helpful answer! I tried it myself and works seamlessly.
  • Bob Barbara
    Bob Barbara about 10 years
    Unfortunately, this does not work if the EXTRA_INITIAL_INTENTS resolve to more than one activity, because they are not merged in the original chooser. Instead, a system item appears in the chooser that, when selected, opens up another chooser where the user can select the application to handle the EXTRA_INITIAL_INTENTS. Since you are not able to control how many application are installed to perform a certain task, this solution is just wishful thinking, flawed to all practical uses.
  • Christian
    Christian about 10 years
    @GiulioPiancastelli even if you are right with your logic in practical usage this solution is very helpful. A user having two Camera-applications might have selected one as default already and then will not get the second chooser. In the case where the extra intent is the camera it fits the needs imo.
  • Fattie
    Fattie almost 10 years
    @Macarse your question link is wrong dude! Catastrophe! :)
  • Fattie
    Fattie almost 10 years
    Another similar awesome answer, with more detail. Shows how to determine if camera or gallery stackoverflow.com/a/12347567/294884
  • sky91
    sky91 over 8 years
    In onActivityResult, only work for pickIntent but not work for takePhotoIntent
  • Forrest Bice
    Forrest Bice over 8 years
    Does anyone know how to avoid the "system item" @GiulioPiancastelli is referring to? For me this only appears to be an issue on Android M (6.x). Any pointers would be very much appreciated!
  • LizG
    LizG over 6 years
    Using this, how can I get the photo to display in portrait mode? If I take a pic in portrait mode, it displays in landscape mode.
  • Dario Brux
    Dario Brux over 6 years
    Use Glide or Picasso to display the image in the correct way