Related
I want to take photo from fragment but if I use follow codes onActivityResult Intent data return null. I didn't figure out why Intent data return null?
CameraFragement.java
private void takePictureIntent() {
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(getActivity().getPackageManager()) != null) {
File photoFile = null;
try {
photoFile = createImageFile();
} catch (IOException e) {
e.printStackTrace();
}
if (photoFile != null) {
Uri photoURI = FileProvider.getUriForFile(getContext(),
"com.example.android.fileprovider",
photoFile);
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
}
}
}
String imagePath;
private File createImageFile() throws IOException {
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = "JPEG_" + timeStamp + "_";
File storageDir = getActivity().getExternalFilesDir(Environment.DIRECTORY_PICTURES);
File image = File.createTempFile(
imageFileName, /* prefix */
".jpg", /* suffix */
storageDir /* directory */
);
imagePath = image.getAbsolutePath();
return image;
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) { // this Intent data return null
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == Activity.RESULT_OK) {
Intent intent = new Intent(getActivity(), PostActivity.class);
intent.setData(data.getData());
startActivity(intent);
}
}
If I use this follow code instead of above takePictureIntent Intent data return with extras but data.getData() return null. I don't want to return data with extras. I don't want to Bitmap data. I need datas Uri form. I need data.getData(). This follow code used to before but now it's not work. I update my application always with Android SDK. I think this is the reason but what is the solution?
private void takePictureIntent() {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(intent, REQUEST_IMAGE_CAPTURE);
}
Edit: I tried almost everything but Intent data still return null. I am using follow code with first code example for now. You can use this, it's works fine. If you have a solution for Intent data please don't forget to notice me.
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == Activity.RESULT_OK) {
File file = new File(imagePath);
Uri uri = Uri.fromFile(file);
Intent intent = new Intent(getActivity(), PostActivity.class);
intent.setData(uri);
startActivity(intent);
}
//capture image
public void requestTakePhoto() {
//SettingsManager.sharedInstance().TAKE_PHOTO = true;
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (intent.resolveActivity(getActivity().getPackageManager()) != null) {
final File photoFile;
try {
photoFile = createImageFile();
}
catch (IOException e) {
// Error occurred while creating the File
Log.e("ChatActivity", "Unable to create photo file", e);
return;
}
// Continue only if the File was successfully created
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(photoFile));
startActivityForResult(intent, REQ_TAKE_PHOTO);
}
}
//save captured image
private Uri mPhotoLocation;
private File createImageFile() throws IOException {
final String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(newDate());
final String imageFileName = timeStamp + "filename";
final File storageDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
final File image = File.createTempFile(imageFileName, ".jpg", storageDir);
mPhotoLocation = Uri.fromFile(image);
return image;
}
//handle result
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data){
switch (requestCode) {
case REQ_PICK_IMAGE:
if (resultCode == Activity.RESULT_OK && data.getData() != null) {
Uri uri =data.getData();
//do your work with data
// String filePath = getRealPathFromUri(uri);
//if (filePath != null && !filePath.isEmpty()) {
// Uri fileUri = Uri.parse(filePath);
//sendImageMessage(fileUri);
}
}
break;
case REQ_TAKE_PHOTO:
if (resultCode == Activity.RESULT_OK ) {
//do your work with data
String filePath = getRealPathFromUri(mPhotoLocation);
if (filePath != null && !filePath.isEmpty()) {
Uri fileUri = Uri.parse(filePath);
sendImageMessage(fileUri);
}
mPhotoLocation = null;
}
break;
case REQ_FILE_ATTACHMENT:
if (resultCode == Activity.RESULT_OK && data.getData() != null) {
Uri uri = data.getData();
//String filePath = getRealPathFromUri(uri);
//if (filePath != null && !filePath.isEmpty()) {
//Uri fileUri = Uri.parse(filePath);
//sendFileMessage(fileUri);
}
}
break;
default:
super.onActivityResult(requestCode, resultCode, data);
break;
}
}
//get realpath
public String getRealPathFromUri(final Uri uri) {
// DocumentProvider
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && DocumentsContract.isDocumentUri(getActivity(), uri)) {
// ExternalStorageProvider
if (isExternalStorageDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
if ("primary".equalsIgnoreCase(type)) {
return Environment.getExternalStorageDirectory() + "/" + split[1];
}
}
// DownloadsProvider
else if (isDownloadsDocument(uri)) {
final String id = DocumentsContract.getDocumentId(uri);
final Uri contentUri = ContentUris.withAppendedId(
Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
return getDataColumn(getActivity(), contentUri, null, null);
}
// MediaProvider
else if (isMediaDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
Uri contentUri = null;
if ("image".equals(type)) {
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
} else if ("video".equals(type)) {
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
} else if ("audio".equals(type)) {
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
}
final String selection = "_id=?";
final String[] selectionArgs = new String[]{
split[1]
};
return getDataColumn(getActivity(), contentUri, selection, selectionArgs);
}
}
// MediaStore (and general)
else if ("content".equalsIgnoreCase(uri.getScheme())) {
// Return the remote address
if (isGooglePhotosUri(uri))
return uri.getLastPathSegment();
return getDataColumn(getActivity(), uri, null, null);
}
// File
else if ("file".equalsIgnoreCase(uri.getScheme())) {
return uri.getPath();
}
return null;
}
private String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) {
Cursor cursor = null;
final String column = "_data";
final String[] projection = {
column
};
try {
cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
null);
if (cursor != null && cursor.moveToFirst()) {
final int index = cursor.getColumnIndexOrThrow(column);
return cursor.getString(index);
}
} finally {
if (cursor != null)
cursor.close();
}
return null;
}
private boolean isExternalStorageDocument(Uri uri) {
return "com.android.externalstorage.documents".equals(uri.getAuthority());
}
private boolean isDownloadsDocument(Uri uri) {
return "com.android.providers.downloads.documents".equals(uri.getAuthority());
}
private boolean isMediaDocument(Uri uri) {
return "com.android.providers.media.documents".equals(uri.getAuthority());
}
private boolean isGooglePhotosUri(Uri uri) {
return "com.google.android.apps.photos.content".equals(uri.getAuthority());
}
the requestcode is lost in the fragment, you need to use activity context:
getActivity().startActivityForResult(intent, REQUEST_IMAGE_CAPTURE);
This library will help you to fetch image from Camera or Storage to view in Activity or Fragment.
Its provide very simple methods t do that,
https://github.com/coomar2841/image-chooser-library
So basically what i am trying to achieve is opening the Gallery in Android and let the user select multiple images. Now this question has been asked frequently but i'm not satisfied with the answers. Mainly because i found something interesting in de docs in my IDE (i come back on this later) and thereby i don't want to use a custom adapter but just the vanilla one.
Now my code for selecting one image is:
Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent,"Select Picture"), 1);
Now People on SO and other websites wil tell you you have 2 options:
1) Do not use ACTION_GET_CONTENT but ACTION_SEND_MULTIPLE instead.
This one doesn't work. This one is according to the docs for sending files and not retrieving and that's exactly what it does. When using ACTION_SEND_MULTIPLE i got a window opened at my device where i have to select an application to send my data to. That's not what i want, so i wonder how people got this achieved with this solution.. Do i miss something?
2) Implement an custom Gallery. Now this is my last option i will consider because imho it's not what i am searching for because i have to style it myself AND why the heck you just can't select multiple images in the vanilla gallery?
There must be an option for this.. Now the interesting thing what i'v found is this:
I found this in the docs description of ACTION_GET_CONTENT.
If the caller can handle multiple returned items (the user performing
multiple selection), then it can specify EXTRA_ALLOW_MULTIPLE to
indicate this.
This is pretty interesting. Here they are referring it to the use case where a user can select multiple items?
Later on they say in the docs:
You may use EXTRA_ALLOW_MULTIPLE to allow the user to select multiple
items.
So this is pretty obvious right? This is what i need. But my following question is: Where can i put this EXTRA_ALLOW_MULTIPLE? The sad thing is that i can't find this no where in the developers.android guide and also is this not defined as a constant in the INTENT class.
Anybody can help me out with this EXTRA_ALLOW_MULTIPLE?
The EXTRA_ALLOW_MULTIPLE option is set on the intent through the Intent.putExtra() method:
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
Your code above should look like this:
Intent intent = new Intent();
intent.setType("image/*");
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent,"Select Picture"), 1);
Note: the EXTRA_ALLOW_MULTIPLE option is only available in Android API 18 and higher.
Define these variables in the class:
int PICK_IMAGE_MULTIPLE = 1;
String imageEncoded;
List<String> imagesEncodedList;
Let's Assume that onClick on a button it should open gallery to select images
Intent intent = new Intent();
intent.setType("image/*");
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent,"Select Picture"), PICK_IMAGE_MULTIPLE);
Then you should override onActivityResult Method
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
try {
// When an Image is picked
if (requestCode == PICK_IMAGE_MULTIPLE && resultCode == RESULT_OK
&& null != data) {
// Get the Image from data
String[] filePathColumn = { MediaStore.Images.Media.DATA };
imagesEncodedList = new ArrayList<String>();
if(data.getData()!=null){
Uri mImageUri=data.getData();
// Get the cursor
Cursor cursor = getContentResolver().query(mImageUri,
filePathColumn, null, null, null);
// Move to first row
cursor.moveToFirst();
int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
imageEncoded = cursor.getString(columnIndex);
cursor.close();
} else {
if (data.getClipData() != null) {
ClipData mClipData = data.getClipData();
ArrayList<Uri> mArrayUri = new ArrayList<Uri>();
for (int i = 0; i < mClipData.getItemCount(); i++) {
ClipData.Item item = mClipData.getItemAt(i);
Uri uri = item.getUri();
mArrayUri.add(uri);
// Get the cursor
Cursor cursor = getContentResolver().query(uri, filePathColumn, null, null, null);
// Move to first row
cursor.moveToFirst();
int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
imageEncoded = cursor.getString(columnIndex);
imagesEncodedList.add(imageEncoded);
cursor.close();
}
Log.v("LOG_TAG", "Selected Images" + mArrayUri.size());
}
}
} else {
Toast.makeText(this, "You haven't picked Image",
Toast.LENGTH_LONG).show();
}
} catch (Exception e) {
Toast.makeText(this, "Something went wrong", Toast.LENGTH_LONG)
.show();
}
super.onActivityResult(requestCode, resultCode, data);
}
NOTE THAT:
the gallery doesn't give you the ability to select multi-images so we here open all images studio that you can select multi-images from them.
and don't forget to add the permissions to your manifest
VERY IMPORTANT:
getData(); to get one single image and I've stored it here in imageEncoded String
if the user select multi-images then
they should be stored in the list
So you have to check which is null to use the other
Wish you have a nice try and to others
A lot of these answers have similarities but are all missing the most important part which is in onActivityResult, check if data.getClipData is null before checking data.getData
The code to call the file chooser:
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image/*"); //allows any image file type. Change * to specific extension to limit it
//**The following line is the important one!
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
startActivityForResult(Intent.createChooser(intent, "Select Picture"), SELECT_PICTURES); //SELECT_PICTURES is simply a global int used to check the calling intent in onActivityResult
The code to get all of the images selected:
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(requestCode == SELECT_PICTURES) {
if(resultCode == Activity.RESULT_OK) {
if(data.getClipData() != null) {
int count = data.getClipData().getItemCount(); //evaluate the count before the for loop --- otherwise, the count is evaluated every loop.
for(int i = 0; i < count; i++) {
Uri imageUri = data.getClipData().getItemAt(i).getUri();
//do something with the image (save it to some directory or whatever you need to do with it here)
}
}
} else if(data.getData() != null) {
String imagePath = data.getData().getPath();
//do something with the image (save it to some directory or whatever you need to do with it here)
}
}
}
Note that Android's chooser has Photos and Gallery available on some devices. Photos allows multiple images to be selected. Gallery allows just one at a time.
I hope this answer isn't late. Because the gallery widget doesn't support multiple selection by default, but you can custom the gridview which accepted your multiselect intent. The other option is to extend the gallery view and add in your own code to allow multiple selection.
This is the simple library can do it: https://github.com/luminousman/MultipleImagePick
Update:
From #ilsy's comment, CustomGalleryActivity in this library use manageQuery, which is deprecated, so it should be changed to getContentResolver().query() and cursor.close() like this answer
Initialize instance:
private String imagePath;
private List<String> imagePathList;
In onActivityResult You have to write this, If-else 2 block. One for single image and another for multiple image.
if (requestCode == GALLERY_CODE && resultCode == RESULT_OK && data != null) {
imagePathList = new ArrayList<>();
if (data.getClipData() != null) {
int count = data.getClipData().getItemCount();
for (int i=0; i<count; i++) {
Uri imageUri = data.getClipData().getItemAt(i).getUri();
getImageFilePath(imageUri);
}
}
else if (data.getData() != null) {
Uri imgUri = data.getData();
getImageFilePath(imgUri);
}
}
Most important part, Get Image Path from uri:
public void getImageFilePath(Uri uri) {
File file = new File(uri.getPath());
String[] filePath = file.getPath().split(":");
String image_id = filePath[filePath.length - 1];
Cursor cursor = getContentResolver().query(android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI, null, MediaStore.Images.Media._ID + " = ? ", new String[]{image_id}, null);
if (cursor!=null) {
cursor.moveToFirst();
imagePath = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
imagePathList.add(imagePath);
cursor.close();
}
}
Hope this can help you.
2022 - The Android Jetpack Compose way
For selecting multiple images in the gallery with Android Jetpack Compose.
val launcherMultipleImages = rememberLauncherForActivityResult(
contract = ActivityResultContracts.GetMultipleContents(),
) { uriList: List<Uri> ->
// TODO
}
And then use launcherMultipleImages.launch("image/*") to start the images selection.
For example :
Button(onClick = { launcherMultipleImages.launch("image/*") }) {
Text(text = "Select images")
}
this works for multiple image selection. also tested in API 29,30 in Google photos.
private static final int PICK_IMAGE = 2;
Intent intent = new Intent(Intent.ACTION_PICK,
android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
startActivityForResult(Intent.createChooser(intent, "Select
images"),PICK_IMAGE);
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == PICK_IMAGE && resultCode == RESULT_OK) {
if(data.getClipData() != null) {
int count = data.getClipData().getItemCount();
for(int i = 0; i < count; i++) {
Uri imageUri = data.getClipData().getItemAt(i).getUri();
//do what do you want to do
}
}
else if(data.getData() != null) {
Uri selectedImageUri = data.getData();
//do what do you want to do
}
}
Define getContent as below;
val getContent =
registerForActivityResult(ActivityResultContracts.GetMultipleContents())
{ uriList ->
// todo
}
after you grant related permission run the below code
getContent.launch("images/*")
I got null from the Cursor.
Then found a solution to convert the Uri into Bitmap that works perfectly.
Here is the solution that works for me:
#Override
public void onActivityResult(int requestCode, int resultCode, #Nullable Intent data) {
{
if (resultCode == Activity.RESULT_OK) {
if (requestCode == YOUR_REQUEST_CODE) {
if (data != null) {
if (data.getData() != null) {
Uri contentURI = data.getData();
ex_one.setImageURI(contentURI);
Log.d(TAG, "onActivityResult: " + contentURI.toString());
try {
Bitmap bitmap = MediaStore.Images.Media.getBitmap(context.getContentResolver(), contentURI);
} catch (IOException e) {
e.printStackTrace();
}
} else {
if (data.getClipData() != null) {
ClipData mClipData = data.getClipData();
ArrayList<Uri> mArrayUri = new ArrayList<Uri>();
for (int i = 0; i < mClipData.getItemCount(); i++) {
ClipData.Item item = mClipData.getItemAt(i);
Uri uri = item.getUri();
try {
Bitmap bitmap = MediaStore.Images.Media.getBitmap(context.getContentResolver(), uri);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
}
}
}
Hi below code is working fine.
Cursor imagecursor1 = managedQuery(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, columns, null,
null, orderBy + " DESC");
this.imageUrls = new ArrayList<String>();
imageUrls.size();
for (int i = 0; i < imagecursor1.getCount(); i++) {
imagecursor1.moveToPosition(i);
int dataColumnIndex = imagecursor1
.getColumnIndex(MediaStore.Images.Media.DATA);
imageUrls.add(imagecursor1.getString(dataColumnIndex));
}
options = new DisplayImageOptions.Builder()
.showStubImage(R.drawable.stub_image)
.showImageForEmptyUri(R.drawable.image_for_empty_url)
.cacheInMemory().cacheOnDisc().build();
imageAdapter = new ImageAdapter(this, imageUrls);
gridView = (GridView) findViewById(R.id.PhoneImageGrid);
gridView.setAdapter(imageAdapter);
You want to more clarifications.
http://mylearnandroid.blogspot.in/2014/02/multiple-choose-custom-gallery.html
I also had the same issue. I also wanted so users could take photos easily while picking photos from the gallery. Couldn't find a native way of doing this therefore I decided to make an opensource project. It is much like MultipleImagePick but just better way of implementing it.
https://github.com/giljulio/android-multiple-image-picker
private static final RESULT_CODE_PICKER_IMAGES = 9000;
Intent intent = new Intent(this, SmartImagePicker.class);
startActivityForResult(intent, RESULT_CODE_PICKER_IMAGES);
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode){
case RESULT_CODE_PICKER_IMAGES:
if(resultCode == Activity.RESULT_OK){
Parcelable[] parcelableUris = data.getParcelableArrayExtra(ImagePickerActivity.TAG_IMAGE_URI);
//Java doesn't allow array casting, this is a little hack
Uri[] uris = new Uri[parcelableUris.length];
System.arraycopy(parcelableUris, 0, uris, 0, parcelableUris.length);
//Do something with the uris array
}
break;
default:
super.onActivityResult(requestCode, resultCode, data);
break;
}
}
Try this one IntentChooser. Just add some lines of code, I did the rest for you.
private void startImageChooserActivity() {
Intent intent = ImageChooserMaker.newChooser(MainActivity.this)
.add(new ImageChooser(true))
.create("Select Image");
startActivityForResult(intent, REQUEST_IMAGE_CHOOSER);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_IMAGE_CHOOSER && resultCode == RESULT_OK) {
List<Uri> imageUris = ImageChooserMaker.getPickMultipleImageResultUris(this, data);
}
}
PS: as mentioned at the answers above, EXTRA_ALLOW_MULTIPLE is only available for API >= 18. And some gallery apps don't make this feature available (Google Photos and Documents (com.android.documentsui) work.
// for choosing multiple images declare variables
int PICK_IMAGE_MULTIPLE = 2;
String realImagePath;
// After requesting FILE READ PERMISSION may be on button click
Intent intent = new Intent();
intent.setType("image/*");
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent,"Select Images"), PICK_IMAGE_MULTIPLE);
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);// FOR CHOOSING MULTIPLE IMAGES
try {
// When an Image is picked
if (requestCode == PICK_IMAGE_MULTIPLE && resultCode == RESULT_OK
&& null != data) {
if (data.getClipData() != null) {
int count = data.getClipData().getItemCount(); //evaluate the count before the for loop --- otherwise, the count is evaluated every loop.
for (int i = 0; i < count; i++) {
Uri imageUri = data.getClipData().getItemAt(i).getUri();
realImagePath = getPath(this, imageUri);
//do something with the image (save it to some directory or whatever you need to do with it here)
Log.e("ImagePath", "onActivityResult: " + realImagePath);
}
} else if (data.getData() != null) {
Uri imageUri = data.getData();
realImagePath = getPath(this, imageUri);
//do something with the image (save it to some directory or whatever you need to do with it here)
Log.e("ImagePath", "onActivityResult: " + realImagePath);
}
}
} catch (Exception e) {
Toast.makeText(this, "Something went wrong", Toast.LENGTH_LONG)
.show();
}
}
public static String getPath(final Context context, final Uri uri) {
// DocumentProvider
if (DocumentsContract.isDocumentUri(context, uri)) {
// ExternalStorageProvider
if (isExternalStorageDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
if ("primary".equalsIgnoreCase(type)) {
return Environment.getExternalStorageDirectory() + "/" + split[1];
}
// TODO handle non-primary volumes
}
// DownloadsProvider
else if (isDownloadsDocument(uri)) {
final String id = DocumentsContract.getDocumentId(uri);
final Uri contentUri = ContentUris.withAppendedId(
Uri.parse("content://downloads/public_downloads"), Long.parseLong(id));
return getDataColumn(context, contentUri, null, null);
}
// MediaProvider
else if (isMediaDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
Uri contentUri = null;
if ("image".equals(type)) {
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
} else if ("video".equals(type)) {
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
} else if ("audio".equals(type)) {
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
}
final String selection = "_id=?";
final String[] selectionArgs = new String[]{
split[1]
};
return getDataColumn(context, contentUri, selection, selectionArgs);
}
}
// MediaStore (and general)
else if ("content".equalsIgnoreCase(uri.getScheme())) {
return getDataColumn(context, uri, null, null);
}
// File
else if ("file".equalsIgnoreCase(uri.getScheme())) {
return uri.getPath();
}
return null;
}
/**
* Get the value of the data column for this Uri. This is useful for
* MediaStore Uris, and other file-based ContentProviders.
*
* #param context The context.
* #param uri The Uri to query.
* #param selection (Optional) Filter used in the query.
* #param selectionArgs (Optional) Selection arguments used in the query.
* #return The value of the _data column, which is typically a file path.
*/
public static String getDataColumn(Context context, Uri uri, String selection,
String[] selectionArgs) {
Cursor cursor = null;
final String column = "_data";
final String[] projection = {
column
};
try {
cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
null);
if (cursor != null && cursor.moveToFirst()) {
final int column_index = cursor.getColumnIndexOrThrow(column);
return cursor.getString(column_index);
}
} finally {
if (cursor != null)
cursor.close();
}
return null;
}
/**
* #param uri The Uri to check.
* #return Whether the Uri authority is ExternalStorageProvider.
*/
public static boolean isExternalStorageDocument(Uri uri) {
return "com.android.externalstorage.documents".equals(uri.getAuthority());
}
/**
* #param uri The Uri to check.
* #return Whether the Uri authority is DownloadsProvider.
*/
public static boolean isDownloadsDocument(Uri uri) {
return "com.android.providers.downloads.documents".equals(uri.getAuthority());
}
/**
* #param uri The Uri to check.
* #return Whether the Uri authority is MediaProvider.
*/
public static boolean isMediaDocument(Uri uri) {
return "com.android.providers.media.documents".equals(uri.getAuthority());
}
this worked perfectly for me credits: Get real path from URI, Android KitKat new storage access framework
For selecting multiple image from gallery
i.putExtra(Intent.EXTRA_ALLOW_MULTIPLE,true);
An Ultimate Solution for multiple image upload with camera option also for Android Lollipop to Android 10, SDK 30.
private static final int FILECHOOSER_RESULTCODE = 1;
private ValueCallback<Uri> mUploadMessage;
private ValueCallback<Uri[]> mUploadMessages;
private Uri mCapturedImageURI = null;
Add this to OnCreate of MainActivity
mWebView.setWebChromeClient(new WebChromeClient() {
// openFileChooser for Android 3.0+
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType){
mUploadMessage = uploadMsg;
openImageChooser();
}
// For Lollipop 5.0+ Devices
public boolean onShowFileChooser(WebView mWebView, ValueCallback<Uri[]> filePathCallback, WebChromeClient.FileChooserParams fileChooserParams) {
mUploadMessages = filePathCallback;
openImageChooser();
return true;
}
// openFileChooser for Android < 3.0
public void openFileChooser(ValueCallback<Uri> uploadMsg){
openFileChooser(uploadMsg, "");
}
//openFileChooser for other Android versions
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
openFileChooser(uploadMsg, acceptType);
}
private void openImageChooser() {
try {
File imageStorageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "FolderName");
if (!imageStorageDir.exists()) {
imageStorageDir.mkdirs();
}
File file = new File(imageStorageDir + File.separator + "IMG_" + String.valueOf(System.currentTimeMillis()) + ".jpg");
mCapturedImageURI = Uri.fromFile(file);
final Intent captureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, mCapturedImageURI);
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("image/*");
i.putExtra(Intent.EXTRA_ALLOW_MULTIPLE,true);
Intent chooserIntent = Intent.createChooser(i, "Image Chooser");
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Parcelable[]{captureIntent});
startActivityForResult(chooserIntent, FILECHOOSER_RESULTCODE);
} catch (Exception e) {
e.printStackTrace();
}
}
});
onActivityResult
public void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == FILECHOOSER_RESULTCODE) {
if (null == mUploadMessage && null == mUploadMessages) {
return;
}
if (null != mUploadMessage) {
handleUploadMessage(requestCode, resultCode, data);
} else if (mUploadMessages != null) {
handleUploadMessages(requestCode, resultCode, data);
}
}
}
private void handleUploadMessage(final int requestCode, final int resultCode, final Intent data) {
Uri result = null;
try {
if (resultCode != RESULT_OK) {
result = null;
} else {
// retrieve from the private variable if the intent is null
result = data == null ? mCapturedImageURI : data.getData();
}
} catch (Exception e) {
e.printStackTrace();
}
mUploadMessage.onReceiveValue(result);
mUploadMessage = null;
// code for all versions except of Lollipop
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
result = null;
try {
if (resultCode != RESULT_OK) {
result = null;
} else {
// retrieve from the private variable if the intent is null
result = data == null ? mCapturedImageURI : data.getData();
}
} catch (Exception e) {
Toast.makeText(getApplicationContext(), "activity :" + e, Toast.LENGTH_LONG).show();
}
mUploadMessage.onReceiveValue(result);
mUploadMessage = null;
}
} // end of code for all versions except of Lollipop
private void handleUploadMessages(final int requestCode, final int resultCode, final Intent data) {
Uri[] results = null;
try {
if (resultCode != RESULT_OK) {
results = null;
} else {
if (data != null) {
String dataString = data.getDataString();
ClipData clipData = data.getClipData();
if (clipData != null) {
results = new Uri[clipData.getItemCount()];
for (int i = 0; i < clipData.getItemCount(); i++) {
ClipData.Item item = clipData.getItemAt(i);
results[i] = item.getUri();
}
}
if (dataString != null) {
results = new Uri[]{Uri.parse(dataString)};
}
} else {
results = new Uri[]{mCapturedImageURI};
}
}
} catch (Exception e) {
e.printStackTrace();
}
mUploadMessages.onReceiveValue(results);
mUploadMessages = null;
}
For Multiple image selection and with selection limit restriction feature, use chintan369/MultiImagePicker library which is the latest of 2021 and supports Android 11 too. It is well documented and also demo is explained on youtube for use. It's very easy to add in the project, easy to use to call the library for image selection and getting results of selected images as Uri list and also you can request result list as absolute file path list.
I am trying to set an image to an imageview that a user selects from their gallery. However all the images seem to load to my imageview except when I select an image from the camera folder. It doesn't put the image into my imageview. Refer to the following image http://i.stack.imgur.com/h6QGG.jpg
Call of intent
Intent galleryIntent = new Intent(Intent.ACTION_PICK,
MediaStore.Images.Media.INTERNAL_CONTENT_URI);
// Start the Intent
Log.d(TAG,"STARTING ACTIVITY FOR RESULT");
startActivityForResult(galleryIntent, RESULT_LOAD_IMAGE);
OnActivityResult
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data){
Log.d(TAG,"IN ON ACTIVIY RESULT");
super.onActivityResult(requestCode, resultCode, data);
try {
Log.d(TAG, "IN INTERESTS ON ACTIVITY RESULT");
if (requestCode == RESULT_LOAD_IMAGE && resultCode == Activity.RESULT_OK && null != data) {
Uri selectedImage = data.getData();
String[] filePathColumn = {MediaStore.Images.Media.DATA};
Cursor cursor = getContext().getContentResolver().query(selectedImage,
filePathColumn, null, null, null);
cursor.moveToFirst();
int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
String picturePath = cursor.getString(columnIndex);
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
File file = new File(picturePath);
bmOptions.inJustDecodeBounds = true;
Bitmap original = BitmapFactory.decodeFile(picturePath);
file.createNewFile();
FileOutputStream ostream = new FileOutputStream(file);
Bitmap newBm = Bitmap.createScaledBitmap(original, 900, 1280, true);
newBm.compress(Bitmap.CompressFormat.JPEG, 90, ostream);
ostream.close();
BitmapFactory.decodeFile(picturePath, bmOptions);
iconpic.setImageBitmap(newBm);
Toast.makeText(getContext(), picturePath, Toast.LENGTH_SHORT)
.show();
cursor.close();
SharedPreferences appPrefs = getContext().getSharedPreferences("com.example.gcmclient_preferences", Context.MODE_PRIVATE);
SharedPreferences.Editor prefsEditor = appPrefs.edit();
prefsEditor.putString("iconPath", picturePath);
prefsEditor.commit();
// String picturePath contains the path of selected Image
}
}
catch (Exception e){
e.printStackTrace();
}
}
Hope this will work.
Uri imageUri;
File file;
public int CAMERA_INTENT_CALLED = 100;
public int GALLERY_INTENT_CALLED = 101;
public int GALLERY_KITKAT_INTENT_CALLED = 102;
if (Build.VERSION.SDK_INT < 19) {
Intent intent = new Intent();
intent.setType("image/jpeg");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent,
"Select image to promote"),
GALLERY_INTENT_CALLED);
} else {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("image/jpeg");
startActivityForResult(intent,
GALLERY_KITKAT_INTENT_CALLED);
}
onActivityResult
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == Activity.RESULT_OK) {
Uri originalUri = null;
if (requestCode == GALLERY_INTENT_CALLED) {
if (data.getData() != null) {
originalUri = data.getData();
String path = getRealPathFromURI(originalUri);
setImageBitmap(path);
originalUri = null;
}
} else if (requestCode == GALLERY_KITKAT_INTENT_CALLED) {
if (data.getData() != null) {
originalUri = data.getData();
String path = getPath(this, originalUri);
setImageBitmap(path);
originalUri = null;
}
} else if (requestCode == CAMERA_INTENT_CALLED) {
Uri selectedImage = imageUri;
try {
if (selectedImage != null) {
getContentResolver().notifyChange(selectedImage, null);
String path = getRealPathFromURI(selectedImage);
Log.e("Imagepath Camera", path);
setImageBitmap(path);
imageUri = null;
}
} catch (Exception e) {
Log.e("Camera", e.toString());
}
}
}
API <19 and above GalleryImage selected
private String getRealPathFromURI(Uri contentURI) {
Cursor cursor = getContentResolver().query(contentURI, null, null,
null, null);
if (cursor == null) { // Source is Dropbox or other similar local file
// path
return contentURI.getPath();
} else {
cursor.moveToFirst();
int idx = cursor
.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
return cursor.getString(idx);
}
}
KITKAT and above GalleryImage selected
public static String getPath(final Context context, final Uri uri) {
final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
// DocumentProvider
if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
// ExternalStorageProvider
if (isExternalStorageDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
if ("primary".equalsIgnoreCase(type)) {
return Environment.getExternalStorageDirectory() + "/"
+ split[1];
}
// TODO handle non-primary volumes
}
// DownloadsProvider
else if (isDownloadsDocument(uri)) {
final String id = DocumentsContract.getDocumentId(uri);
final Uri contentUri = ContentUris.withAppendedId(
Uri.parse("content://downloads/public_downloads"),
Long.valueOf(id));
return getDataColumn(context, contentUri, null, null);
}
// MediaProvider
else if (isMediaDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
Uri contentUri = null;
if ("image".equals(type)) {
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
} else if ("video".equals(type)) {
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
} else if ("audio".equals(type)) {
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
}
final String selection = "_id=?";
final String[] selectionArgs = new String[] { split[1] };
return getDataColumn(context, contentUri, selection,
selectionArgs);
}
}
// MediaStore (and general)
else if ("content".equalsIgnoreCase(uri.getScheme())) {
// Return the remote address
if (isGooglePhotosUri(uri))
return uri.getLastPathSegment();
return getDataColumn(context, uri, null, null);
}
// File
else if ("file".equalsIgnoreCase(uri.getScheme())) {
return uri.getPath();
}
return null;
}
public static boolean isExternalStorageDocument(Uri uri) {
return "com.android.externalstorage.documents".equals(uri
.getAuthority());
}
/**
* #param uri
* The Uri to check.
* #return Whether the Uri authority is DownloadsProvider.
*/
public static boolean isGooglePhotosUri(Uri uri) {
return "com.google.android.apps.photos.content".equals(uri
.getAuthority());
}
public static boolean isDownloadsDocument(Uri uri) {
return "com.android.providers.downloads.documents".equals(uri
.getAuthority());
}
/**
* #param uri
* The Uri to check.
* #return Whether the Uri authority is MediaProvider.
*/
public static boolean isMediaDocument(Uri uri) {
return "com.android.providers.media.documents".equals(uri
.getAuthority());
}
Before KitKat (or before the new Gallery) the Intent.ACTION_GET_CONTENT returned a URI like this
content://media/external/images/media/3951.
Using the ContentResolver and quering for
MediaStore.Images.Media.DATA returned the file URL.
In KitKat however the Gallery returns a URI (via "Last") like this:
content://com.android.providers.media.documents/document/image:3951
How do I handle this?
This requires no special permissions, and works with the Storage Access Framework, as well as the unofficial ContentProvider pattern (file path in _data field).
/**
* Get a file path from a Uri. This will get the the path for Storage Access
* Framework Documents, as well as the _data field for the MediaStore and
* other file-based ContentProviders.
*
* #param context The context.
* #param uri The Uri to query.
* #author paulburke
*/
public static String getPath(final Context context, final Uri uri) {
final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
// DocumentProvider
if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
// ExternalStorageProvider
if (isExternalStorageDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
if ("primary".equalsIgnoreCase(type)) {
return Environment.getExternalStorageDirectory() + "/" + split[1];
}
// TODO handle non-primary volumes
}
// DownloadsProvider
else if (isDownloadsDocument(uri)) {
final String id = DocumentsContract.getDocumentId(uri);
final Uri contentUri = ContentUris.withAppendedId(
Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
return getDataColumn(context, contentUri, null, null);
}
// MediaProvider
else if (isMediaDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
Uri contentUri = null;
if ("image".equals(type)) {
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
} else if ("video".equals(type)) {
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
} else if ("audio".equals(type)) {
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
}
final String selection = "_id=?";
final String[] selectionArgs = new String[] {
split[1]
};
return getDataColumn(context, contentUri, selection, selectionArgs);
}
}
// MediaStore (and general)
else if ("content".equalsIgnoreCase(uri.getScheme())) {
// Return the remote address
if (isGooglePhotosUri(uri))
return uri.getLastPathSegment();
return getDataColumn(context, uri, null, null);
}
// File
else if ("file".equalsIgnoreCase(uri.getScheme())) {
return uri.getPath();
}
return null;
}
/**
* Get the value of the data column for this Uri. This is useful for
* MediaStore Uris, and other file-based ContentProviders.
*
* #param context The context.
* #param uri The Uri to query.
* #param selection (Optional) Filter used in the query.
* #param selectionArgs (Optional) Selection arguments used in the query.
* #return The value of the _data column, which is typically a file path.
*/
public static String getDataColumn(Context context, Uri uri, String selection,
String[] selectionArgs) {
Cursor cursor = null;
final String column = "_data";
final String[] projection = {
column
};
try {
cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
null);
if (cursor != null && cursor.moveToFirst()) {
final int index = cursor.getColumnIndexOrThrow(column);
return cursor.getString(index);
}
} finally {
if (cursor != null)
cursor.close();
}
return null;
}
/**
* #param uri The Uri to check.
* #return Whether the Uri authority is ExternalStorageProvider.
*/
public static boolean isExternalStorageDocument(Uri uri) {
return "com.android.externalstorage.documents".equals(uri.getAuthority());
}
/**
* #param uri The Uri to check.
* #return Whether the Uri authority is DownloadsProvider.
*/
public static boolean isDownloadsDocument(Uri uri) {
return "com.android.providers.downloads.documents".equals(uri.getAuthority());
}
/**
* #param uri The Uri to check.
* #return Whether the Uri authority is MediaProvider.
*/
public static boolean isMediaDocument(Uri uri) {
return "com.android.providers.media.documents".equals(uri.getAuthority());
}
/**
* #param uri The Uri to check.
* #return Whether the Uri authority is Google Photos.
*/
public static boolean isGooglePhotosUri(Uri uri) {
return "com.google.android.apps.photos.content".equals(uri.getAuthority());
}
See an up-to-date version of this method here.
Try this:
if (Build.VERSION.SDK_INT <19){
Intent intent = new Intent();
intent.setType("image/jpeg");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent, getResources().getString(R.string.select_picture)),GALLERY_INTENT_CALLED);
} else {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("image/jpeg");
startActivityForResult(intent, GALLERY_KITKAT_INTENT_CALLED);
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode != Activity.RESULT_OK) return;
if (null == data) return;
Uri originalUri = null;
if (requestCode == GALLERY_INTENT_CALLED) {
originalUri = data.getData();
} else if (requestCode == GALLERY_KITKAT_INTENT_CALLED) {
originalUri = data.getData();
final int takeFlags = data.getFlags()
& (Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
// Check for the freshest data.
getContentResolver().takePersistableUriPermission(originalUri, takeFlags);
}
loadSomeStreamAsynkTask(originalUri);
}
Probably need
#SuppressLint("NewApi")
for
takePersistableUriPermission
Had the same problem, tried the solution above but though it worked generally, for some reason I was getting permission denial on Uri content provider for some images although I had the android.permission.MANAGE_DOCUMENTS permission added properly.
Anyway found other solution which is to force opening image gallery instead of KITKAT documents view with :
// KITKAT
i = new Intent(Intent.ACTION_PICK,android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(i, CHOOSE_IMAGE_REQUEST);
and then load the image:
Uri selectedImageURI = data.getData();
input = c.getContentResolver().openInputStream(selectedImageURI);
BitmapFactory.decodeStream(input , null, opts);
EDIT
ACTION_OPEN_DOCUMENT might require you to persist permissions flags etc and generally often results in Security Exceptions...
Other solution is to use the ACTION_GET_CONTENT combined with c.getContentResolver().openInputStream(selectedImageURI) which will work both on pre-KK and KK. Kitkat will use new documents view then and this solution will work with all apps like Photos, Gallery, File Explorer, Dropbox, Google Drive etc...) but remember that when using this solution you have to create image in your onActivityResult() and store it on SD Card for example. Recreating this image from saved uri on next app launch would throw Security Exception on content resolver even when you add permission flags as described in Google API docs (that's what happened when I did some testing)
Additionally the Android Developer API Guidelines suggest:
ACTION_OPEN_DOCUMENT is not intended to be a replacement for ACTION_GET_CONTENT. The one you should use depends on the needs of
your app:
Use ACTION_GET_CONTENT if you want your app to simply read/import
data. With this approach, the app imports a copy of the data, such as
an image file.
Use ACTION_OPEN_DOCUMENT if you want your app to have
long term, persistent access to documents owned by a document
provider. An example would be a photo-editing app that lets users edit
images stored in a document provider.
Just as Commonsware mentioned, you shouldn't assume, that the stream you get via ContentResolver is convertable into file.
What you really should do is to open the InputStream from the ContentProvider, then create a Bitmap out of it. And it works on 4.4 and earlier versions as well, no need for reflection.
//cxt -> current context
InputStream input;
Bitmap bmp;
try {
input = cxt.getContentResolver().openInputStream(fileUri);
bmp = BitmapFactory.decodeStream(input);
} catch (FileNotFoundException e1) {
}
Of course if you handle big images, you should load them with appropriate inSampleSize: http://developer.android.com/training/displaying-bitmaps/load-bitmap.html. But that's another topic.
I believe the responses already posted should get people going in the right direction. However here is what I did that made sense for the legacy code I was updating. The legacy code was using the URI from the gallery to change and then save the images.
Prior to 4.4 (and google drive), the URIs would look like this:
content://media/external/images/media/41
As stated in the question, they more often look like this:
content://com.android.providers.media.documents/document/image:3951
Since I needed the ability to save images and not disturb the already existing code, I just copied the URI from the gallery into the data folder of the app. Then originated a new URI from the saved image file in the data folder.
Here's the idea:
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
startActivityForResult(intent), CHOOSE_IMAGE_REQUEST);
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
File tempFile = new File(this.getFilesDir().getAbsolutePath(), "temp_image");
//Copy URI contents into temporary file.
try {
tempFile.createNewFile();
copyAndClose(this.getContentResolver().openInputStream(data.getData()),new FileOutputStream(tempFile));
}
catch (IOException e) {
//Log Error
}
//Now fetch the new URI
Uri newUri = Uri.fromFile(tempFile);
/* Use new URI object just like you used to */
}
Note - copyAndClose() just does file I/O to copy InputStream into a FileOutputStream. The code is not posted.
Just wanted to say that this answer is brilliant and I'm using it for a long time without problems. But some time ago I've stumbled upon a problem that DownloadsProvider returns URIs in format content://com.android.providers.downloads.documents/document/raw%3A%2Fstorage%2Femulated%2F0%2FDownload%2Fdoc.pdf and hence app is crashed with NumberFormatException as it's impossible to parse its uri segments as long. But raw: segment contains direct uri which can be used to retrieve a referenced file. So I've fixed it by replacing isDownloadsDocument(uri) if content with following:
final String id = DocumentsContract.getDocumentId(uri);
if (!TextUtils.isEmpty(id)) {
if (id.startsWith("raw:")) {
return id.replaceFirst("raw:", "");
}
try {
final Uri contentUri = ContentUris.withAppendedId(
Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
return getDataColumn(context, contentUri, null, null);
} catch (NumberFormatException e) {
Log.e("FileUtils", "Downloads provider returned unexpected uri " + uri.toString(), e);
return null;
}
}
I've combine multiple answers into one working solution that results with file path
Mime type is irrelevant for the example purpose.
Intent intent;
if(Build.VERSION.SDK_INT >= 19){
intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, false);
intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
}else{
intent = new Intent(Intent.ACTION_GET_CONTENT);
}
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setType("application/octet-stream");
if(isAdded()){
startActivityForResult(intent, RESULT_CODE);
}
Handling result
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if( requestCode == RESULT_CODE && resultCode == Activity.RESULT_OK) {
Uri uri = data.getData();
if (uri != null && !uri.toString().isEmpty()) {
if(Build.VERSION.SDK_INT >= 19){
final int takeFlags = data.getFlags() & Intent.FLAG_GRANT_READ_URI_PERMISSION;
//noinspection ResourceType
getActivity().getContentResolver()
.takePersistableUriPermission(uri, takeFlags);
}
String filePath = FilePickUtils.getSmartFilePath(getActivity(), uri);
// do what you need with it...
}
}
}
FilePickUtils
import android.annotation.SuppressLint;
import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
public class FilePickUtils {
private static String getPathDeprecated(Context ctx, Uri uri) {
if( uri == null ) {
return null;
}
String[] projection = { MediaStore.Images.Media.DATA };
Cursor cursor = ctx.getContentResolver().query(uri, projection, null, null, null);
if( cursor != null ){
int column_index = cursor
.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
return cursor.getString(column_index);
}
return uri.getPath();
}
public static String getSmartFilePath(Context ctx, Uri uri){
if (Build.VERSION.SDK_INT < 19) {
return getPathDeprecated(ctx, uri);
}
return FilePickUtils.getPath(ctx, uri);
}
#SuppressLint("NewApi")
public static String getPath(final Context context, final Uri uri) {
final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
// DocumentProvider
if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
// ExternalStorageProvider
if (isExternalStorageDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
if ("primary".equalsIgnoreCase(type)) {
return Environment.getExternalStorageDirectory() + "/" + split[1];
}
// TODO handle non-primary volumes
}
// DownloadsProvider
else if (isDownloadsDocument(uri)) {
final String id = DocumentsContract.getDocumentId(uri);
final Uri contentUri = ContentUris.withAppendedId(
Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
return getDataColumn(context, contentUri, null, null);
}
// MediaProvider
else if (isMediaDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
Uri contentUri = null;
if ("image".equals(type)) {
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
} else if ("video".equals(type)) {
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
} else if ("audio".equals(type)) {
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
}
final String selection = "_id=?";
final String[] selectionArgs = new String[] {
split[1]
};
return getDataColumn(context, contentUri, selection, selectionArgs);
}
}
// MediaStore (and general)
else if ("content".equalsIgnoreCase(uri.getScheme())) {
return getDataColumn(context, uri, null, null);
}
// File
else if ("file".equalsIgnoreCase(uri.getScheme())) {
return uri.getPath();
}
return null;
}
/**
* Get the value of the data column for this Uri. This is useful for
* MediaStore Uris, and other file-based ContentProviders.
*
* #param context The context.
* #param uri The Uri to query.
* #param selection (Optional) Filter used in the query.
* #param selectionArgs (Optional) Selection arguments used in the query.
* #return The value of the _data column, which is typically a file path.
*/
public static String getDataColumn(Context context, Uri uri, String selection,
String[] selectionArgs) {
Cursor cursor = null;
final String column = "_data";
final String[] projection = {
column
};
try {
cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
null);
if (cursor != null && cursor.moveToFirst()) {
final int column_index = cursor.getColumnIndexOrThrow(column);
return cursor.getString(column_index);
}
} finally {
if (cursor != null)
cursor.close();
}
return null;
}
/**
* #param uri The Uri to check.
* #return Whether the Uri authority is ExternalStorageProvider.
*/
public static boolean isExternalStorageDocument(Uri uri) {
return "com.android.externalstorage.documents".equals(uri.getAuthority());
}
/**
* #param uri The Uri to check.
* #return Whether the Uri authority is DownloadsProvider.
*/
public static boolean isDownloadsDocument(Uri uri) {
return "com.android.providers.downloads.documents".equals(uri.getAuthority());
}
/**
* #param uri The Uri to check.
* #return Whether the Uri authority is MediaProvider.
*/
public static boolean isMediaDocument(Uri uri) {
return "com.android.providers.media.documents".equals(uri.getAuthority());
}
}
Question
How to get an actual file path from a URI
Answer
To my knowledge, we don't need to get the file path from a URI because for most of the cases we can directly use the URI to get our work done (like 1. getting bitmap 2. Sending a file to the server, etc.)
1. Sending to the server
We can directly send the file to the server using just the URI.
Using the URI we can get InputStream, which we can directly send to the server using MultiPartEntity.
Example
/**
* Used to form Multi Entity for a URI (URI pointing to some file, which we got from other application).
*
* #param uri URI.
* #param context Context.
* #return Multi Part Entity.
*/
public MultipartEntity formMultiPartEntityForUri(final Uri uri, final Context context) {
MultipartEntity multipartEntity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE, null, Charset.forName("UTF-8"));
try {
InputStream inputStream = mContext.getContentResolver().openInputStream(uri);
if (inputStream != null) {
ContentBody contentBody = new InputStreamBody(inputStream, getFileNameFromUri(uri, context));
multipartEntity.addPart("[YOUR_KEY]", contentBody);
}
}
catch (Exception exp) {
Log.e("TAG", exp.getMessage());
}
return multipartEntity;
}
/**
* Used to get a file name from a URI.
*
* #param uri URI.
* #param context Context.
* #return File name from URI.
*/
public String getFileNameFromUri(final Uri uri, final Context context) {
String fileName = null;
if (uri != null) {
// Get file name.
// File Scheme.
if (ContentResolver.SCHEME_FILE.equals(uri.getScheme())) {
File file = new File(uri.getPath());
fileName = file.getName();
}
// Content Scheme.
else if (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
Cursor returnCursor =
context.getContentResolver().query(uri, null, null, null, null);
if (returnCursor != null && returnCursor.moveToFirst()) {
int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
fileName = returnCursor.getString(nameIndex);
returnCursor.close();
}
}
}
return fileName;
}
2. Getting a BitMap from a URI
If the URI is pointing to image then we will get bitmap, else null:
/**
* Used to create bitmap for the given URI.
* <p>
* 1. Convert the given URI to bitmap.
* 2. Calculate ratio (depending on bitmap size) on how much we need to subSample the original bitmap.
* 3. Create bitmap bitmap depending on the ration from URI.
* 4. Reference - http://stackoverflow.com/questions/3879992/how-to-get-bitmap-from-an-uri
*
* #param context Context.
* #param uri URI to the file.
* #param bitmapSize Bitmap size required in PX.
* #return Bitmap bitmap created for the given URI.
* #throws IOException
*/
public static Bitmap createBitmapFromUri(final Context context, Uri uri, final int bitmapSize) throws IOException {
// 1. Convert the given URI to bitmap.
InputStream input = context.getContentResolver().openInputStream(uri);
BitmapFactory.Options onlyBoundsOptions = new BitmapFactory.Options();
onlyBoundsOptions.inJustDecodeBounds = true;
onlyBoundsOptions.inDither = true;//optional
onlyBoundsOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;//optional
BitmapFactory.decodeStream(input, null, onlyBoundsOptions);
input.close();
if ((onlyBoundsOptions.outWidth == -1) || (onlyBoundsOptions.outHeight == -1)) {
return null;
}
// 2. Calculate ratio.
int originalSize = (onlyBoundsOptions.outHeight > onlyBoundsOptions.outWidth) ? onlyBoundsOptions.outHeight : onlyBoundsOptions.outWidth;
double ratio = (originalSize > bitmapSize) ? (originalSize / bitmapSize) : 1.0;
// 3. Create bitmap.
BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
bitmapOptions.inSampleSize = getPowerOfTwoForSampleRatio(ratio);
bitmapOptions.inDither = true;//optional
bitmapOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;//optional
input = context.getContentResolver().openInputStream(uri);
Bitmap bitmap = BitmapFactory.decodeStream(input, null, bitmapOptions);
input.close();
return bitmap;
}
/**
* For Bitmap option inSampleSize - We need to give value in power of two.
*
* #param ratio Ratio to be rounded of to power of two.
* #return Ratio rounded of to nearest power of two.
*/
private static int getPowerOfTwoForSampleRatio(final double ratio) {
int k = Integer.highestOneBit((int) Math.floor(ratio));
if (k == 0) return 1;
else return k;
}
Comments
Android doesn't provide any methods to get file path from a URI, and in most of the above answers we have hard coded some constants, which may break in feature release (sorry, I may be wrong).
Before going directly going to a solution of the getting file path from a URI, try if you can solve your use case with a URI and Android default methods.
Reference
https://developer.android.com/guide/topics/providers/content-provider-basics.html
https://developer.android.com/reference/android/content/ContentResolver.html
https://hc.apache.org/httpcomponents-client-ga/httpmime/apidocs/org/apache/http/entity/mime/content/InputStreamBody.html
This Android library handles the case changes in KitKat(including the oldere versions - 2.1+):
https://github.com/iPaulPro/aFileChooser
Use the String path = FileUtils.getPath(context, uri) to convert the returned Uri to a path string useable on all OS version.
See more about it here: https://stackoverflow.com/a/20559175/860488
For those who is still using #Paul Burke's code with Android SDK version 23 and above, if your project met the error saying that you are missing EXTERNAL_PERMISSION, and you are very sure you have already added user-permission in your AndroidManifest.xml file. That's because you may in Android API 23 or above and Google make it necessary to guarantee permission again while you make the action to access the file in runtime.
That means: If your SDK version is 23 or above, you are asked for READ & WRITE permission while you are selecting the picture file and want to know the URI of it.
And following is my code, in addition to Paul Burke's solution. I add these code and my project start to work fine.
private static final int REQUEST_EXTERNAL_STORAGE = 1;
private static final String[] PERMISSINOS_STORAGE = {
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
};
public static void verifyStoragePermissions(Activity activity) {
int permission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (permission != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(
activity,
PERMISSINOS_STORAGE,
REQUEST_EXTERNAL_STORAGE
);
}
}
And in your activity&fragment where you are asking for the URI:
private void pickPhotoFromGallery() {
CompatUtils.verifyStoragePermissions(this);
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
// startActivityForResult(intent, REQUEST_PHOTO_LIBRARY);
startActivityForResult(Intent.createChooser(intent, "éĉİç
§ç"),
REQUEST_PHOTO_LIBRARY);
}
In my case, CompatUtils.java is where I define the verifyStoragePermissions method (as static type so I can call it within other activity).
Also it should make more sense if you make an if state first to see whether the current SDK version is above 23 or not before you call the verifyStoragePermissions method.
This is what I do:
Uri selectedImageURI = data.getData(); imageFile = new File(getRealPathFromURI(selectedImageURI));
private String getRealPathFromURI(Uri contentURI) {
Cursor cursor = getContentResolver().query(contentURI, null, null, null, null);
if (cursor == null) { // Source is Dropbox or other similar local file path
return contentURI.getPath();
} else {
cursor.moveToFirst();
int idx = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
return cursor.getString(idx);
}
}
NOTE: managedQuery() method is deprecated, so I am not using it.
This answer is from m3n0R on question android get real path by Uri.getPath() and I claim no credit. I just thought that people who haven't solved this issue yet could use this.
I've tried several of the answers here, and I think I have a solution that will work every time and manages permissions as well.
It is based on the clever solution from LEO. This post should contain all the code you need to make this work, and it should work on any phone and Android version ;)
In order to have the ability to pick a file from an SD card, you'll need this in your manifest:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
Constants:
private static final int PICK_IMAGE = 456; // Whatever number you like
public static final int MY_PERMISSIONS_REQUEST_READ_EXTERNAL = 28528; // Whatever number you like
public static final String FILE_TEMP_NAME = "temp_image"; // Whatever file name you like
Check permission and launchImagePick if possible
if (ContextCompat.checkSelfPermission(getThis(),
Manifest.permission.READ_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(getThis(),
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
MY_PERMISSIONS_REQUEST_READ_EXTERNAL);
}
else {
launchImagePick();
}
Permission response
#Override
public void onRequestPermissionsResult(int requestCode,
#NonNull
String permissions[],
#NonNull
int[] grantResults) {
if (manageReadExternalPermissionResponse(this, requestCode, grantResults)) {
launchImagePick();
}
}
Manage permission response
public static boolean manageReadExternalPermissionResponse(final Activity activity, int requestCode, int[] grantResults) {
if (requestCode == MY_PERMISSIONS_REQUEST_READ_EXTERNAL) {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Permission was granted, yay! Do the
// contacts-related task you need to do.
return true;
} else if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_DENIED) {
boolean showRationale = ActivityCompat.shouldShowRequestPermissionRationale(activity,
Manifest.permission.READ_EXTERNAL_STORAGE);
if (!showRationale) {
// The user also CHECKED "never ask again".
// You can either enable some fall back,
// disable features of your app
// or open another dialog explaining
// again the permission and directing to
// the app setting.
} else {
// The user did NOT check "never ask again".
// This is a good place to explain the user
// why you need the permission and ask if he/she wants
// to accept it (the rationale).
}
} else {
// Permission denied, boo! Disable the
// functionality that depends on this permission.
}
}
return false;
}
Launch image pick
private void launchImagePick() {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
intent.addCategory(Intent.CATEGORY_OPENABLE);
startActivityForResult(intent, PICK_IMAGE);
// see onActivityResult
}
Manage Image pick response
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == PICK_IMAGE) {
if (resultCode == Activity.RESULT_OK) {
if (data != null && data.getData() != null) {
try {
InputStream inputStream = getContentResolver().openInputStream(data.getData())
if (inputStream != null) {
// No special persmission needed to store the file like that
FileOutputStream fos = openFileOutput(FILE_TEMP_NAME, Context.MODE_PRIVATE);
final int BUFFER_SIZE = 1 << 10 << 3; // 8 KiB buffer
byte[] buffer = new byte[BUFFER_SIZE];
int bytesRead = -1;
while ((bytesRead = inputStream.read(buffer)) > -1) {
fos.write(buffer, 0, bytesRead);
}
inputStream.close();
fos.close();
File tempImageFile = new File(getFilesDir()+"/"+FILE_TEMP_NAME);
// Do whatever you want with the File
// Delete when not needed anymore
deleteFile(FILE_TEMP_NAME);
}
}
catch (Exception e) {
e.printStackTrace();
}
} else {
// Error display
}
} else {
// The user did not select any image
}
}
}
That's all folks; this works for me on all the telephones I have.
If anyone's interested, I made a working Kotlin version for ACTION_GET_CONTENT:
var path: String = uri.path // uri = any content Uri
val databaseUri: Uri
val selection: String?
val selectionArgs: Array<String>?
if ("/document/image:" in path || "/document/image%3A" in path) {
// files selected from "Documents"
databaseUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
selection = "_id=?"
selectionArgs = arrayOf(DocumentsContract.getDocumentId(uri).split(":")[1])
} else { // files selected from all other sources, especially on Samsung devices
databaseUri = uri
selection = null
selectionArgs = null
}
try {
val projection = arrayOf(MediaStore.Images.Media.DATA,
MediaStore.Images.Media._ID,
MediaStore.Images.Media.ORIENTATION,
MediaStore.Images.Media.DATE_TAKEN) // some example data you can query
val cursor = context.contentResolver.query(databaseUri,
projection, selection, selectionArgs, null)
if (cursor.moveToFirst()) {
// do whatever you like with the data
}
cursor.close()
} catch (e: Exception) {
Log.e(TAG, e.message, e)
}
Please try to avoid using takePersistableUriPermission method because it raised runtime exception for me.
/**
* Select from gallery.
*/
public void selectFromGallery() {
if (Build.VERSION.SDK_INT < AppConstants.KITKAT_API_VERSION) {
Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
((Activity)mCalledContext).startActivityForResult(intent,AppConstants.GALLERY_INTENT_CALLED);
} else {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
((Activity)mCalledContext).startActivityForResult(intent, AppConstants.GALLERY_AFTER_KITKAT_INTENT_CALLED);
}
}
OnActivity for result to handle the image data:
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
//gallery intent result handling before kit-kat version
if(requestCode==AppConstants.GALLERY_INTENT_CALLED
&& resultCode == RESULT_OK) {
Uri selectedImage = data.getData();
String[] filePathColumn = {MediaStore.Images.Media.DATA};
Cursor cursor = getContentResolver().query(selectedImage,filePathColumn, null, null, null);
cursor.moveToFirst();
int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
String filePath = cursor.getString(columnIndex);
cursor.close();
photoFile = new File(filePath);
mImgCropping.startCropImage(photoFile,AppConstants.REQUEST_IMAGE_CROP);
}
//gallery intent result handling after kit-kat version
else if (requestCode == AppConstants.GALLERY_AFTER_KITKAT_INTENT_CALLED
&& resultCode == RESULT_OK) {
Uri selectedImage = data.getData();
InputStream input = null;
OutputStream output = null;
try {
//converting the input stream into file to crop the
//selected image from sd-card.
input = getApplicationContext().getContentResolver().openInputStream(selectedImage);
try {
photoFile = mImgCropping.createImageFile();
} catch (IOException e) {
e.printStackTrace();
}catch(Exception e) {
e.printStackTrace();
}
output = new FileOutputStream(photoFile);
int read = 0;
byte[] bytes = new byte[1024];
while ((read = input.read(bytes)) != -1) {
try {
output.write(bytes, 0, read);
} catch (IOException e) {
e.printStackTrace();
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
This is a total hack, but here's what I did...
So while playing with setting up a DocumentsProvider, I noticed that the sample code (in getDocIdForFile, around line 450) generates a unique id for a selected document based on the file's (unique) path relative to the specified root you give it (that is, what you set mBaseDir to on line 96).
So the URI ends up looking something like:
content://com.example.provider/document/root:path/to/the/file
As the docs say, it's assuming only a single root (in my case that's Environment.getExternalStorageDirectory() but you may use somewhere else... then it takes the file path, starting at the root, and makes it the unique ID, prepending "root:". So I can determine the path by eliminating the "/document/root:" part from uri.getPath(), creating an actual file path by doing something like this:
public void onActivityResult(int requestCode, int resultCode, Intent data) {
// check resultcodes and such, then...
uri = data.getData();
if (uri.getAuthority().equals("com.example.provider")) {
String path = Environment.getExternalStorageDirectory(0.toString()
.concat("/")
.concat(uri.getPath().substring("/document/root:".length())));
doSomethingWithThePath(path); }
else {
// another provider (maybe a cloud-based service such as GDrive)
// created this uri. So handle it, or don't. You can allow specific
// local filesystem providers, filter non-filesystem path results, etc.
}
I know. It's shameful, but it worked. Again, this relies on you using your own documents provider in your app to generate the document ID.
(Also, there's a better way to build the path that don't assume "/" is the path separator, etc. But you get the idea.)
This worked fine for me:
else if(requestCode == GALLERY_ACTIVITY_NEW && resultCode == Activity.RESULT_OK)
{
Uri uri = data.getData();
Log.i(TAG, "old uri = " + uri);
dumpImageMetaData(uri);
try {
ParcelFileDescriptor parcelFileDescriptor =
getContentResolver().openFileDescriptor(uri, "r");
FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
Log.i(TAG, "File descriptor " + fileDescriptor.toString());
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options);
options.inSampleSize =
BitmapHelper.calculateInSampleSize(options,
User.PICTURE_MAX_WIDTH_IN_PIXELS,
User.PICTURE_MAX_HEIGHT_IN_PIXELS);
options.inJustDecodeBounds = false;
Bitmap bitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options);
imageViewPic.setImageBitmap(bitmap);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream);
// get byte array here
byte[] picData = stream.toByteArray();
ParseFile picFile = new ParseFile(picData);
user.setProfilePicture(picFile);
}
catch(FileNotFoundException exc)
{
Log.i(TAG, "File not found: " + exc.toString());
}
}
Building up on Paul Burke's answer I faced many problems resolving external SD card's URI path as most of the suggested "built-in" functions return paths which do not get resolved to files.
However, this is my approach of his
// TODO handle non-primary volumes.
String resolvedPath = "";
File[] possibleExtSdComposites = context.getExternalFilesDirs(null);
for (File f : possibleExtSdComposites) {
// Reset final path
resolvedPath = "";
// Construct list of folders
ArrayList<String> extSdSplit = new ArrayList<>(Arrays.asList(f.getPath().split("/")));
// Look for folder "<your_application_id>"
int idx = extSdSplit.indexOf(BuildConfig.APPLICATION_ID);
// ASSUMPTION: Expected to be found at depth 2 (in this case ExtSdCard's root is /storage/0000-0000/) - e.g. /storage/0000-0000/Android/data/<your_application_id>/files
ArrayList<String> hierarchyList = new ArrayList<>(extSdSplit.subList(0, idx - 2));
// Construct list containing full possible path to the file
hierarchyList.add(tail);
String possibleFilePath = TextUtils.join("/", hierarchyList);
// If file is found --> success
if (idx != -1 && new File(possibleFilePath).exists()) {
resolvedPath = possibleFilePath;
break;
}
}
if (!resolvedPath.equals("")) {
return resolvedPath;
} else {
return null;
}
Note it depends on hierarchy which might be different on every phone manufacturer - I have not tested them all (it worked well so far on Xperia Z3 API 23 and Samsung Galaxy A3 API 23).
Please confirm if it does not perform well elsewhere.
for this type of uri
content://com.android.providers.media.documents/document/document%3A19298
or uri.getAuthority()is any of these
"com.google.android.apps.docs.storage".equals(uri.getAuthority()) || "com.google.android.apps.docs.storage.legacy".equals(uri.getAuthority());
use this function
private static String getDriveFilePath(Uri uri, Context context) {
Uri returnUri = uri;
Cursor returnCursor = context.getContentResolver().query(returnUri, null, null, null, null);
int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE);
returnCursor.moveToFirst();
String name = (returnCursor.getString(nameIndex));
String size = (Long.toString(returnCursor.getLong(sizeIndex)));
File file = new File(context.getCacheDir(), name);
try {
InputStream inputStream = context.getContentResolver().openInputStream(uri);
FileOutputStream outputStream = new FileOutputStream(file);
int read = 0;
int maxBufferSize = 1 * 1024 * 1024;
int bytesAvailable = inputStream.available();
//int bufferSize = 1024;
int bufferSize = Math.min(bytesAvailable, maxBufferSize);
final byte[] buffers = new byte[bufferSize];
while ((read = inputStream.read(buffers)) != -1) {
outputStream.write(buffers, 0, read);
}
Log.e("File Size", "Size " + file.length());
inputStream.close();
outputStream.close();
Log.e("File Path", "Path " + file.getPath());
Log.e("File Size", "Size " + file.length());
} catch (Exception e) {
Log.e("Exception", e.getMessage());
}
return file.getPath();
}
#paul burke's answer works fine for both camera and gallery pictures for API level 19 and above, but it doesn't work if your Android project's minimum SDK is set to below 19, and some answers referring above doesn't work for both gallery and camera. Well, I have modified #paul burke's code which works for API level below 19. Below is the code.
public static String getPath(final Context context, final Uri uri) {
final boolean isKitKat = Build.VERSION.SDK_INT >=
Build.VERSION_CODES.KITKAT;
Log.i("URI",uri+"");
String result = uri+"";
// DocumentProvider
// if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
if (isKitKat && (result.contains("media.documents"))) {
String[] ary = result.split("/");
int length = ary.length;
String imgary = ary[length-1];
final String[] dat = imgary.split("%3A");
final String docId = dat[1];
final String type = dat[0];
Uri contentUri = null;
if ("image".equals(type)) {
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
}
else if ("video".equals(type)) {
}
else if ("audio".equals(type)) {
}
final String selection = "_id=?";
final String[] selectionArgs = new String[] {
dat[1]
};
return getDataColumn(context, contentUri, selection, selectionArgs);
}
else
if ("content".equalsIgnoreCase(uri.getScheme())) {
return getDataColumn(context, uri, null, null);
}
// File
else if ("file".equalsIgnoreCase(uri.getScheme())) {
return uri.getPath();
}
return null;
}
public static String getDataColumn(Context context, Uri uri, String selection,
String[] selectionArgs) {
Cursor cursor = null;
final String column = "_data";
final String[] projection = {
column
};
try {
cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
null);
if (cursor != null && cursor.moveToFirst()) {
final int column_index = cursor.getColumnIndexOrThrow(column);
return cursor.getString(column_index);
}
}
finally {
if (cursor != null)
cursor.close();
}
return null;
}
The answer to your question is that you need to have permissions. Type the following code in your manifest.xml file:
<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="18" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
<uses-permission android:name="android.permission.WRITE_OWNER_DATA"></uses-permission>
<uses-permission android:name="android.permission.READ_OWNER_DATA"></uses-permission>`
It worked for me...
So basically what i am trying to achieve is opening the Gallery in Android and let the user select multiple images. Now this question has been asked frequently but i'm not satisfied with the answers. Mainly because i found something interesting in de docs in my IDE (i come back on this later) and thereby i don't want to use a custom adapter but just the vanilla one.
Now my code for selecting one image is:
Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent,"Select Picture"), 1);
Now People on SO and other websites wil tell you you have 2 options:
1) Do not use ACTION_GET_CONTENT but ACTION_SEND_MULTIPLE instead.
This one doesn't work. This one is according to the docs for sending files and not retrieving and that's exactly what it does. When using ACTION_SEND_MULTIPLE i got a window opened at my device where i have to select an application to send my data to. That's not what i want, so i wonder how people got this achieved with this solution.. Do i miss something?
2) Implement an custom Gallery. Now this is my last option i will consider because imho it's not what i am searching for because i have to style it myself AND why the heck you just can't select multiple images in the vanilla gallery?
There must be an option for this.. Now the interesting thing what i'v found is this:
I found this in the docs description of ACTION_GET_CONTENT.
If the caller can handle multiple returned items (the user performing
multiple selection), then it can specify EXTRA_ALLOW_MULTIPLE to
indicate this.
This is pretty interesting. Here they are referring it to the use case where a user can select multiple items?
Later on they say in the docs:
You may use EXTRA_ALLOW_MULTIPLE to allow the user to select multiple
items.
So this is pretty obvious right? This is what i need. But my following question is: Where can i put this EXTRA_ALLOW_MULTIPLE? The sad thing is that i can't find this no where in the developers.android guide and also is this not defined as a constant in the INTENT class.
Anybody can help me out with this EXTRA_ALLOW_MULTIPLE?
The EXTRA_ALLOW_MULTIPLE option is set on the intent through the Intent.putExtra() method:
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
Your code above should look like this:
Intent intent = new Intent();
intent.setType("image/*");
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent,"Select Picture"), 1);
Note: the EXTRA_ALLOW_MULTIPLE option is only available in Android API 18 and higher.
Define these variables in the class:
int PICK_IMAGE_MULTIPLE = 1;
String imageEncoded;
List<String> imagesEncodedList;
Let's Assume that onClick on a button it should open gallery to select images
Intent intent = new Intent();
intent.setType("image/*");
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent,"Select Picture"), PICK_IMAGE_MULTIPLE);
Then you should override onActivityResult Method
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
try {
// When an Image is picked
if (requestCode == PICK_IMAGE_MULTIPLE && resultCode == RESULT_OK
&& null != data) {
// Get the Image from data
String[] filePathColumn = { MediaStore.Images.Media.DATA };
imagesEncodedList = new ArrayList<String>();
if(data.getData()!=null){
Uri mImageUri=data.getData();
// Get the cursor
Cursor cursor = getContentResolver().query(mImageUri,
filePathColumn, null, null, null);
// Move to first row
cursor.moveToFirst();
int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
imageEncoded = cursor.getString(columnIndex);
cursor.close();
} else {
if (data.getClipData() != null) {
ClipData mClipData = data.getClipData();
ArrayList<Uri> mArrayUri = new ArrayList<Uri>();
for (int i = 0; i < mClipData.getItemCount(); i++) {
ClipData.Item item = mClipData.getItemAt(i);
Uri uri = item.getUri();
mArrayUri.add(uri);
// Get the cursor
Cursor cursor = getContentResolver().query(uri, filePathColumn, null, null, null);
// Move to first row
cursor.moveToFirst();
int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
imageEncoded = cursor.getString(columnIndex);
imagesEncodedList.add(imageEncoded);
cursor.close();
}
Log.v("LOG_TAG", "Selected Images" + mArrayUri.size());
}
}
} else {
Toast.makeText(this, "You haven't picked Image",
Toast.LENGTH_LONG).show();
}
} catch (Exception e) {
Toast.makeText(this, "Something went wrong", Toast.LENGTH_LONG)
.show();
}
super.onActivityResult(requestCode, resultCode, data);
}
NOTE THAT:
the gallery doesn't give you the ability to select multi-images so we here open all images studio that you can select multi-images from them.
and don't forget to add the permissions to your manifest
VERY IMPORTANT:
getData(); to get one single image and I've stored it here in imageEncoded String
if the user select multi-images then
they should be stored in the list
So you have to check which is null to use the other
Wish you have a nice try and to others
A lot of these answers have similarities but are all missing the most important part which is in onActivityResult, check if data.getClipData is null before checking data.getData
The code to call the file chooser:
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image/*"); //allows any image file type. Change * to specific extension to limit it
//**The following line is the important one!
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
startActivityForResult(Intent.createChooser(intent, "Select Picture"), SELECT_PICTURES); //SELECT_PICTURES is simply a global int used to check the calling intent in onActivityResult
The code to get all of the images selected:
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(requestCode == SELECT_PICTURES) {
if(resultCode == Activity.RESULT_OK) {
if(data.getClipData() != null) {
int count = data.getClipData().getItemCount(); //evaluate the count before the for loop --- otherwise, the count is evaluated every loop.
for(int i = 0; i < count; i++) {
Uri imageUri = data.getClipData().getItemAt(i).getUri();
//do something with the image (save it to some directory or whatever you need to do with it here)
}
}
} else if(data.getData() != null) {
String imagePath = data.getData().getPath();
//do something with the image (save it to some directory or whatever you need to do with it here)
}
}
}
Note that Android's chooser has Photos and Gallery available on some devices. Photos allows multiple images to be selected. Gallery allows just one at a time.
I hope this answer isn't late. Because the gallery widget doesn't support multiple selection by default, but you can custom the gridview which accepted your multiselect intent. The other option is to extend the gallery view and add in your own code to allow multiple selection.
This is the simple library can do it: https://github.com/luminousman/MultipleImagePick
Update:
From #ilsy's comment, CustomGalleryActivity in this library use manageQuery, which is deprecated, so it should be changed to getContentResolver().query() and cursor.close() like this answer
Initialize instance:
private String imagePath;
private List<String> imagePathList;
In onActivityResult You have to write this, If-else 2 block. One for single image and another for multiple image.
if (requestCode == GALLERY_CODE && resultCode == RESULT_OK && data != null) {
imagePathList = new ArrayList<>();
if (data.getClipData() != null) {
int count = data.getClipData().getItemCount();
for (int i=0; i<count; i++) {
Uri imageUri = data.getClipData().getItemAt(i).getUri();
getImageFilePath(imageUri);
}
}
else if (data.getData() != null) {
Uri imgUri = data.getData();
getImageFilePath(imgUri);
}
}
Most important part, Get Image Path from uri:
public void getImageFilePath(Uri uri) {
File file = new File(uri.getPath());
String[] filePath = file.getPath().split(":");
String image_id = filePath[filePath.length - 1];
Cursor cursor = getContentResolver().query(android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI, null, MediaStore.Images.Media._ID + " = ? ", new String[]{image_id}, null);
if (cursor!=null) {
cursor.moveToFirst();
imagePath = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
imagePathList.add(imagePath);
cursor.close();
}
}
Hope this can help you.
2022 - The Android Jetpack Compose way
For selecting multiple images in the gallery with Android Jetpack Compose.
val launcherMultipleImages = rememberLauncherForActivityResult(
contract = ActivityResultContracts.GetMultipleContents(),
) { uriList: List<Uri> ->
// TODO
}
And then use launcherMultipleImages.launch("image/*") to start the images selection.
For example :
Button(onClick = { launcherMultipleImages.launch("image/*") }) {
Text(text = "Select images")
}
this works for multiple image selection. also tested in API 29,30 in Google photos.
private static final int PICK_IMAGE = 2;
Intent intent = new Intent(Intent.ACTION_PICK,
android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
startActivityForResult(Intent.createChooser(intent, "Select
images"),PICK_IMAGE);
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == PICK_IMAGE && resultCode == RESULT_OK) {
if(data.getClipData() != null) {
int count = data.getClipData().getItemCount();
for(int i = 0; i < count; i++) {
Uri imageUri = data.getClipData().getItemAt(i).getUri();
//do what do you want to do
}
}
else if(data.getData() != null) {
Uri selectedImageUri = data.getData();
//do what do you want to do
}
}
Define getContent as below;
val getContent =
registerForActivityResult(ActivityResultContracts.GetMultipleContents())
{ uriList ->
// todo
}
after you grant related permission run the below code
getContent.launch("images/*")
I got null from the Cursor.
Then found a solution to convert the Uri into Bitmap that works perfectly.
Here is the solution that works for me:
#Override
public void onActivityResult(int requestCode, int resultCode, #Nullable Intent data) {
{
if (resultCode == Activity.RESULT_OK) {
if (requestCode == YOUR_REQUEST_CODE) {
if (data != null) {
if (data.getData() != null) {
Uri contentURI = data.getData();
ex_one.setImageURI(contentURI);
Log.d(TAG, "onActivityResult: " + contentURI.toString());
try {
Bitmap bitmap = MediaStore.Images.Media.getBitmap(context.getContentResolver(), contentURI);
} catch (IOException e) {
e.printStackTrace();
}
} else {
if (data.getClipData() != null) {
ClipData mClipData = data.getClipData();
ArrayList<Uri> mArrayUri = new ArrayList<Uri>();
for (int i = 0; i < mClipData.getItemCount(); i++) {
ClipData.Item item = mClipData.getItemAt(i);
Uri uri = item.getUri();
try {
Bitmap bitmap = MediaStore.Images.Media.getBitmap(context.getContentResolver(), uri);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
}
}
}
Hi below code is working fine.
Cursor imagecursor1 = managedQuery(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, columns, null,
null, orderBy + " DESC");
this.imageUrls = new ArrayList<String>();
imageUrls.size();
for (int i = 0; i < imagecursor1.getCount(); i++) {
imagecursor1.moveToPosition(i);
int dataColumnIndex = imagecursor1
.getColumnIndex(MediaStore.Images.Media.DATA);
imageUrls.add(imagecursor1.getString(dataColumnIndex));
}
options = new DisplayImageOptions.Builder()
.showStubImage(R.drawable.stub_image)
.showImageForEmptyUri(R.drawable.image_for_empty_url)
.cacheInMemory().cacheOnDisc().build();
imageAdapter = new ImageAdapter(this, imageUrls);
gridView = (GridView) findViewById(R.id.PhoneImageGrid);
gridView.setAdapter(imageAdapter);
You want to more clarifications.
http://mylearnandroid.blogspot.in/2014/02/multiple-choose-custom-gallery.html
I also had the same issue. I also wanted so users could take photos easily while picking photos from the gallery. Couldn't find a native way of doing this therefore I decided to make an opensource project. It is much like MultipleImagePick but just better way of implementing it.
https://github.com/giljulio/android-multiple-image-picker
private static final RESULT_CODE_PICKER_IMAGES = 9000;
Intent intent = new Intent(this, SmartImagePicker.class);
startActivityForResult(intent, RESULT_CODE_PICKER_IMAGES);
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode){
case RESULT_CODE_PICKER_IMAGES:
if(resultCode == Activity.RESULT_OK){
Parcelable[] parcelableUris = data.getParcelableArrayExtra(ImagePickerActivity.TAG_IMAGE_URI);
//Java doesn't allow array casting, this is a little hack
Uri[] uris = new Uri[parcelableUris.length];
System.arraycopy(parcelableUris, 0, uris, 0, parcelableUris.length);
//Do something with the uris array
}
break;
default:
super.onActivityResult(requestCode, resultCode, data);
break;
}
}
Try this one IntentChooser. Just add some lines of code, I did the rest for you.
private void startImageChooserActivity() {
Intent intent = ImageChooserMaker.newChooser(MainActivity.this)
.add(new ImageChooser(true))
.create("Select Image");
startActivityForResult(intent, REQUEST_IMAGE_CHOOSER);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_IMAGE_CHOOSER && resultCode == RESULT_OK) {
List<Uri> imageUris = ImageChooserMaker.getPickMultipleImageResultUris(this, data);
}
}
PS: as mentioned at the answers above, EXTRA_ALLOW_MULTIPLE is only available for API >= 18. And some gallery apps don't make this feature available (Google Photos and Documents (com.android.documentsui) work.
// for choosing multiple images declare variables
int PICK_IMAGE_MULTIPLE = 2;
String realImagePath;
// After requesting FILE READ PERMISSION may be on button click
Intent intent = new Intent();
intent.setType("image/*");
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent,"Select Images"), PICK_IMAGE_MULTIPLE);
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);// FOR CHOOSING MULTIPLE IMAGES
try {
// When an Image is picked
if (requestCode == PICK_IMAGE_MULTIPLE && resultCode == RESULT_OK
&& null != data) {
if (data.getClipData() != null) {
int count = data.getClipData().getItemCount(); //evaluate the count before the for loop --- otherwise, the count is evaluated every loop.
for (int i = 0; i < count; i++) {
Uri imageUri = data.getClipData().getItemAt(i).getUri();
realImagePath = getPath(this, imageUri);
//do something with the image (save it to some directory or whatever you need to do with it here)
Log.e("ImagePath", "onActivityResult: " + realImagePath);
}
} else if (data.getData() != null) {
Uri imageUri = data.getData();
realImagePath = getPath(this, imageUri);
//do something with the image (save it to some directory or whatever you need to do with it here)
Log.e("ImagePath", "onActivityResult: " + realImagePath);
}
}
} catch (Exception e) {
Toast.makeText(this, "Something went wrong", Toast.LENGTH_LONG)
.show();
}
}
public static String getPath(final Context context, final Uri uri) {
// DocumentProvider
if (DocumentsContract.isDocumentUri(context, uri)) {
// ExternalStorageProvider
if (isExternalStorageDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
if ("primary".equalsIgnoreCase(type)) {
return Environment.getExternalStorageDirectory() + "/" + split[1];
}
// TODO handle non-primary volumes
}
// DownloadsProvider
else if (isDownloadsDocument(uri)) {
final String id = DocumentsContract.getDocumentId(uri);
final Uri contentUri = ContentUris.withAppendedId(
Uri.parse("content://downloads/public_downloads"), Long.parseLong(id));
return getDataColumn(context, contentUri, null, null);
}
// MediaProvider
else if (isMediaDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
Uri contentUri = null;
if ("image".equals(type)) {
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
} else if ("video".equals(type)) {
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
} else if ("audio".equals(type)) {
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
}
final String selection = "_id=?";
final String[] selectionArgs = new String[]{
split[1]
};
return getDataColumn(context, contentUri, selection, selectionArgs);
}
}
// MediaStore (and general)
else if ("content".equalsIgnoreCase(uri.getScheme())) {
return getDataColumn(context, uri, null, null);
}
// File
else if ("file".equalsIgnoreCase(uri.getScheme())) {
return uri.getPath();
}
return null;
}
/**
* Get the value of the data column for this Uri. This is useful for
* MediaStore Uris, and other file-based ContentProviders.
*
* #param context The context.
* #param uri The Uri to query.
* #param selection (Optional) Filter used in the query.
* #param selectionArgs (Optional) Selection arguments used in the query.
* #return The value of the _data column, which is typically a file path.
*/
public static String getDataColumn(Context context, Uri uri, String selection,
String[] selectionArgs) {
Cursor cursor = null;
final String column = "_data";
final String[] projection = {
column
};
try {
cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
null);
if (cursor != null && cursor.moveToFirst()) {
final int column_index = cursor.getColumnIndexOrThrow(column);
return cursor.getString(column_index);
}
} finally {
if (cursor != null)
cursor.close();
}
return null;
}
/**
* #param uri The Uri to check.
* #return Whether the Uri authority is ExternalStorageProvider.
*/
public static boolean isExternalStorageDocument(Uri uri) {
return "com.android.externalstorage.documents".equals(uri.getAuthority());
}
/**
* #param uri The Uri to check.
* #return Whether the Uri authority is DownloadsProvider.
*/
public static boolean isDownloadsDocument(Uri uri) {
return "com.android.providers.downloads.documents".equals(uri.getAuthority());
}
/**
* #param uri The Uri to check.
* #return Whether the Uri authority is MediaProvider.
*/
public static boolean isMediaDocument(Uri uri) {
return "com.android.providers.media.documents".equals(uri.getAuthority());
}
this worked perfectly for me credits: Get real path from URI, Android KitKat new storage access framework
For selecting multiple image from gallery
i.putExtra(Intent.EXTRA_ALLOW_MULTIPLE,true);
An Ultimate Solution for multiple image upload with camera option also for Android Lollipop to Android 10, SDK 30.
private static final int FILECHOOSER_RESULTCODE = 1;
private ValueCallback<Uri> mUploadMessage;
private ValueCallback<Uri[]> mUploadMessages;
private Uri mCapturedImageURI = null;
Add this to OnCreate of MainActivity
mWebView.setWebChromeClient(new WebChromeClient() {
// openFileChooser for Android 3.0+
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType){
mUploadMessage = uploadMsg;
openImageChooser();
}
// For Lollipop 5.0+ Devices
public boolean onShowFileChooser(WebView mWebView, ValueCallback<Uri[]> filePathCallback, WebChromeClient.FileChooserParams fileChooserParams) {
mUploadMessages = filePathCallback;
openImageChooser();
return true;
}
// openFileChooser for Android < 3.0
public void openFileChooser(ValueCallback<Uri> uploadMsg){
openFileChooser(uploadMsg, "");
}
//openFileChooser for other Android versions
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
openFileChooser(uploadMsg, acceptType);
}
private void openImageChooser() {
try {
File imageStorageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "FolderName");
if (!imageStorageDir.exists()) {
imageStorageDir.mkdirs();
}
File file = new File(imageStorageDir + File.separator + "IMG_" + String.valueOf(System.currentTimeMillis()) + ".jpg");
mCapturedImageURI = Uri.fromFile(file);
final Intent captureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, mCapturedImageURI);
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("image/*");
i.putExtra(Intent.EXTRA_ALLOW_MULTIPLE,true);
Intent chooserIntent = Intent.createChooser(i, "Image Chooser");
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Parcelable[]{captureIntent});
startActivityForResult(chooserIntent, FILECHOOSER_RESULTCODE);
} catch (Exception e) {
e.printStackTrace();
}
}
});
onActivityResult
public void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == FILECHOOSER_RESULTCODE) {
if (null == mUploadMessage && null == mUploadMessages) {
return;
}
if (null != mUploadMessage) {
handleUploadMessage(requestCode, resultCode, data);
} else if (mUploadMessages != null) {
handleUploadMessages(requestCode, resultCode, data);
}
}
}
private void handleUploadMessage(final int requestCode, final int resultCode, final Intent data) {
Uri result = null;
try {
if (resultCode != RESULT_OK) {
result = null;
} else {
// retrieve from the private variable if the intent is null
result = data == null ? mCapturedImageURI : data.getData();
}
} catch (Exception e) {
e.printStackTrace();
}
mUploadMessage.onReceiveValue(result);
mUploadMessage = null;
// code for all versions except of Lollipop
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
result = null;
try {
if (resultCode != RESULT_OK) {
result = null;
} else {
// retrieve from the private variable if the intent is null
result = data == null ? mCapturedImageURI : data.getData();
}
} catch (Exception e) {
Toast.makeText(getApplicationContext(), "activity :" + e, Toast.LENGTH_LONG).show();
}
mUploadMessage.onReceiveValue(result);
mUploadMessage = null;
}
} // end of code for all versions except of Lollipop
private void handleUploadMessages(final int requestCode, final int resultCode, final Intent data) {
Uri[] results = null;
try {
if (resultCode != RESULT_OK) {
results = null;
} else {
if (data != null) {
String dataString = data.getDataString();
ClipData clipData = data.getClipData();
if (clipData != null) {
results = new Uri[clipData.getItemCount()];
for (int i = 0; i < clipData.getItemCount(); i++) {
ClipData.Item item = clipData.getItemAt(i);
results[i] = item.getUri();
}
}
if (dataString != null) {
results = new Uri[]{Uri.parse(dataString)};
}
} else {
results = new Uri[]{mCapturedImageURI};
}
}
} catch (Exception e) {
e.printStackTrace();
}
mUploadMessages.onReceiveValue(results);
mUploadMessages = null;
}
For Multiple image selection and with selection limit restriction feature, use chintan369/MultiImagePicker library which is the latest of 2021 and supports Android 11 too. It is well documented and also demo is explained on youtube for use. It's very easy to add in the project, easy to use to call the library for image selection and getting results of selected images as Uri list and also you can request result list as absolute file path list.