Google Photos vs Stock Gallery - Intent Picker
Asked Answered
C

2

5

In my app, I allow the user to pick a profile image from their gallery, like so:

enter image description here

When You click the first option, on my Android 5.0 device, I get this:

enter image description here

If I use the normal Gallery app that is based off the AOSP project, everything works fine and dandy. However, the Photo's app appears to use a different intent.

Here is how I handle code for the normal gallery:

Intent photoPickerIntent = new Intent(
                    Intent.ACTION_PICK,
                    android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
            photoPickerIntent.setType("image/*");
            photoPickerIntent.putExtra("crop", "true");
            photoPickerIntent.putExtra("outputX", 300);
            photoPickerIntent.putExtra("outputY", 300);
            photoPickerIntent.putExtra("aspectX", 1);
            photoPickerIntent.putExtra("aspectY", 1);
            photoPickerIntent.putExtra("scale", true);
            photoPickerIntent.putExtra(MediaStore.EXTRA_OUTPUT, getTempUri());
            photoPickerIntent.putExtra("outputFormat",
                    Bitmap.CompressFormat.JPEG.toString());
            startActivityForResult(photoPickerIntent, RESULT_LOAD_IMAGE);

And then the intent result handler:

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

    if (requestCode == RESULT_LOAD_IMAGE
            && resultCode == Activity.RESULT_OK) {

        if (data != null) {

            tempFile = getTempFile();

            filePath = Environment.getExternalStorageDirectory() + "/"
                    + "temporary_holder.jpg";
            Log.d("LOAD REQUEST filePath", filePath);

            Bitmap selectedImage = BitmapFactory.decodeFile(filePath);
            iPP.setImageBitmap(selectedImage);

        }

        imagePath = filePath;
        new UploadImage().execute();
    }
}

Some of the helper functions from above:

   private static Uri getTempUri() {
        return Uri.fromFile(getTempFile());
    }

    private static File getTempFile() {

        if (Environment.getExternalStorageState().equals(
                Environment.MEDIA_MOUNTED)) {

            File file = new File(Environment.getExternalStorageDirectory(),
                    "temporary_holder.jpg");
            try {
                file.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }

            return file;
        }
        return null;
    }

Some of that is probably not needed to show but I included it all in case it is interfering.

When I use Google Photos to pick a photo, my ImageView is blank (instead of filling with selected pick). The image is not selected and I can't go to the manual cropping view like I have it set with the Gallery. So basically, nothing happens.


NEW CODE IN RESPONSE TO ANSWER

        photoPickerIntent = new Intent(Intent.ACTION_GET_CONTENT);
        photoPickerIntent.setType("image/*");
        photoPickerIntent.putExtra("crop", "true");
        photoPickerIntent.putExtra("outputX", 300);
        photoPickerIntent.putExtra("outputY", 300);
        photoPickerIntent.putExtra("aspectX", 1);
        photoPickerIntent.putExtra("aspectY", 1);
        photoPickerIntent.putExtra("scale", true);
        photoPickerIntent.putExtra(MediaStore.EXTRA_OUTPUT, getTempUri());
        photoPickerIntent.putExtra("outputFormat",
                Bitmap.CompressFormat.JPEG.toString());
        startActivityForResult(photoPickerIntent, RESULT_LOAD_IMAGE);



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



    if (resultCode == Activity.RESULT_OK) {

        if (data != null) {

            Log.i("data", data.toString());

            switch (requestCode) {

                case RESULT_LOAD_IMAGE:

                    Log.i("RESULT_LOAD_IMAGE", "MARK");
                    // Received an image from the picker, now send an Intent for cropping
                    final String CROP_ACTION = "com.android.camera.action.CROP";
                    Intent photoCropIntent = new Intent(CROP_ACTION);
                    photoCropIntent.putExtra("crop", "true");
                    photoCropIntent.putExtra("aspectX", 1);
                    photoCropIntent.putExtra("aspectY", 1);
                    photoCropIntent.putExtra("outputX", 300);
                    photoCropIntent.putExtra("outputY", 300);
                    photoCropIntent.putExtra(MediaStore.EXTRA_OUTPUT, getTempUri());
                    photoCropIntent.putExtra("outputFormat",
                            Bitmap.CompressFormat.JPEG.toString());
                    photoCropIntent.setData(data.getData());

                    startActivityForResult(photoPickerIntent, RESULT_CROP_IMAGE);

                    break;

                case RESULT_CROP_IMAGE:

                    Log.i("RESULT_CROP_IMAGE", "MARK");

                    tempFile = getTempFile();
                    imagePath = Environment.getExternalStorageDirectory() + "/"   + "temporary_holder.jpg";
                    Log.i("imagePath", imagePath);

                    Uri selectedImageURI = data.getData();
                    InputStream image_stream;
                    try {
                        image_stream = getActivity().getContentResolver().openInputStream(selectedImageURI);
                        Bitmap bitmap = BitmapFactory.decodeStream(image_stream);
                        iPP.setImageBitmap(bitmap);
                    } catch (FileNotFoundException e) {
                        e.printStackTrace();
                    }
                    new UploadImage().execute();

                    break;

                default:
                    // Handle default case
            }
        }

    }

}

