I am trying to open an image / picture in the Gallery built-in app from inside my application.
I have a URI of the picture (the picture is located on the SD card).
This is a complete solution. I've just updated this example code with the information provided in the answer below by @mad. Also check the solution below from @Khobaib explaining how to deal with picasa images.
I've just reviewed my original answer and created a simple Android Studio project you can checkout from github and import directly on your system.
https://github.com/hanscappelle/SO-2169649
(note that the multiple file selection still needs work)
With support for images from file explorers thanks to user mad.
public class BrowsePictureActivity extends Activity {
// this is the action code we use in our intent,
// this way we know we're looking at the response from our own action
private static final int SELECT_PICTURE = 1;
private String selectedImagePath;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
findViewById(R.id.Button01)
.setOnClickListener(new OnClickListener() {
public void onClick(View arg0) {
// in onCreate or any event where your want the user to
// select a file
Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent,
"Select Picture"), SELECT_PICTURE);
}
});
}
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_OK) {
if (requestCode == SELECT_PICTURE) {
Uri selectedImageUri = data.getData();
selectedImagePath = getPath(selectedImageUri);
}
}
}
/**
* helper to retrieve the path of an image URI
*/
public String getPath(Uri uri) {
// just some safety built in
if( uri == null ) {
// TODO perform some logging or show user feedback
return null;
}
// try to retrieve the image from the media store first
// this will only work for images selected from gallery
String[] projection = { MediaStore.Images.Media.DATA };
Cursor cursor = managedQuery(uri, projection, null, null, null);
if( cursor != null ){
int column_index = cursor
.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
String path = cursor.getString(column_index);
cursor.close();
return path;
}
// this is our fallback here
return uri.getPath();
}
}
Since someone requested that information in a comment and it's better to have information gathered.
Set an extra parameter EXTRA_ALLOW_MULTIPLE
on the intent:
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
And in the Result handling check for that parameter:
if (Intent.ACTION_SEND_MULTIPLE.equals(data.getAction()))
&& Intent.hasExtra(Intent.EXTRA_STREAM)) {
// retrieve a collection of selected images
ArrayList<Parcelable> list = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
// iterate over these images
if( list != null ) {
for (Parcelable parcel : list) {
Uri uri = (Uri) parcel;
// TODO handle the images one by one here
}
}
}
Note that this is only supported by API level 18+.
this is my revisit to this topic, gathering all the information here, plus from other relevant stack overflow questions. It returns images from some provider, while handling out-of-memory conditions and image rotation. It supports gallery, picasa and file managers, like drop box. Usage is simple: as input, the constructor receives the content resolver and the uri. The output is the final bitmap.
/**
* Creates resized images without exploding memory. Uses the method described in android
* documentation concerning bitmap allocation, which is to subsample the image to a smaller size,
* close to some expected size. This is required because the android standard library is unable to
* create a reduced size image from an image file using memory comparable to the final size (and
* loading a full sized multi-megapixel picture for processing may exceed application memory budget).
*/
public class UserPicture {
static int MAX_WIDTH = 600;
static int MAX_HEIGHT = 800;
Uri uri;
ContentResolver resolver;
String path;
Matrix orientation;
int storedHeight;
int storedWidth;
public UserPicture(Uri uri, ContentResolver resolver) {
this.uri = uri;
this.resolver = resolver;
}
private boolean getInformation() throws IOException {
if (getInformationFromMediaDatabase())
return true;
if (getInformationFromFileSystem())
return true;
return false;
}
/* Support for gallery apps and remote ("picasa") images */
private boolean getInformationFromMediaDatabase() {
String[] fields = { Media.DATA, ImageColumns.ORIENTATION };
Cursor cursor = resolver.query(uri, fields, null, null, null);
if (cursor == null)
return false;
cursor.moveToFirst();
path = cursor.getString(cursor.getColumnIndex(Media.DATA));
int orientation = cursor.getInt(cursor.getColumnIndex(ImageColumns.ORIENTATION));
this.orientation = new Matrix();
this.orientation.setRotate(orientation);
cursor.close();
return true;
}
/* Support for file managers and dropbox */
private boolean getInformationFromFileSystem() throws IOException {
path = uri.getPath();
if (path == null)
return false;
ExifInterface exif = new ExifInterface(path);
int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_NORMAL);
this.orientation = new Matrix();
switch(orientation) {
case ExifInterface.ORIENTATION_NORMAL:
/* Identity matrix */
break;
case ExifInterface.ORIENTATION_FLIP_HORIZONTAL:
this.orientation.setScale(-1, 1);
break;
case ExifInterface.ORIENTATION_ROTATE_180:
this.orientation.setRotate(180);
break;
case ExifInterface.ORIENTATION_FLIP_VERTICAL:
this.orientation.setScale(1, -1);
break;
case ExifInterface.ORIENTATION_TRANSPOSE:
this.orientation.setRotate(90);
this.orientation.postScale(-1, 1);
break;
case ExifInterface.ORIENTATION_ROTATE_90:
this.orientation.setRotate(90);
break;
case ExifInterface.ORIENTATION_TRANSVERSE:
this.orientation.setRotate(-90);
this.orientation.postScale(-1, 1);
break;
case ExifInterface.ORIENTATION_ROTATE_270:
this.orientation.setRotate(-90);
break;
}
return true;
}
private boolean getStoredDimensions() throws IOException {
InputStream input = resolver.openInputStream(uri);
Options options = new Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(resolver.openInputStream(uri), null, options);
/* The input stream could be reset instead of closed and reopened if it were possible
to reliably wrap the input stream on a buffered stream, but it's not possible because
decodeStream() places an upper read limit of 1024 bytes for a reset to be made (it calls
mark(1024) on the stream). */
input.close();
if (options.outHeight <= 0 || options.outWidth <= 0)
return false;
storedHeight = options.outHeight;
storedWidth = options.outWidth;
return true;
}
public Bitmap getBitmap() throws IOException {
if (!getInformation())
throw new FileNotFoundException();
if (!getStoredDimensions())
throw new InvalidObjectException(null);
RectF rect = new RectF(0, 0, storedWidth, storedHeight);
orientation.mapRect(rect);
int width = (int)rect.width();
int height = (int)rect.height();
int subSample = 1;
while (width > MAX_WIDTH || height > MAX_HEIGHT) {
width /= 2;
height /= 2;
subSample *= 2;
}
if (width == 0 || height == 0)
throw new InvalidObjectException(null);
Options options = new Options();
options.inSampleSize = subSample;
Bitmap subSampled = BitmapFactory.decodeStream(resolver.openInputStream(uri), null, options);
Bitmap picture;
if (!orientation.isIdentity()) {
picture = Bitmap.createBitmap(subSampled, 0, 0, options.outWidth, options.outHeight,
orientation, false);
subSampled.recycle();
} else
picture = subSampled;
return picture;
}
}
References:
I went through the solution from @hcpl & @mad. hcpl's solution supports well for local image in the gallery & mad provided a better solution on top of that - it helps to load OI/Astro/Dropbox image as well. But in my app, while working on picasa library that's now integrated in Android Gallery, both solution fail.
I searched & analyzed a bit & eventually have come with a better & elegant solution that overcomes this limitation. Thanks to Dimitar Darazhanski for his blog, that helped me in this case, I modified a bit to make it easier to understand. Here is my solution goes -
public class BrowsePicture extends Activity {
//YOU CAN EDIT THIS TO WHATEVER YOU WANT
private static final int SELECT_PICTURE = 1;
private String selectedImagePath;
//ADDED
private String filemanagerstring;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
((Button) findViewById(R.id.Button01))
.setOnClickListener(new OnClickListener() {
public void onClick(View arg0) {
// in onCreate or any event where your want the user to
// select a file
Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent,
"Select Picture"), SELECT_PICTURE);
}
});
}
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_OK) {
if (requestCode == SELECT_PICTURE) {
Uri selectedImageUri = data.getData();
Log.d("URI VAL", "selectedImageUri = " + selectedImageUri.toString());
selectedImagePath = getPath(selectedImageUri);
if(selectedImagePath!=null){
// IF LOCAL IMAGE, NO MATTER IF ITS DIRECTLY FROM GALLERY (EXCEPT PICASSA ALBUM),
// OR OI/ASTRO FILE MANAGER. EVEN DROPBOX IS SUPPORTED BY THIS BECAUSE DROPBOX DOWNLOAD THE IMAGE
// IN THIS FORM - file:///storage/emulated/0/Android/data/com.dropbox.android/...
System.out.println("local image");
}
else{
System.out.println("picasa image!");
loadPicasaImageFromGallery(selectedImageUri);
}
}
}
}
// NEW METHOD FOR PICASA IMAGE LOAD
private void loadPicasaImageFromGallery(final Uri uri) {
String[] projection = { MediaColumns.DATA, MediaColumns.DISPLAY_NAME };
Cursor cursor = getContentResolver().query(uri, projection, null, null, null);
if(cursor != null) {
cursor.moveToFirst();
int columnIndex = cursor.getColumnIndex(MediaColumns.DISPLAY_NAME);
if (columnIndex != -1) {
new Thread(new Runnable() {
// NEW THREAD BECAUSE NETWORK REQUEST WILL BE MADE THAT WILL BE A LONG PROCESS & BLOCK UI
// IF CALLED IN UI THREAD
public void run() {
try {
Bitmap bitmap = android.provider.MediaStore.Images.Media.getBitmap(getContentResolver(), uri);
// THIS IS THE BITMAP IMAGE WE ARE LOOKING FOR.
} catch (Exception ex) {
ex.printStackTrace();
}
}
}).start();
}
}
cursor.close();
}
public String getPath(Uri uri) {
String[] projection = { MediaColumns.DATA};
Cursor cursor = getContentResolver().query(uri, projection, null, null, null);
if(cursor != null) {
//HERE YOU WILL GET A NULLPOINTER IF CURSOR IS NULL
//THIS CAN BE, IF YOU USED OI FILE MANAGER FOR PICKING THE MEDIA
cursor.moveToFirst();
int columnIndex = cursor.getColumnIndexOrThrow(MediaColumns.DATA);
String filePath = cursor.getString(columnIndex);
cursor.close();
return filePath;
}
else
return uri.getPath(); // FOR OI/ASTRO/Dropbox etc
}
Check it & let me know if there's some issue with it. I have tested it & it works well in every case.
Hope this will help everyone.
There are two useful tutorials about image picker with downloadable source code here:
How to Create Android Image Picker
How to Select and Crop Image on Android
However, the app will be forced to close sometime, you can fix it by adding android:configChanges attribute into main activity in Manifest file like as:
<activity android:name=".MainActivity"
android:label="@string/app_name" android:configChanges="keyboardHidden|orientation" >
It seems that the camera API lost control with orientation so this will help it. :)
Please find the answer for the selecting single image from gallery
import android.app.Activity;
import android.net.Uri;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
public class PickImage extends Activity {
Button btnOpen, btnGet, btnPick;
TextView textInfo1, textInfo2;
ImageView imageView;
private static final int RQS_OPEN_IMAGE = 1;
private static final int RQS_GET_IMAGE = 2;
private static final int RQS_PICK_IMAGE = 3;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.image_pick);
btnOpen = (Button)findViewById(R.id.open);
btnGet = (Button)findViewById(R.id.get);
btnPick = (Button)findViewById(R.id.pick);
textInfo1 = (TextView)findViewById(R.id.info1);
textInfo2 = (TextView)findViewById(R.id.info2);
imageView = (ImageView) findViewById(R.id.image);
btnOpen.setOnClickListener(btnOpenOnClickListener);
btnGet.setOnClickListener(btnGetOnClickListener);
btnPick.setOnClickListener(btnPickOnClickListener);
}
View.OnClickListener btnOpenOnClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("image/*");
startActivityForResult(intent, RQS_OPEN_IMAGE);
}
};
View.OnClickListener btnGetOnClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_GET_CONTENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("image/*");
startActivityForResult(intent, RQS_OPEN_IMAGE);
}
};
View.OnClickListener btnPickOnClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(Intent.ACTION_PICK,
android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(intent, RQS_PICK_IMAGE);
}
};
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == Activity.RESULT_OK) {
if (requestCode == RQS_OPEN_IMAGE ||
requestCode == RQS_GET_IMAGE ||
requestCode == RQS_PICK_IMAGE) {
imageView.setImageBitmap(null);
textInfo1.setText("");
textInfo2.setText("");
Uri mediaUri = data.getData();
textInfo1.setText(mediaUri.toString());
String mediaPath = mediaUri.getPath();
textInfo2.setText(mediaPath);
//display the image
try {
InputStream inputStream = getBaseContext().getContentResolver().openInputStream(mediaUri);
Bitmap bm = BitmapFactory.decodeStream(inputStream);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
byte[] byteArray = stream.toByteArray();
imageView.setImageBitmap(bm);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
}
}
basis with the above code, I reflected the code like below, may be it's more suitable:
public String getPath(Uri uri) {
String selectedImagePath;
//1:MEDIA GALLERY --- query from MediaStore.Images.Media.DATA
String[] projection = { MediaStore.Images.Media.DATA };
Cursor cursor = managedQuery(uri, projection, null, null, null);
if(cursor != null){
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
selectedImagePath = cursor.getString(column_index);
}else{
selectedImagePath = null;
}
if(selectedImagePath == null){
//2:OI FILE Manager --- call method: uri.getPath()
selectedImagePath = uri.getPath();
}
return selectedImagePath;
}