2

I have a file in my raw assets directory which I am trying to save to shared phone storage that will allow other apps to open and view this video.

I am able to save the file to my local app storage or to external private app storage using the following code.

    Intent in = new Intent(Intent.ACTION_VIEW);

    // using getFilesDir() below causes this error: 
    File videoFile = new File(getExternalFilesDirs(null)[1], "talking.mp4");

    // check if we have already copied file over
    if (!videoFile.exists()) {

        try {
            InputStream is = getResources().openRawResource(R.raw.talking);
            boolean newFile = videoFile.createNewFile();
            if (!newFile) {
                throw new Exception("Failed to create file");
            }
            OutputStream os = new FileOutputStream(videoFile);
            byte[] data = new byte[is.available()];
            is.read(data);
            os.write(data);
            is.close();
            os.close();
        } catch (IOException e) {
            Log.w("ExternalStorage", "Error writing " + videoFile, e);
            Toast.makeText(this, "Failed to copy talking.mp4", Toast.LENGTH_LONG).show();
            return;
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // Line below 135  
    Uri uri =  FileProvider.getUriForFile(getApplicationContext(), getPackageName(), videoFile);
    // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Problematic Line

    // type of file to view
    in.setDataAndType(uri, "video/*");

    // ask some other app to deal with it
    startActivity(in);

The problem is I keep getting this error:

Caused by: java.lang.IllegalArgumentException: Failed to find configured root that contains 
    /storage/1B12-3A08/Android/data/${getPackageName()}/files/talking.mp4

        at androidx.core.content.FileProvider$SimplePathStrategy.getUriForFile(FileProvider.java:744)
        at androidx.core.content.FileProvider.getUriForFile(FileProvider.java:418)
        at ${getPackageName()}.MainActivity.btnPlayIntentClicked(MainActivity.java:135)

Having done a quick bit of research with the help of this link and FileProvider, I added this to my Manifest:

<application ... >
    //...
    <provider
        android:name="androidx.core.content.FileProvider"
        android:authorities="${getPackageName()}"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/paths" />
    </provider> 
</application>

and have the following resource at res/xml/paths.xml:

<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <files-path name="my_files" path="." />
    <external-files-path name="my_files" path="." />
    <external-path name="my_files" path="." />
</paths>

Question:

Having configured the available content root, I should be able to copy over a file from raw resources to external private storage and request Android (via implicit intent) to play the video using e.g. MX Player or VLC for Android.

What am I missing with the content root configuration? (I added both external-files-path and external-path for good measure)


Update after @Commonsware's answer

I changed

File videoFile = new File(getExternalFilesDirs(null)[1], "talking.mp4");

to

File videoFile = new File(getExternalFilesDir(null), "talking.mp4");

which is also just

File videoFile = new File(getExternalFilesDirs(null)[0], "talking.mp4");

But this gave the same error, but just another path prefix to the file:

Caused by: java.lang.IllegalArgumentException: Failed to find configured root that contains /storage/emulated/0/Android/data/${getPackageName()}/files/talking.mp4

I was able to get the file the content root to work and pass the file Uri with an Intent, which gave the option of open with VLC (for Android) and a few others, problem was non of the apps could play the file. I cannot remember how I did it though


Update 2

@BlackApps's suggestion of changing the path names in the res/xml/paths.xml file solved the problem

Initially I had different names, but it had not worked and didn't consider changing them back after some experimentation.

Reason why it caused the problem:

Content Root paths are loaded on app start via this function:

FileProvider.parsePathStrategy(Context, String)

Inside, it create a cache (Map) of all paths by using their corresponding file provider paths to provide additional context (appending a path to the specified path in the <external-file/>,<files-path/>. Given the input:

<external-file name="some_name" path="MyAwesomeAssets"/>

will result in an (external) target path (of Environment.getExternalStorageDirectory()) prepended to the path "MyAweomseAssets" resulting in a output path of /storage/emulated/0/MyAwesomeAssets.

This is then added to a cache, but in my case since I had the same name for each, it would simply override the last path i.e. FIFO approach.

So, when I am attempting to get the Uri of a file in the content roots I specified, there will only be one found, and that would be the last one defined in res/xml/paths.xml.

CybeX
  • 2,060
  • 3
  • 48
  • 115
  • `name="my_files" ` You have three times the same name. Make them different. – blackapps Dec 09 '20 at 09:20
  • @blackapps yes - that solved the issue. Had given it different names before but that didn't help either. Regardless, your answer solves the problem. – CybeX Dec 09 '20 at 10:07

1 Answers1

0

FileProvider does not support removable storage. Either change getExternalFilesDirs(null)[1] to getExternalFilesDir(null), or you will need to write your own ContentProvider to serve the content from your desired location (or directly from assets, if you prefer).

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • @CybeX: `` maps to `getExternalFilesDir(null)`, so it is unclear why your setup is not working. You might want to examine your merged manifest and confirm that your `` element is there and is unchanged -- if some library happens to also be using your same authority string, perhaps its metadata is getting picked up for some reason. – CommonsWare Dec 09 '20 at 00:53
  • for the record, this is a new VM - check the manifest and provider is as shown above - I am quite puzzled. – CybeX Dec 09 '20 at 05:14