Nguyen  Minh Binh
Nguyen Minh Binh

Reputation: 24423

Android - Pick and View a file of any type

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")
    }

Upvotes: 4

Views: 5575

Answers (4)

Nguyen  Minh Binh
Nguyen Minh Binh

Reputation: 24423

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

Upvotes: 1

greenapps
greenapps

Reputation: 11214

  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.

Upvotes: 3

Android Geek
Android Geek

Reputation: 9225

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

Upvotes: 0

CommonsWare
CommonsWare

Reputation: 1006604

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.

Upvotes: 3

Related Questions