Search code examples
androidandroid-fileproviderandroid-sharing

How to make Android's FileProvider working with external storage on Android 11+


I am trying to open a file stored in the root of the external storage in another application on Android 11+.

Following the FileProvider docs, I write the following code:

AndroidManifest.xml:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<application
    android:label="@string/app_name"
    android:requestLegacyExternalStorage="true">

    <activity
        android:name=".MainActivity"
        android:exported="true">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>

    <provider
        android:name="androidx.core.content.FileProvider"
        android:authorities="com.hmdm.fileprovidertest.provider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/provider_paths"/>
    </provider>

</application>

res/xml/provider_paths.xml:

<?xml version="1.0" encoding="utf-8"?>
<paths><external-path name="external_files" path="."/></paths>

MainActivity.java:

private static final String FILE_PATH = "/storage/emulated/0/sample.jpg";
private static final int PERMISSIONS_REQUEST = 1000;
private static final String LOG_TAG = "FileProviderTest";

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    Button button = findViewById(R.id.action);
    button.setOnClickListener(view -> openFile());
}

@Override
protected void onResume() {
    super.onResume();

    if (ContextCompat.checkSelfPermission(
            this, Manifest.permission.READ_EXTERNAL_STORAGE) !=
            PackageManager.PERMISSION_GRANTED) {
        requestPermissions(new String[]{
                Manifest.permission.READ_EXTERNAL_STORAGE
        }, PERMISSIONS_REQUEST);
    }
}

private void openFile() {
    Intent i = new Intent(Intent.ACTION_VIEW);

    Uri uri;
    File file = new File(FILE_PATH);
    try {
        uri = FileProvider.getUriForFile(this, getPackageName() + ".provider", file);
    } catch (/*IllegalArgument*/Exception e) {
        Toast.makeText(this, "Can't get URI for file: " + file.getPath(), Toast.LENGTH_LONG).show();
        return;
    }
    i.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

    Log.d(LOG_TAG, "Opening Content Provider URI: " + uri.toString());
    i.setData(uri);
    i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);

    try {
        startActivity(i);
    } catch (ActivityNotFoundException e) {
        Toast.makeText(this, "Failed to find activity for VIEW intent!", Toast.LENGTH_LONG).show();
    } catch (Exception e) {
        Toast.makeText(this, "Exception: " + e.getMessage(), Toast.LENGTH_LONG).show();
    }
}

The full code of the project can be found here: https://github.com/h-mdm/FileProviderTest

The problem is that when I'm trying to call the openFile() method for the existing image file, I'm getting the "Media not found" message from the system image viewer.

The logcat shows the message:

Failed to find provider info for com.hmdm.fileprovidertest.provider

The application is able to access the file (if you run the app and create the /storage/emulated/0/sample.jpg file, it will be displayed by the app itself).

The same code works well on Android 10 and below.

What I'm doing wrong? Are there any working examples of using FileProvider on Android 11+?


Solution

  • i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)

    That statement overwrites the addFlags() statement and hence the read permission you gave.

    Swap those statements. As addFlags() should be last.