0

Due to the storage access changes that were made on Android 11, SDK 30, I have changed the path where I save my PDF files and images.

Before, I was using this:

File file = new File(Environment.getExternalStorageDirectory() + "/" + folderName + "/" + fileName);

Here is the file provider:

<provider
    android:name="androidx.core.content.FileProvider"
    android:authorities="${applicationId}.provider"
    android:exported="false"
    android:grantUriPermissions="true">

  <meta-data
      android:name="android.support.FILE_PROVIDER_PATHS"
      android:resource="@xml/provider_paths" />

</provider>

Here is the provider_paths.xml before the change of the path:

<paths>
    <external-path name="external_files" path="myFolder/"/>
    <files-path name="files" path="docs/" />
</paths>

Now, I have changed the path in:

 File file = new File(context.getExternalFilesDir(null) + "/" + folderName + "/" + fileName);

The PDF files and the images are saved successfully.

But when I try to share the PDF from my PDFView in the app, the application crashes at the FileProvider.getUriForFile(...). It was working fine before I changed the path.

case R.id.action_share:
    Intent intentShare = null;

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        Uri u=FileProvider.getUriForFile(getBaseActivity(), AUTHORITY, file);
        intentShare = new Intent(Intent.ACTION_SEND);
        intentShare.setType("application/pdf");
        intentShare.putExtra(Intent.EXTRA_STREAM, u);
        intentShare.putExtra(Intent.EXTRA_SUBJECT, "Sharing File...");
        intentShare.putExtra(Intent.EXTRA_TEXT, "Sharing File...");
        intentShare.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

        startActivity(intentShare);

    }

getBaseActivity() returns the context

AUTHORITY is "[name of package].provider"

I have changed the File Provider many times, trying many combinations, including the following (I put all separately not all at once) and nothing seems to work..

<external-files-path name="external_files" path="." />
<external-files-path name="external_files" path="/" />
<external-files-path name="my_folder" path="myFolder/" />
<external-path name="my_folder" path="Android/data/[name of the package]/files/myFolder" />
<files-path name="files" path="." />
<external-files-path name="external_files" path="." />

I keep getting this error and I cannot find a suitable solution, I've been stuck here for 3 weeks...

Here are the logcat lines:

2021-12-09 15:09:09.771 23495-23495/[package name] k E/AndroidRuntime: FATAL EXCEPTION: main Process: [package name], PID: 23495 java.lang.IllegalArgumentException: Failed to find configured root that contains /storage/emulated/0/Android/data/[package name]/files/myFolder/879881480803.pdf at androidx.core.content.FileProvider$SimplePathStrategy.getUriForFile(FileProvider.java:744) at androidx.core.content.FileProvider.getUriForFile(FileProvider.java:418) at hr.asseco.ui.activity.fragment.PdfFragment.onOptionsItemSelected(PdfFragment.java:145) at androidx.fragment.app.Fragment.performOptionsItemSelected(Fragment.java:2733) at androidx.fragment.app.FragmentManagerImpl.dispatchOptionsItemSelected(FragmentManagerImpl.java:2758) at androidx.fragment.app.FragmentController.dispatchOptionsItemSelected(FragmentController.java:411) at androidx.fragment.app.FragmentActivity.onMenuItemSelected(FragmentActivity.java:390) at androidx.appcompat.app.AppCompatActivity.onMenuItemSelected(AppCompatActivity.java:228) at androidx.appcompat.view.WindowCallbackWrapper.onMenuItemSelected(WindowCallbackWrapper.java:109) at androidx.appcompat.view.WindowCallbackWrapper.onMenuItemSelected(WindowCallbackWrapper.java:109) at androidx.appcompat.app.ToolbarActionBar$2.onMenuItemClick(ToolbarActionBar.java:65) at androidx.appcompat.widget.Toolbar$1.onMenuItemClick(Toolbar.java:207) at androidx.appcompat.widget.ActionMenuView$MenuBuilderCallback.onMenuItemSelected(ActionMenuView.java:779) at androidx.appcompat.view.menu.MenuBuilder.dispatchMenuItemSelected(MenuBuilder.java:834) at androidx.appcompat.view.menu.MenuItemImpl.invoke(MenuItemImpl.java:158) at androidx.appcompat.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:985) at androidx.appcompat.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:975) at androidx.appcompat.widget.ActionMenuView.invokeItem(ActionMenuView.java:623) at androidx.appcompat.view.menu.ActionMenuItemView.onClick(ActionMenuItemView.java:151) at android.view.View.performClick(View.java:7161) at android.view.View.performClickInternal(View.java:7138) at android.view.View.access$3500(View.java:811) at android.view.View$PerformClick.run(View.java:27419) at android.os.Handler.handleCallback(Handler.java:883) at android.os.Handler.dispatchMessage(Handler.java:100) at android.os.Looper.loop(Looper.java:221) at android.app.ActivityThread.main(ActivityThread.java:7542) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:539) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950)