The above code isn't working either. I tried to make it resemble the answer below. What happens is:

I click "Choose from Gallery". And it does not give me a choice anymore, now it opens directly from the stock Gallery (that is not a big deal). I click on the image, and it ... appears to start the same intent over again -- it brings back the gallery wanting me to pick another image: instead of the Cropping Activity. Then after the second time, it will set my ImageView with the selected file.

Cachet answered 10/7, 2014 at 23:49 Comment(2)
how did you solved this problem ?Mitinger
@Cachet @Mitinger Here is a solution to the second part of the question in case someone is starting ACTION_GET_INTENT from an activity with android:launchMode=singleInstanceRelict
C
5

Solution

First, update the photoPickerIntent to use ACTION_GET_CONTENT, and remove the extras related to cropping, since cropping will be handled by another Intent later:

        Intent photoPickerIntent = new Intent(Intent.ACTION_GET_CONTENT);
        photoPickerIntent.setType("image/*");
        // Do not include the extras related to cropping here;
        // this Intent is for selecting the image only.
        startActivityForResult(photoPickerIntent, RESULT_LOAD_IMAGE);

Then, onActivityResult() will have to handle two results: RESULT_LOAD_IMAGE will send another intent for the crop, and RESULT_CROP_IMAGE will continue processing as you did before:

    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (resultCode == Activity.RESULT_OK) {
            if (data != null) {
                switch (requestCode) {
                case RESULT_LOAD_IMAGE:
                    // Received an image from the picker, now send an Intent for cropping
                    final String CROP_ACTION = "com.android.camera.action.CROP";
                    Intent photoCropIntent = new Intent(CROP_ACTION);
                    photoCropIntent.setData(data.getData());

                    // TODO: first get this running without extras, then test each one here
                    startActivityForResult(photoCropIntent, RESULT_CROP_IMAGE);
                    break;
                case RESULT_CROP_IMAGE:
                    // Received the cropped image, continue processing.  Note that this 
                    // is just copied and pasted from your question and may have 
                    // omissions.
                    tempFile = getTempFile();

                    filePath = Environment.getExternalStorageDirectory() + "/"
                            + "temporary_holder.jpg";
                    Log.d("LOAD REQUEST filePath", filePath);

                    Bitmap selectedImage = BitmapFactory.decodeFile(filePath);
                    iPP.setImageBitmap(selectedImage);

                    imagePath = filePath;
                    new UploadImage().execute();
                    break;
                default:
                    // Handle default case
                }
            }
        }

Note that although I've tested parts of this code, I haven't tested this entire block of code at runtime. If it doesn't work right out-of-the-box, though, it should be pretty close. Please comment if you have any questions or issues, and I'll update my answer accordingly.

Background

On an Android 5.0.1 (API 21) device with both AOSP Gallery2 (com.android.gallery3d) and Photos installed, I ran your Intent. I was prompted to choose between Gallery or Photos.

When I chose Photos, a picker was presented, and I picked an image. I was immediately returned to my Activity, with no cropping options.

When I chose Gallery, a picker was presented, and I picked an image. I was then prompted to choose an app for cropping. Both Photos and Gallery were presented as options for cropping.

Here's the interesting log output when choosing Gallery:

// Send the Intent
3-07 15:20:10.347      719-817/? I/ActivityManager﹕ Displayed android/com.android.internal.app.ResolverActivity: +127ms

