FileUriExposedException in Android [duplicate]

I need to open folder in internal storage that contains images.

I use following code.

Java

 File folder = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),  "MyPhotos");
Intent intent = new Intent(Intent.ACTION_VIEW);
String path =folder.getPath();
Uri myImagesdir = Uri.parse("file://" + path );
intent.setDataAndType(myImagesdir,"*/*");   
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(intent);

PATHS

 <paths xmlns:android="http://schemas.android.com/apk/res/android">
     <external-path name="images" path="Pictures" />
     <external-path name="external_files" path="."/>
     <external-path name="files_root" path="Android/data/${applicationId}"/> </paths>

Manifest

  <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="com.example.android.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths" />
        </provider>

xml/file_paths

<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="images" path="Pictures" />
    <external-path name="external_files" path="."/>
    <external-path name="files_root" path="Android/data/${applicationId}"/>
</paths>

ERROR

FATAL EXCEPTION: main Process: android.apps.bnb.company.myphotos, PID: 22482 android.os.FileUriExposedException: file:///storage/emulated/0/Pictures/MyPhotos exposed beyond app through Intent.getData()

Is any another way to open folder in internal storage? Thanks!

UPDATE #1

Using this arcticle https://inthecheesefactory.com/blog/how-to-share-access-to-file-with-fileprovider-on-android-nougat/en I replaced

Uri myImagesdir = Uri.parse("file://" + path );

with

 Uri myImagesdir = Uri.parse("content://" + path );

And the error gone.

Anyway I have to choose always app to open this folder.

Is it possibility to use My Files app by default to open certain folder?


Solution 1:

Is it possibility to use My Files app by default to open certain folder?

Yes & No. Its not 100% guaranteed that it will work on all devices.

Edit 1:

Following is one of the way with which it can be done. I have tested on few emulators (running Android N & Android O) and loads default file explorer:

MainActivity.java

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent intent = new Intent(Intent.ACTION_VIEW);
        Uri dirUri = FileProvider.getUriForFile(this,getApplicationContext().getPackageName() + ".com.example.myapplication",Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS));
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        //intent.setDataAndtType(); I will change this value in the alternatives below
    }

AndroidManifest.xml

<provider
        android:name=".GenericFileProvider"
        android:authorities="${applicationId}.com.example.myapplication"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/file_paths" />
</provider>

GenericFileProvider.java

public class GenericFileProvider extends FileProvider {
}

file_paths.xml

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="external_files" path="."/>
</paths>

The above approach doesn't work on big players like samsung

Alternatives

1. Using type DocumentsContract.Document.MIME_TYPE_DIR

intent.setDataAndType(dirUri,DocumentsContract.Document.MIME_TYPE_DIR);

This approach works on several emulators and limited set of devices. It doesn't work with big players like Samsung or Huawei.

2. Using type resource/folder

intent.setDataAndType(dirUri,"resource/folder");

This approach works only if user has installed ES file explorer app.

If you choose to use , then you have to check if any intent is available to handle it by using:

PackageManager packageManager = getActivity().getPackageManager();

if (intent.resolveActivity(packageManager) != null) {
    startActivity(intent);
} else {
   // either display error or take necessary action
}

3. Using type */*

intent.setDataAndType(dirUri,"*/*");

This approach works, if user chooses File Manager app from the Intent Chooser and mark it as default app to handle */*. However it has some drawbacks (Thanks to @CommonsWare for bringing some of it out):

  • This type will load all the apps on the device and allow user to choose one of them to complete the action.
  • If there is no file explorer and user chooses other apps to load your intent, then the other app will crash or simply show black screen. E.g. You use Gallery or some other app to launch it rather than file explorer, then Gallery app will either crash or show black screen.
  • Even if there is file explorer but user decides to use other apps, the other apps could crash

4. Using type text/csv

intent.setDataAndType(uri, "text/csv")

This will limit the number of apps which will displayed to the user but same limitations when */* used is applied. Apps which can handle csv will be displayed and if user chooses it then the apps would crash.

There are some device specific implementations available here as mentioned by @Academy of Programmer which requires to identify the default file manager's intent and extra's need by it.

Conclusion:

There is no standard type available to achieve it since there is no standard followed by the File Managers to support specific type at the moment. In future may be Google will come up with some approach. Best alternative would be to implement your own file manager just like Dropbox or Google Drive does. There are several libraries available which provide this feature.

Solution 2:

The Below Code is used by me to open an image from Storage :

StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
StrictMode.setVmPolicy(builder.build());

File path = new File(Environment.getExternalStorageDirectory() + "/" + "ParentDirectory" + "/" + "ChildDirectory");

File filepath = new File(path + "/" + yourImageName.png);

Uri uri = Uri.fromFile(filepath);

Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(uri, "image/jpeg");
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);

Edited Answer :

StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
StrictMode.setVmPolicy(builder.build());

File path = new File(Environment.getExternalStorageDirectory() + "/" + "ParentDirectory" + "/" + "ChildDirectory");

Uri uri = Uri.fromFile(path);

Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(uri, "image/jpeg");
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);