I want to pick Images(JPG, PNG) and GIF(gif) files separately. This works for most Gallery apps and file managers except Google Photos app. When I pick images or gifs using Google Photos app, all images are displayed i.e. JPG, PNG, GIF, BMP, etc. How can I tell Google Photos to allow picking only selected type of files?
Here's my code:
val contentPicker = activity.registerForActivityResult(
ActivityResultContracts.GetContent()
) { uri: Uri? ->
// Use Picked URI
}
val mime = when {
MediaType.isPhoto(mediaType) -> "image/*" // or image/jpg or image/png
MediaType.isGif(mediaType) -> "image/gif"
else -> throw RuntimeException("Wrong Media Type to pick: $mediaType")
}
contentPicker.launch(mime)
If there's another way besides this(maybe targeting Google Photos only), please share as an answer. I couldn't find any relevant question with proper answer. I want to use system apps only to pick content.
Edit (Nov 23, 2021)
Even if I use following intent to open Files app, a user can just go to Side Bar and select Photos app. Files app prevents picking gif/heic images. But, when Photos app is opened from within the Files app, user can select gif/heic images without any restriction.
fun getPickPhotoIntent(): Intent {
val pickIntent = Intent(Intent.ACTION_GET_CONTENT)
pickIntent.addCategory(Intent.CATEGORY_OPENABLE)
pickIntent.type = "image/jpg"
val mimeTypes = arrayOf("image/bmp", "image/jpeg", "image/png")
pickIntent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes)
pickIntent.putExtra(Intent.EXTRA_LOCAL_ONLY, true)
return pickIntent
}
fun getPickPhotoIntent(): Intent {
val pickIntent = Intent(com.google.android.apps.photos.picker.external.ExternalPickerActivity)//package name Used
pickIntent.addCategory(Intent.CATEGORY_OPENABLE)
pickIntent.type = "image/jpg"
val mimeTypes = arrayOf("image/bmp", "image/jpeg", "image/png")
pickIntent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes)
pickIntent.putExtra(Intent.EXTRA_LOCAL_ONLY, true)
return pickIntent
}
Related
My app can share internally stored files with the androidx.core.content.FileProvider.
The intent and the chooser are created with the following snippet:
val shareIntent: Intent = Intent().apply {
action = Intent.ACTION_SEND
putExtra(
Intent.EXTRA_STREAM,
FileProvider.getUriForFile(this, authority, file, displayName),
)
type = document.mimeType.mediaType
}
startActivity(Intent.createChooser(shareIntent, getString(R.string.share_title)))
I explicitly pass the displayName to the FileProvider, but in the app chooser, I see the file's local name (see image).
Is there a way to show the correct display name in the chooser?
Since Android introduced major changes in storage framework recently much of the documentation talks about permissions and scoped storage. But I couldn't find details on how to process Uri of a file, for it to be readable by other apps.
The intent action to view/read a file by other apps fail. I don't understand what's the problem here;
Does it have to do with difference between java.io.File and java.nio.File?
The Uri has missing permissions or the Uri is not well formatted.
The Android storage samples (FileManager) has this bug as well. It lists all the files in a directory successfully but can't open a selected image, or a document. I've reported this issue but no help so far.
Following snippet is from FileManager (storage-samples)
fun openFile(activity: AppCompatActivity, selectedItem: File) {
// Get URI and MIME type of file
val uri = Uri.fromFile(selectedItem).normalizeScheme()
val mime: String = getMimeType(uri.toString())
// Open file with user selected app
val intent = Intent()
intent.action = Intent.ACTION_VIEW
intent.data = uri
intent.type = mime
return activity.startActivity(intent)
}
After the hints from the comments, I found the answer in developer docs.
Caution: If you want to set both the URI and MIME type, don't call setData() and setType() because they each nullify the value of the other. Always use setDataAndType() to set both URI and MIME type.
The reason behind openFile didn't throw FileUriExposedException in android-storage-samples is that after setting intent.type, the Uri gets nullified and when I changed it to setDataAndType() I got the exception. The final snippet looks like
fun openFile(activity: AppCompatActivity, selectedItem: File) {
// Get URI and MIME type of file
val uri = FileProvider.getUriForFile(activity.applicationContext, AUTHORITY, selectedItem)
// val uri = Uri.fromFile(selectedItem).normalizeScheme()
val mime: String = getMimeType(uri.toString())
// Open file with user selected app
val intent = Intent()
intent.action = Intent.ACTION_VIEW
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
// intent.data = uri
// intent.type = mime
intent.setDataAndType(uri, mime)
return activity.startActivity(intent)
}
I think they forgot to update the samples over time, let me create a pull request to commit this change over there as well.
I am making a camera app which takes both front camera and back camera images/video and I do not want individual thumbnails on the Camera preview for each file.
I want to open "/storage/emulated/0/DCIM/Camera" folder using the Files app and further open the photo/video which is not possible with ACTION_GET_CONTENT as it selects the image and exits the Files app as tried here -
val intent = Intent(Intent.ACTION_GET_CONTENT)
val uri: Uri = Uri.parse(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).path.toString() + "/Camera")
intent.setDataAndType(uri, "*/*")
startActivity(Intent.createChooser(intent, "Open folder"))
I tried ACTION_VIEW too, but it is not specific to one folder and opens the gallery showing all media as tried here -
val intent = Intent()
intent.action = Intent.ACTION_VIEW
intent.type = "image/*"
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
startActivity(intent)
"image/*" shows images and videos too in the gallery for me which is good. When "*/*" is used we can use the Files app too but it opens the downloads folder.
One solution I found works only with ES Explorer as tried here -
val uri: Uri = Uri.parse(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).path.toString() + "/Camera")
val intent = Intent(Intent.ACTION_VIEW)
intent.setDataAndType(uri, "resource/folder")
startActivity(intent)
This is due to "resource/folder" not supported leading to a crash. Changing "resource/folder" to "*/*" makes Files app open the downloads folder and Photos app to hang.
It seems gallery can do it via buckets, but it too is not universal.
I am not asking for much, just to display my Camera folder from where I can open and view any photo/video.
It was not possible if the user had not once selected a file using ACTION_GET_CONTENT was the opinion.
But now... try this code:
String scheme = "content://com.android.externalstorage.documents/document/primary%3APictures";
// String scheme = "content://com.android.externalstorage.documents/document/primary%3ADCIM%2FCamera";
Uri uri = Uri.parse(scheme);
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("*/*");
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, uri); // Added in API level 26
startActivityForResult(intent, 12345);
Toast.makeText(context, "Picker opened in:\n\n" + uri.toString(), Toast.LENGTH_LONG).show();
It works here and i'm pretty amazed.
To open the Files app starting with a certain folder you can use below code but only for Android 11 devices sadly.
//String scheme = "content://com.android.externalstorage.documents/document/primary%3APictures";
//String scheme = "content://com.android.externalstorage.documents/document/primary%3ADownload";
//String scheme = "content://com.android.externalstorage.documents/document/10F9-2E19%3ADownload";
String scheme = "content://com.android.externalstorage.documents/document/primary%3ADCIM%2FCamera";
Uri uri = Uri.parse(scheme);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(uri, "vnd.android.document/root");
startActivity(intent );
Toast.makeText(context, "Files app opening in:\n\n" + uri.toString(), Toast.LENGTH_LONG).show();
I'm trying to make a button in my app when user click on it, it'll open a specific URI in file manager but the best I could've done is that the button opens recent tab in default file manager.
Please, if it's possible, suggest me a code which opens a chooser for user to choose between his file manager applications and when user chose, that file manager opens in specific URI that I defined in my code.
Here is my code:
val intent = Intent(Intent.ACTION_GET_CONTENT)
val uri = Uri.parse(
//my path
)
intent.data = uri
intent.type = "*/*"
startActivity(Intent.createChooser(intent, "Open folder"))
Also one of the users suggested me to use INITIAL_URI I've did it like this but didn't work :
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
val uri = Uri.parse(
//my path
)
intent.data = uri
intent.type = "*/*"
intent.putExtra("android.provider.extra.INITIAL_URI", uri)
intent.putExtra("android.content.extra.SHOW_ADVANCED", true)
startActivity(Intent.createChooser(intent, "Open folder"))
suggest me a code which opens a chooser for user to choose between his file manager applications and when user chose, that file manager opens in specific URI that I defined in my code
That has never been a pattern in Android app development. There is no standard Intent action for what you seek that is likely to be implemented by much of anything, let alone a significant number of file manager apps.
fun openNewTabWindow(urls: String, context : Context) {
val uris = Uri.parse(urls)
val intents = Intent(Intent.ACTION_VIEW, uris)
val myV = Bundle()
myV.putBoolean("new_window", true)
intents.putExtras(myV)
context.startActivity(intents)
}
I want to lets users able to pick any file type in their phones, then view it again using intent.
To pick file item I write this ( I'm using Kotlin, same issue in Java):
fun goToDocumentPicker() {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.type = "*/*"
if (intent.resolveActivity(mContext.packageManager) != null)
mContext.startActivityForResult(intent, REQUEST_DOCUMENT)
}
And to show the picked item:
fun showDocumentPreviewer(uri: Uri) {
val i = Intent(Intent.ACTION_VIEW)
i.data = uri
mContext.startActivity(i)
}
The document picker works fine, in the onActivityResult I can receive the Uri object of selected document, but the document previewer can't open the object. Already try to set mime-type to the previewer's intent but not success. Did I use incorrect way to open file in Android? Any generic way to show any file type in Android? (Because I want to support many file types)
picked Uri: content://com.android.providers.media.documents/document/image:80
Update:
Base on #CommonsWare's comment, I edited the preview function like below:
fun showAttachmentPreviewer(uri: Uri, mimeType: String?) {
Log.d("TEST", "Preview " + uri.toString())
val intent = Intent(Intent.ACTION_VIEW, uri)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
val chooser = Intent.createChooser(intent, "Open with")
if (intent.resolveActivity(mContext.packageManager) != null)
mContext.startActivity(chooser)
else
mContext.showSnackBar("No suitable application to open file")
}
Now the app always crash with below crash-log:
java.lang.SecurityException: Uid 10202 does not have permission to uri
0 #
content://com.android.providers.media.documents/document/audio:17915
Update 2:
My app crash at line mContext.startActivity(chooser). Here is the full crash-log:
FATAL EXCEPTION: main
Process: com.makeit.lite, PID: 12851
java.lang.SecurityException: Uid 10477 does not have permission to uri
0 #
content://com.android.providers.media.documents/document/image:24776
at android.os.Parcel.readException(Parcel.java:1540)
at android.os.Parcel.readException(Parcel.java:1493)
at
android.app.ActivityManagerProxy.startActivity(ActivityManagerNative.java:2514)
at
android.app.Instrumentation.execStartActivity(Instrumentation.java:1494)
at android.app.Activity.startActivityForResult(Activity.java:3913)
at
android.support.v4.app.BaseFragmentActivityJB.startActivityForResult(BaseFragmentActivityJB.java:50)
at
android.support.v4.app.FragmentActivity.startActivityForResult(FragmentActivity.java:79)
at android.app.Activity.startActivityForResult(Activity.java:3860)
at
android.support.v4.app.FragmentActivity.startActivityForResult(FragmentActivity.java:859)
at android.app.Activity.startActivity(Activity.java:4184)
at android.app.Activity.startActivity(Activity.java:4152)
at
com.makeit.lite.attachment.AttachmentNavigator.showAttachmentPreviewer(AttachmentNavigator.kt:92)
at
com.makeit.lite.attachment.list.AttachmentListPresenter.onAttachmentClicked(AttachmentListPresenter.kt:37)
at
com.makeit.lite.attachment.list.AttachmentListFragment$onViewCreated$1.onItemClick(AttachmentListFragment.kt:39)
at
eu.davidea.viewholders.FlexibleViewHolder.onClick(FlexibleViewHolder.java:121)
at android.view.View.performClick(View.java:5156)
at android.view.View$PerformClick.run(View.java:20755)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:145)
at android.app.ActivityThread.main(ActivityThread.java:5835)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at
com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1399)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1194)
Update 3:
If I set mimeType to intent: intent.type = mimeType and the app won't crash anymore. The mimeType is the string I get from the picker intent (beside the content: uri). The mimeType value canbe image/jpeg or anything base on selected file type. Although it won't crash, the file at given uri won't display as well. If I choose the Gallery from Intent-Chooser, the Gallery open and show all images. I guess the 3rd app don't know how to determinate the file at given uri.
Here is the latest source of my function:
fun showAttachmentPreviewer(uri: Uri, mimeType: String?) {
Log.d("TEST", "Preview " + uri.toString() + " For type" + mimeType)
val intent = Intent(Intent.ACTION_VIEW, uri)
intent.type = mimeType //Can be "image/jpeg" or sth corresponding to the filetype.
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
val chooser = Intent.createChooser(intent, "Open with")
if (intent.resolveActivity(mContext.packageManager) != null)
mContext.startActivity(chooser)
else
mContext.showSnackBar("No suitable application to open file")
}
Did I use incorrect way to open file in Android?
First, there may not be an app capable of viewing the file type.
Second, you have not granted permission for the app to view the content. Use addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) on the ACTION_VIEW Intent.
val intent = Intent(Intent.ACTION_VIEW, uri)
intent.type = mimeType
Change to
val intent = Intent(Intent.ACTION_VIEW);
intent.setDataAndType (uri, mimeType );
You should set uri and mime type with one statement. Otherwise the receiving app will receive null for the uri. Most apps will not tell you that they did not receive an uri. The Gallery app for instance does not.
And i do not know the exact function name for Kotlin.
Fist of all, I want to say thank-you to #GreenApps, #Ankit, and #CommonsWare. Who spent time to investigate my issue.
I finally found the root cause when extracting the source to separated simple project. It's because I parse uri-string into uri-instance this way (the code of root-cause didn't included in my question. My apologies):
val uri = Uri.parse(URLDecoder.decode(uriString, "UTF-8")) as Uri
when I changed to val uri = Uri.parse(uriString) them problem solved.
Btw, I want to share the sample code for picker and previewer in my Github for someone may need to take a look. The java source is on master branch, and the Kotlin source is on kotlin-version branch. Using this simple code, now I can pick any file type (image, audio, video, pdf, xlsx,...) and then open it later via Intent.ACTION_VIEW.
Here it is:
https://github.com/IHNEL/UriPickerPreviewer
If you want to perform a perfect functionality with the file you should use any third party library for this functionality.
you should check the below link for the most common libraries for the file pickers:-
https://android-arsenal.com/tag/35?sort=created