My app has functionality for allowing the user to either select a photo or video from their current library, or to take a new one, and then attach to an email. It seems to be working on the majority of devices and Android versions. However, I'm seeing some crashes intermittently for some device setups when getting the uri to the image or video to attach to the email. An example crash is: Fatal Exception: java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=67425, result=-1, data=Intent { dat=content://com.google.android.apps.docs.storage/document/acc=1;doc=3 flg=0x1 }} to activity: java.lang.NullPointerException: Attempt to invoke virtual method 'char[] java.lang.String.toCharArray()' on a null object reference. This happens on the "File file = new File(imagePath);" line in the email() method below. Are there changes I can make to my code to allow for a more universal solution to this? My minSdkVersion is 16, and my targetSdkVersion is 23.
Below is my code for the implementation:
private enum Type {
PHOTO,
VIDEO
}
private enum Source {
LIBRARY,
CAMERA
}
private void select(Type type) {
if(selectedSource == Source.LIBRARY) {
// choose from library
Intent getIntent = new Intent(Intent.ACTION_GET_CONTENT);
if (type == Type.PHOTO) {
getIntent.setType("image/*");
} else {
getIntent.setType("video/*");
}
Intent pickIntent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
if (type == Type.PHOTO) {
pickIntent.setType("image/*");
} else {
pickIntent.setType("video/*");
}
Intent chooserIntent = Intent.createChooser(getIntent, "Select Photo/Video");
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[]{pickIntent});
startActivityForResult(chooserIntent, CHOOSE_IMAGE_VIDEO_ACTIVITY_REQUEST_CODE);
} else if(selectedSource == Source.CAMERA) {
// take photo/video from camera
// check for camera permission
if ( ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED ) {
requestPermissions(new String[]{Manifest.permission.CAMERA}, REQUEST_CAMERA);
}
if (Build.VERSION.SDK_INT >= 23 && ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
//Toast.makeText(getActivity(), "You need to allow camera access", Toast.LENGTH_LONG).show();
return;
} else {
if (getActivity().getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY)) {
if ( ContextCompat.checkSelfPermission(getActivity(), READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED ) {
requestPermissions(new String[]{READ_EXTERNAL_STORAGE}, REQUEST_EXTERNAL_STORAGE);
}
if (Build.VERSION.SDK_INT >= 23 && ContextCompat.checkSelfPermission(getActivity(), READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
//Toast.makeText(getActivity(), "You need to allow external storage access", Toast.LENGTH_LONG).show();
return;
} else {
if (selectedType == Type.PHOTO) {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (intent.resolveActivity(getActivity().getPackageManager()) != null) {
File photo = null;
try {
// place where to store camera taken picture
photo = this.createTemporaryFile("photo", ".jpg");
photo.delete();
} catch (Exception e) {
//Log.v(TAG, "Can't create file to take picture!");
Toast.makeText(getActivity(), "Please check SD card! Image shot is impossible!", Toast.LENGTH_LONG).show();
}
mImageUri = Uri.fromFile(photo);
intent.putExtra(MediaStore.EXTRA_OUTPUT, mImageUri);
startActivityForResult(intent, TAKE_IMAGE_ACTIVITY_REQUEST_CODE);
} else {
Toast.makeText(getActivity(), "Unable to access the camera", Toast.LENGTH_LONG).show();
}
} else if (selectedType == Type.VIDEO) {
Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
if (intent.resolveActivity(getActivity().getPackageManager()) != null) {
File video = null;
try {
// place where to store camera taken video
video = this.createTemporaryFile("video", ".mp4");
video.delete();
} catch (Exception e) {
//Log.v(TAG, "Can't create file to take picture!");
Toast.makeText(getActivity(), "Please check SD card! Video is impossible!", Toast.LENGTH_LONG).show();
}
mImageUri = Uri.fromFile(video);
intent.putExtra(MediaStore.EXTRA_OUTPUT, mImageUri);
startActivityForResult(intent, TAKE_VIDEO_ACTIVITY_REQUEST_CODE);
} else {
Toast.makeText(getActivity(), "Unable to access the camera", Toast.LENGTH_LONG).show();
}
}
}
} else {
Toast.makeText(getActivity(), "No camera available", Toast.LENGTH_LONG).show();
}
}
}
}
#Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
switch (requestCode) {
case REQUEST_CAMERA: {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
System.out.println("REQUEST CAMERA RESULT");
select(selectedType);
} else {
//Permission denied
Toast.makeText(getActivity(), "You need to allow camera access", Toast.LENGTH_LONG).show();
}
return;
}
case REQUEST_EXTERNAL_STORAGE: {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
System.out.println("REQUEST EXTERNAL STORAGE RESULT");
select(selectedType);
} else {
//Permission denied
Toast.makeText(getActivity(), "You need to allow external storage access", Toast.LENGTH_LONG).show();
}
return;
}
}
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == CHOOSE_IMAGE_VIDEO_ACTIVITY_REQUEST_CODE) {
// from image picker
if (resultCode == Activity.RESULT_OK) {
if(data != null) {
//InputStream inputStream = getActivity().getContentResolver().openInputStream(data.getData());
mImageUri = data.getData();
imagePath = getPath(getActivity(), mImageUri);
email();
}
}
} else if(requestCode == TAKE_IMAGE_ACTIVITY_REQUEST_CODE || requestCode == TAKE_VIDEO_ACTIVITY_REQUEST_CODE) {
if (resultCode == Activity.RESULT_OK) {
grabImageOrVideoTaken();
}
}
}
private void grabImageOrVideoTaken() {
getActivity().getContentResolver().notifyChange(mImageUri, null);
imagePath = getPath(getActivity(), mImageUri);
email();
}
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;
}
private void email() {
String mediaType = "photo";
if(selectedType == Type.VIDEO) {
mediaType = "video";
}
String email = "test#test.com";
Intent intent = new Intent(Intent.ACTION_SEND, Uri.fromParts("mailto", email, null));
intent.putExtra(Intent.EXTRA_SUBJECT, getResources().getString(R.string.app_name) + ": Photo/Video Submission");
intent.putExtra(Intent.EXTRA_EMAIL, new String[]{email});
File file = new File(imagePath);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
if (selectedType == Type.PHOTO) {
intent.setType("image/jpeg");
} else {
intent.setType("video/3gp");
}
intent.putExtra(Intent.EXTRA_STREAM, Uri.parse("file://" + file.getAbsolutePath()));
startActivity(Intent.createChooser(intent, "Submit Photo/Video"));
}
Are there changes I can make to my code to allow for a more universal solution to this?
Sure. Get rid of getPath().
It seems to be working on the majority of devices and Android versions.
Not really.
Use the Uri properly. It is not a file. Even in the few cases where it does point to a file, you may not have access to that file via the filesystem.
You say that you are using this for an email attachment. If that is ACTION_SEND with a EXTRA_STREAM extra, use the Uri that you were given. Add FLAG_GRANT_READ_URI_PERMISSION on the ACTION_SEND Intent, so that the read permissions that you were granted get sent along to the ACTION_SEND handler.
If you are using something else for the email, use a ContentResolver and openInputStream() to get an InputStream on the file or content Uri. Then, either pass that stream to the other code, or use that stream to make your own local file copy (e.g., in getCacheDir()), and then use that local copy, deleting it when you do not need it.
Related
Android 11 Can not get media files path from external storage (uri content://com.android.providers.media.documents/document/) . I have all permissions. In another devices I can get files path. But in Realmi 8 Android 11 I get empty sting.
maybe there are some features of storage and access in this model that I do not know about?
I didn't check if this problem is in another Realme model.
Here is my code:
///////////
myButton.setOnclickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
checkWritePermission();
}
});
private void checkWritePermission() {
if (isWritePermissionGranted()) {
sendNewMessage(titleEditText.getText().toString(), messageEditText.getText().toString(),
files, false);
} else {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE}, PERMISSIONS_REQUEST_WRITE_ACCESS);
}
}
private Boolean isWritePermissionGranted() {
return ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED &&
ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions,
#NonNull int[] grantResults) {
if (requestCode == PERMISSIONS_REQUEST_READ_STORAGE) {
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// permission was granted, yay! Do the
// contacts-related task you need to do.
navigateToCustomPhotoGallary();
}
} else if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED
&& requestCode == REQUEST_FROM_DIALOG) {
AttachmentDialog.newInstance().show(getSupportFragmentManager());
}
if (requestCode == PERMISSIONS_REQUEST_WRITE_ACCESS && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
sendNewMessage(titleEditText.getText().toString(), messageEditText.getText().toString(),
files, false);
}
}
///////
{
String[] mimeTypes = CommonConstants.ALLOWED_MIME_TYPES;
Intent intent;
intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
intent.setType("*/*");
intent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes);
startActivityForResult(intent, PICK_PDF_SINGLE);
}
//////
String filePath = "";
String selection = null;
String[] selectionArgs = null;
// Uri is different in versions after KITKAT (Android 4.4), we need to
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && DocumentsContract.isDocumentUri(context.getApplicationContext(), uri)) {
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];
if (isMediaDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
if ("image".equals(type)) {
uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
} else if ("video".equals(type)) {
uri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
} else if ("audio".equals(type)) {
uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
} else if ("document".equals(type)) {
uri = MediaStore.Files.getContentUri("external");
}
selection = "_id=?";
selectionArgs = new String[]{
split[1]
};
}
}
if ("content".equalsIgnoreCase(uri.getScheme())) {
String[] projection = {
MediaStore.Images.Media.DATA
};
try (Cursor cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null)) {
if (cursor != null) {
return getFilePathFromCursor(cursor, projection[0]);
}
}
} else if ("file".equalsIgnoreCase(uri.getScheme())) {
return uri.getPath();
}
return null;
}
If you're stil not getting your files then maybe my answer can help you.
Try it - Load media files in android 10 and above
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.
Goal: to be able to attach a file of any type to a <input type="file" /> in a webview in Android 4.1+. (Android 5+ is fine)
I set openFileChooser as I saw fit based on few examples I have found. It works on 4.1 but not on 4.4.4 where the files attached do not have their filename correctly set.
Instead is set as filename the last path of the intent.mData return to onActivityResult, .e.g, for a mData value of content://com.android.providers.downloads.documents/document/2, the filename will be 2―without extension of course―while the name should be image.png.
What can I do to fix it? Would there be any problem in my code?
I do my testing on an emulator: Galaxy Nexus, API 19, target: Default
See code below.
webView.setWebChromeClient(new WebChromeClient() {
public void openFileChooser(ValueCallback<Uri> valueCallback, String acceptType, String capture) {
if (UseWebViewActivity.this.valueCallback != null) {
UseWebViewActivity.this.valueCallback.onReceiveValue(null);
}
UseWebViewActivity.this.valueCallback = valueCallback;
Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT);
contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE);
contentSelectionIntent.setType("*/*");
startActivityForResult(Intent.createChooser(contentSelectionIntent,
getString(R.string.file_chooser_title)), INPUT_FILE_REQUEST_CODE);
}
});
// ...
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
if (requestCode == INPUT_FILE_REQUEST_CODE && valueCallback != null) {
Uri result = null;
if (resultCode == Activity.RESULT_OK) {
result = intent.getData();
}
valueCallback.onReceiveValue(result);
valueCallback = null;
}
}
The problem is in the onActivityResult() method for KitKat which returns the URI. You need to get the realpath of the imagefile.
Notice how i get the Real Path of the IMG by calling :
KitkatPath = Uri.parse("file://"+getPath(MobilestyxAppActivity.this, result)); in the onActivityResult().
You must add the string "file://" for it to work.
PS : I have gathered the code from various sources and modified it.
Look at my code below which works for Kitkat & Lolipop and other low versions of Android.
/** CODE FOR FILE UPLOAD*/
private static final int INPUT_FILE_REQUEST_CODE = 1;
private static final int FILECHOOSER_RESULTCODE = 1;
private ValueCallback<Uri> mUploadMessage;
private Uri mCapturedImageURI = null;
private ValueCallback<Uri[]> mFilePathCallback;
private String mCameraPhotoPath;
Uri KitkatPath ;
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (requestCode != INPUT_FILE_REQUEST_CODE || mFilePathCallback == null) {
super.onActivityResult(requestCode, resultCode, data);
return;
}
Uri[] results = null;
// Check that the response is a good one
if (resultCode == Activity.RESULT_OK) {
if (data == null) {
// If there is not data, then we may have taken a photo
if (mCameraPhotoPath != null) {
results = new Uri[]{Uri.parse(mCameraPhotoPath)};
}
} else {
String dataString = data.getDataString();
if (dataString != null) {
results = new Uri[]{Uri.parse(dataString)};
}
}
}
mFilePathCallback.onReceiveValue(results);
mFilePathCallback = null;
}
else if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
System.out.println("In KitKat Condition");
if (requestCode != FILECHOOSER_RESULTCODE || mUploadMessage == null) {
System.out.println("In != Null");
super.onActivityResult(requestCode, resultCode, data);
return;
}
if (requestCode == FILECHOOSER_RESULTCODE) {
System.out.println("requestCode == FileChooser ResultCode");
if (null == this.mUploadMessage) {
System.out.println("In null == this.mUploadMessage");
return;
}
Uri result = null;
try {
if (resultCode != RESULT_OK) {
result = null;
} else {
//newcode
// retrieve from the private variable if the intent is null
result = data == null ? mCapturedImageURI : data.getData();
KitkatPath = Uri.parse("file://"+getPath(MobilestyxAppActivity.this, result));
System.out.println("KitkatPath== "+KitkatPath);
System.out.println("result = "+result);
}
} catch (Exception e) {
// Toast.makeText(getApplicationContext(), "activity :" + e, Toast.LENGTH_LONG).show();
e.printStackTrace();
}
// mUploadMessage.onReceiveValue(result);
mUploadMessage.onReceiveValue(KitkatPath);
System.out.println("mUploadMessage = "+mUploadMessage);
mUploadMessage = null;
}
}
return;
}
/** CODE FOR FILE UPLOAD*/
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());
}
private File createImageFile() throws IOException {
// Create an image file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date(heightDiff));
String imageFileName = "JPEG_" + timeStamp + "_";
File storageDir = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES);
File imageFile = File.createTempFile(
imageFileName, /* prefix */
".jpg", /* suffix */
storageDir /* directory */
);
return imageFile;
}
& now in the WebViewChromeClient ---
browser.setWebChromeClient(new WebChromeClient()
{
// For Android 5.0
public boolean onShowFileChooser(WebView view, ValueCallback<Uri[]> filePath, WebChromeClient.FileChooserParams fileChooserParams) {
System.out.println("in 5.0");
// Double check that we don't have any existing callbacks
if (mFilePathCallback != null) {
mFilePathCallback.onReceiveValue(null);
}
mFilePathCallback = filePath;
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
// Create the File where the photo should go
File photoFile = null;
try {
photoFile = createImageFile();
takePictureIntent.putExtra("PhotoPath", mCameraPhotoPath);
} catch (IOException ex) {
// Error occurred while creating the File
// Log.e(TAG, "Unable to create Image File", ex);
}
// Continue only if the File was successfully created
if (photoFile != null) {
mCameraPhotoPath = "file:" + photoFile.getAbsolutePath();
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,
Uri.fromFile(photoFile));
} else {
takePictureIntent = null;
}
}
Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT);
contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE);
contentSelectionIntent.setType("image/*");
Intent[] intentArray;
if (takePictureIntent != null) {
intentArray = new Intent[]{takePictureIntent};
} else {
intentArray = new Intent[0];
}
Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER);
chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent);
chooserIntent.putExtra(Intent.EXTRA_TITLE, "Image Chooser");
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray);
startActivityForResult(chooserIntent, INPUT_FILE_REQUEST_CODE);
return true;
}
// openFileChooser for Android 3.0+
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) {
System.out.println("in 3.0+");
mUploadMessage = uploadMsg;
// Create AndroidExampleFolder at sdcard
// Create AndroidExampleFolder at sdcard
File imageStorageDir = new File(
Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES)
, "AndroidExampleFolder");
if (!imageStorageDir.exists()) {
// Create AndroidExampleFolder at sdcard
imageStorageDir.mkdirs();
}
// Create camera captured image file path and name
File file = new File(
imageStorageDir + File.separator + "IMG_"
+ String.valueOf(System.currentTimeMillis())
+ ".jpg");
mCapturedImageURI = Uri.fromFile(file);
//
// Camera capture image intent
final Intent captureIntent = new Intent(
android.provider.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/*");
// Create file chooser intent
Intent chooserIntent = Intent.createChooser(i, "Image Chooser");
// Set camera intent to file chooser
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS
, new Parcelable[] { captureIntent });
// On select image call onActivityResult method of activity
startActivityForResult(chooserIntent, FILECHOOSER_RESULTCODE);
}
// openFileChooser for Android < 3.0
public void openFileChooser(ValueCallback<Uri> uploadMsg) {
System.out.println("in <3.0");
openFileChooser(uploadMsg, "");
}
// openFileChooser for other Android versions
public void openFileChooser(ValueCallback<Uri> uploadMsg,
String acceptType, String capture) {
System.out.println("in OTHER");
openFileChooser(uploadMsg, acceptType);
}
Try using aFileChooser to avoid complications with API Level compatibilities (the library has it handled):
aFileChooser is an Android Library Project that simplifies the process
of presenting a file chooser on Android 2.1+.
Intents provide the ability to hook into third-party app components
for content selection. This works well for media files, but if you
want users to be able to select any file, they must have an existing
"file explorer" app installed. Because many Android devices don't have
stock File Explorers, the developer must often instruct the user to
install one, or build one, themselves. aFileChooser solves this issue.
Features:
Full file explorer
Simplify GET_CONTENT Intent creation
Hooks into Storage Access Framework
Determine MIME data types
Follows Android conventions (Fragments, Loaders, Intents, etc.)
Supports API 7+
Check the readme.md on The repo's GitHub page for setup and usage instructions.
Android 4.1 and 4.4 have different workings here since Storage Access Framework was introduced in Android 4.4 (API level 19). To retrieve the file name, you can try something like this:
Uri uri = intent.getData();
File imageFile = new File(uri);
if(imageFile.exists()){
String name = imageFile.getName();
}
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.