// Choose Gallery
03-07 15:20:27.762      719-735/? I/ActivityManager﹕ START u0 {act=android.intent.action.PICK typ=image/* flg=0x3000000 cmp=com.android.gallery3d/.app.GalleryActivity (has extras)} from uid 10084 on display 0
// (fixing highlighting after MIME type on previous line) */
03-07 15:20:27.814  22967-22967/? W/GalleryActivity﹕ action PICK is not supported
03-07 15:20:27.814  22967-22967/? V/StateManager﹕ startState class com.android.gallery3d.app.AlbumSetPage
03-07 15:20:27.967      719-817/? I/ActivityManager﹕ Displayed com.android.gallery3d/.app.GalleryActivity: +190ms

// Pick an image
3-07 15:21:45.993  22967-22967/? V/StateManager﹕ startStateForResult class com.android.gallery3d.app.AlbumPage, 1
03-07 15:21:46.011  22967-22967/? D/AlbumPage﹕ onSyncDone: ********* result=0
03-07 15:21:46.034  22967-24132/? I/GLRootView﹕ layout content pane 1080x1701 (compensation 0)
03-07 15:21:48.447     719-1609/? I/ActivityManager﹕ START u0 {act=com.android.camera.action.CROP dat=content://media/external/images/media/1000 flg=0x2000000 cmp=android/com.android.internal.app.ResolverActivity (has extras)} from uid 10100 on display 0

First, note W/GalleryActivity﹕ action PICK is not supported. I'm not sure why it works, but according to Dianne Hackborn, "...you should consider ACTION_PICK deprecated. The modern action is ACTION_GET_CONTENT which is much better supported..." I've addressed this in my solution above.

The good news is, however, it seems that after picking an image, .putExtra("crop", "true"); causes Gallery to send another Intent for cropping. And the log clearly shows the action and data to use.

I tested this cropping intent, and I was prompted to choose an app for cropping, just like before. And again, both Photos and Gallery were presented as options, and they both brought up cropping interfaces.

It seems that although Photos supports cropping by Intent, it just ignores the extras relating to cropping in ACTION_PICK, whereas Gallery responds by sending another Intent after picking an image. Regardless, having the details of a working cropping Intent leads to the solution above.

Cairngorm answered 7/3, 2015 at 7:19 Comment(15)
Ok, I tried to incorporate the code but still having new issues. I added my new code at bottom of answer. with explanation of what is happening. Can you take a look? Also, not sure if I need this code .putExtra("crop", "true");?Cachet
The first sentence of my answer says, "First, update the photoPickerIntent to use ACTION_GET_CONTENT, and remove the extras related to cropping". Since the cropping activity has been pushed entirely to the second Intent (CROP_ACTION), the first one (ACTION_GET_CONTENT) should be clean. I included a TODO: on the second Intent regarding adding the extras there, but I recommend working out the bugs first without them, adding them once everything else is running smoothly. I'll check the rest of your code in a bit.Cairngorm
I updated my answer to clarify a bit, and aside from the extras, nothing else stands out in the rest of your code. Again, I recommend leaving them out entirely until you've got the two intents doing what they're supposed to do, then testing them one-by-one with each application in the picker/chooser so that you can decide whether to include them.Cairngorm
Question, was there a typo in your answer? When you said ` startActivityForResult(photoPickerIntent, RESULT_CROP_IMAGE);` is that first intent suppose to be this photoCropIntent? I cleaned things up adn got a where crash. Then I changed this possible Typo, and no crash, but I get a toast that says Cannot Load Image. That Toast is coming from the gallery I think not my code. I am using the new Moto X btw.,Cachet
Yep, that was a typo, sorry! I updated the answer. As for the Toast you're getting, have you checked the URI being passed to the Intent through data.getData() to make sure it's valid? On my side, passing the URI directly from the result of the first Intent worked fine, prompting me to choose between Gallery and Photos. Both apps brought up their cropping activities with the image.Cairngorm
Yeah, I am logging the data returned and I get Intent { }. this happens I believe as a result of executing startActivityForResult(photoCropIntent, RESULT_CROP_IMAGE);. I think it is also at this time, and because of the empty Intent I get "Cannot Load the Image!" Toast.Cachet
Did you remove the extras? Your first Intent should have only the type: Intent photoPickerIntent = new Intent(Intent.ACTION_GET_CONTENT); photoPickerIntent.setType("image/*"); startActivityForResult(photoPickerIntent, RESULT_LOAD_IMAGE);Cairngorm
If you're not receiving a Uri from this ACTION_GET_CONTENT Intent, then try using another app to select the image. If it launches directly into an app, then it's either the only app you have installed that handles Intents of that type, or it's set as default. The default can be cleared in Settings | Apps | <app name>Cairngorm
Yes all extras are removed. I have cleared defaults on the Stock Gallery and Google+ (For Photos). But the intent goes in the stock gallery everytime. I am pretty sure Photos should be picking this up too but it is not. Even so, the Stock Gallery on Moto X must be very close to AOSP version since they don't add much to their skin. Yet, empty intent... I am wondering if I need the URI because my old method was to do this cropIntent.putExtra(MediaStore.EXTRA_OUTPUT, getTempUri()); which copies a temp image to your SD. I read you don't need data returned in this situation. Unsure though.Cachet
Have you tried testing this on another device/emulator? Also you can try replacing ACTION_GET_CONTENT with ACTION_PICK again, since it worked for you before: Intent photoPickerIntent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI); photoPickerIntent.setType("image/*"); startActivityForResult(photoPickerIntent, RESULT_LOAD_IMAGE);Cairngorm
I tried it on an NVidia Shield tablet and it doens't even have a Gallery to pick from. Only Google Photos where it crashed. I'm just going to give you 100 point but can't mark correct until I get this working. Thanks for your help again. I am gonna have to move on to some other issues...Cachet
Thanks! When you decide to revisit it, please do comment with any other questions you may have.Cairngorm
@KickingLettuce I am facing same issue. did you find the way to solve this problem ?Mitinger
@Mitinger Sadly no . I abandoned the project basically after low usage.Cachet
@KickingLettuce i solved the porblem and also posted my solution below. If it seems good, please approve my post below https://mcmap.net/q/535566/-google-photos-vs-stock-gallery-intent-pickerMitinger
M
2

I have solved this problem this way.

Pick image:

private void pickUserImage() {

    if (doHavePermission()) {
        Intent photoPickerIntent = new Intent(Intent.ACTION_PICK,
                android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
        photoPickerIntent.setType("image/*");
        photoPickerIntent.putExtra("crop", "true");
        photoPickerIntent.putExtra("scale", true);
        photoPickerIntent.putExtra("outputX", 256);
        photoPickerIntent.putExtra("outputY", 256);
        photoPickerIntent.putExtra("aspectX", 1);
        photoPickerIntent.putExtra("aspectY", 1);
        photoPickerIntent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
        photoPickerIntent.putExtra(MediaStore.EXTRA_OUTPUT, getTempUri());
        startActivityForResult(photoPickerIntent, PICK_FROM_GALLERY);
    }
}

used method getTempUri() is

private Uri getTempUri() {
    return Uri.fromFile(getTempFile());
}

private File getTempFile() {

    if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {

        File file = new File(Environment.getExternalStorageDirectory(),TEMP_PHOTO_FILE);
        try {
            file.createNewFile();
        } catch (IOException e) {
            Log.printStackTrace(e);
        }

        return file;
    } else {

        return null;
    }
}

get the picked image

    @Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (resultCode == Activity.RESULT_OK) {
        if (requestCode == PICK_FROM_GALLERY) {
            if (data!=null) {

                Uri selectedImageUri = data.getData();
                //String tempPath = getPath(selectedImageUri, UserProfileActivity.this);
                String url = data.getData().toString();
                if (url.startsWith("content://com.google.android.apps.photos.content")){
                    try {
                        InputStream is = getContentResolver().openInputStream(selectedImageUri);
                        if (is != null) {
                            Bitmap selectedImage = BitmapFactory.decodeStream(is);
                            //You can use this bitmap according to your purpose or Set bitmap to imageview
                            if (selectedImage != null) {
                                isImageUpdated = true;
                                isImageUpdated = true;
                                Bitmap resizedBitmap = null;
                                if (selectedImage.getWidth() >= 256 && selectedImage.getHeight() >= 256) {
                                    resizedBitmap = Bitmap.createBitmap(selectedImage,
                                            selectedImage.getWidth() - 128,
                                            selectedImage.getHeight() - 128,
                                            256, 256);
                                }
                                if (resizedBitmap != null) {
                                    imageViewUserImage.setImageDrawable(ImageHelper.getRoundedCornerImage(resizedBitmap, 20));
                                } else {
                                    imageViewUserImage.setImageDrawable(ImageHelper.getRoundedCornerImage(selectedImage, 20));
                                }
                            }
                        }
                    } catch (FileNotFoundException e) {
                        Log.printStackTrace(e);
                    }
                } else {
                    File tempFile = getTempFile();
                    String filePath = Environment.getExternalStorageDirectory() + "/" + TEMP_PHOTO_FILE;
                    Log.d(TAG, "path " + filePath);

                    Bitmap selectedImage = BitmapFactory.decodeFile(filePath);
                    if (selectedImage != null) {
                        isImageUpdated = true;
                        imageViewUserImage.setImageDrawable(ImageHelper.getRoundedCornerImage(selectedImage, 20));
                    }
                    if (tempFile.exists()) {
                        tempFile.delete();
                    }
                }
            }
        }
    }
    super.onActivityResult(requestCode, resultCode, data);
}

Image converted to rounded corner image as follows:

public class ImageHelper {

    public static RoundedBitmapDrawable getRoundedCornerImage(Bitmap bitmap, int cornerRadius) {
        RoundedBitmapDrawable dr = RoundedBitmapDrawableFactory.create(null, bitmap);
        dr.setCornerRadius(cornerRadius);
        return dr;
    }
}

Its too late to answer but it can help someone else.

Mitinger answered 26/5, 2017 at 5:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.