Friday, 5 November 2021

ContentProvider for files on removable micro-sd card for Android 11+

As FileProvider cannot serve from micro-sd card one has to make its own extending ContentProvider.

So i did time ago. And was working fine for all Android versions below 11.

It can serve files from the whole device including micro sd card.

So also from private getExternalFilesDirs()[0] and getExternalFilesDirs()[1].

Now see these two paths both on same micro-sd:

/storage/1234-5678/Documents/mytext.txt
/storage/1234-5678/Android/data/<package>/files/mytext.txt

They could be served.

But on an Android 11+ devices HTMLViewer and Chrome can only handle the first path. The app itself can always handle its own files using path or own provider.

On Android 11+ own apps that were choosen with ACTON_VIEW and used .readLine() to read from uri could handle first path and failed for second path. I could finally solve it for my own apps by not using .readLine() but looking at .available() and doing a direct .read() from inputstream.

This is the provider class i use:

public class ABCContentProvider extends ContentProvider {
String TAG = "abccontentprovider";

@Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException
{
    Log.d(TAG, "open-File() mode: " + mode );  // "r" | "w"
    Log.d(TAG, "open-File() uri.getEncodedPath(): " + uri.getEncodedPath() );

    String path = uri.getEncodedPath();

    File f = new File(path);

    if ( ! f.exists() )
    {
        Log.d(TAG, "path does not exist" );
        throw new FileNotFoundException(
                "in ABCProvider\n"
                        + path
                        + "\nmode: " + mode
        );
    }

    if ( mode.equals("r") )
        return (ParcelFileDescriptor.open(f,ParcelFileDescriptor.MODE_READ_ONLY));

    return (ParcelFileDescriptor.open(f,ParcelFileDescriptor.MODE_READ_WRITE));
}

// Omitted all other methods as they are not used.
// Android Studio will add them for you
}

In AndroidManifest.xml:

    <provider
        android:name=".ABCContentProvider"
        android:authorities="aaa.bbb.ccc.provider"
        android:enabled="true"
        android:exported="true" />

Use following uries for an ACTIEN_VIEW intent. (Change 1234-5678 accordingly to used micro sd card)

Uri uri1 = Uri.parse("content://aaa.bbb.ccc.provider/storage/1234-5678/Documents/mytext.txt");
Uri uri2 = Uri.parse("content://aaa.bbb.ccc.provider/storage/1234-5678/Android/data/<package>/files/mytext.txt");

Test the uries in the providing app first.

Following intent is used to start an external app.

 Intent intent = new Intent(Intent.ACTION_VIEW);
 intent.setDataAndType(uri, "text/plain");
 intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
 startActivity(intent);

My question is: Why, on Android 11 and 12, is ContentProvider for files on a micro sd card acting different for files from different locations?

I could solve the read problem for .txt files but if my external app wants to save edits it fails.



from ContentProvider for files on removable micro-sd card for Android 11+

No comments:

Post a Comment