Bruno
  • 3,872
  • 4
  • 20
  • 37
Dipsy
  • 141
  • 1
  • 1
  • 7
  • Check this: https://stackoverflow.com/a/52849797/6819340 – Darshan Dec 09 '21 at 12:25
  • Please check this: https://stackoverflow.com/a/42516202/4185813 – Ajay Kulkarni Dec 09 '21 at 12:50
  • @DarShan tried it, not working – Dipsy Dec 09 '21 at 13:58
  • @AjayKulkarni tried that too, not working – Dipsy Dec 09 '21 at 13:58
  • @blackapps I added the logcat lines and the code where the exception happens – Dipsy Dec 09 '21 at 14:16
  • `` That should do it. Put only one for external-files-path in the xml file. Maybe that underscore is not allowed. – blackapps Dec 09 '21 at 14:23
  • @blackapps i tried it, doesn't work, I also edited the post – Dipsy Dec 09 '21 at 14:37
  • And why dont you use the right names in your code? ` "/" + folderName + "/" + fileName` ? As we can see already: `/files/myFolder/879881480803.pdf` – blackapps Dec 09 '21 at 14:50
  • If you put `String AUTHORITY = getPackageName() + ".provider";` In your code. And here then everybody can see what you do. And you dont have to tell afterwards what AUTHORITY stands for. – blackapps Dec 09 '21 at 14:55
  • @blackapps this is an existing project that I've started to work on and I don't have much experience in Andorid(I am a student), my colleagues that are now on a long vacation have written this and I need to fix this problem. They know what AUTHORITY means in our code and I should not change that. I have provided the code for the provider. – Dipsy Dec 09 '21 at 15:03
  • You should not change their code but just make your own little function which you can post here. Code everybody can copy and paste to try out. You dont have to post the lines after getUriForFile() if getUriForFile is the problem. – blackapps Dec 09 '21 at 15:18
  • `Here is the provider_paths.xml before the change of the path:` Change it to like i suggested. – blackapps Dec 09 '21 at 15:19

2 Answers2

0

Following is in Kotlin code but it works for me I think it will help your problem

Manifest

<application
        ....
        android:requestLegacyExternalStorage="true">

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

xml/provider_paths

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

Code

var myPdfFilePath: File? = null
myPdfFilePath= File(checkFolder(), "myPdfFile.pdf") 

private fun checkFolder(): File {

    val root = if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q) {
        File(getExternalFilesDir(null), folderName)
    } else {
        File(Environment.getExternalStorageDirectory(), folderName)
    }

    var isDirectoryCreated: Boolean = root.exists()
    if (!isDirectoryCreated) {
        isDirectoryCreated = root.mkdir()
    }
    Log.d("Folder", "Created ? $isDirectoryCreated")
    return root
}

private fun sharePdfFile() {
        val contentUri: Uri = FileProvider.getUriForFile(
            applicationContext,
            "com.example.pdf.provider",
            myPdfFilePath!!
        )

        if (myPdfFilePath?.exists()!!) {
            val shareIntent = Intent()
            shareIntent.action = Intent.ACTION_SEND
            shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
            shareIntent.setDataAndType(contentUri, contentResolver.getType(contentUri))
            shareIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(myPdfFilePath))
            startActivity(Intent.createChooser(shareIntent, "Choose an app"))
        }

    }
Yaqoob Bhatti
  • 1,271
  • 3
  • 14
  • 30
0
For Android 11 and Above:

**Manifest File**:

 <provider
            android:name="androidx.core.content.FileProvider"
            android:authorities="(packagename).provider"(.provider name would be used to pass in parameter so please provide same name here too)
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths" />
        </provider>

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

</paths>

You can understand the the type of path from [docs][1].

And if your path contains "storage/emulated/0" just pass "/" in path value of xml.

And in code just call:
Uri uri = FileProvider.getUriForFile(getActivity(),
                "provider"(provide the same name defined in manifest of provider)enter code here, // Over here
                new File(filePath));


  [1]: https://developer.android.com/reference/androidx/core/content/FileProvider