Xamarin FileProvider can't open files in Android

Hikari91Hikari91 ITMember ✭✭
edited March 20 in Xamarin.Forms

Hi,
in my Xamarin Forms application I create pdfs that can be opened with Android application.

With device with Android 7 all works fine, now I have tested it with Android 8 and the file can't be open.

I have installed the Xam.PluginMedia and leave all default settings:

in the MainActivity I have override the OnRequestPermissionsResult function with:

Plugin.Permissions.PermissionsImplementation.Current.OnRequestPermissionsResult(requestCode, permissions, grantResults);

the file_paths.xaml is:

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

and in the AndroidManifest.xml I have added:

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

The code to open the preview is this:

public void OpenPreviewFile(string FilePath)
{

    Java.IO.File JavaFile = new Java.IO.File(FilePath);   

    Android.Net.Uri Path = FileProvider.GetUriForFile(global::Android.App.Application.Context, global::Android.App.Application.Context.PackageName + ".fileprovider", JavaFile);

    string Extension = global::Android.Webkit.MimeTypeMap.GetFileExtensionFromUrl(Path.ToString());
    string MimeType = global::Android.Webkit.MimeTypeMap.Singleton.GetMimeTypeFromExtension(Extension);
    Intent Intent = new Intent(Intent.ActionView);

    Intent.SetDataAndType(Path, MimeType);
    Intent.AddFlags(ActivityFlags.GrantReadUriPermission);
    Intent.SetFlags(ActivityFlags.ClearWhenTaskReset | ActivityFlags.NewTask);

    try
    {
        MainActivity.AppInstance.StartActivity(Intent.CreateChooser(Intent, TS.Translate("ANDROID_ScegliApp")));
    }
    catch (System.Exception)
    {
        Toast.MakeText(MainActivity.AppInstance, TS.Translate("ANDROID_ImpossibileAprireIlFIle"), ToastLength.Short).Show();
    }
}

The files generated are under this path:
Android/data/MY_PACKAGE_ID/files/GeneratedReports

I have attached a video to show the actual behavior.

Thank you :smiley:

Best Answer

  • Hikari91Hikari91 IT ✭✭
    edited March 25 Accepted Answer

    Hi,
    I found the problem.

    Inside the file_paths.xml, the "path" value of the nodes must be started with "/",
    so with this node:
    <files-path name="GeneratedReports" path="/GeneratedReports" />

    my code works!

    Thank you all!

    EDIT:
    Also I need to remove MyIntent.SetFlags(ActivityFlags.ClearWhenTaskReset | ActivityFlags.NewTask);

Answers

  • jezhjezh Member, Xamarin Team Xamurai

    Could you please post a basic demo so that we can test with it?

  • Hikari91Hikari91 ITMember ✭✭

    @jezh said:
    Could you please post a basic demo so that we can test with it?

    Here a sample project.

    Start the application, then open with it a file (e.g a PDF), then the file name will appears in the list, tap on it and open with another application (e.g. Acrobat Reader).

    A Toast with the error will appear.

    :smile:

  • JoeMankeJoeManke USMember ✭✭✭✭✭
    edited March 21

    Without digging too deep into it, my first guess is that it's a problem with your file_paths.xml file. You only include external paths to Pictures and Movies directories, and I hope you aren't storing the PDFs in either of those folders. This is the paths.xml file I have in my app:

    <?xml version="1.0" encoding="utf-8" ?>
    <paths xmlns:android="http://schemas.android.com/apk/res/android">
      <files-path name="my_files" path="."/>
    </paths>
    
  • Hikari91Hikari91 ITMember ✭✭

    @JoeManke said:
    Without digging too deep into it, my first guess is that it's a problem with your file_paths.xml file. You only include external paths to Pictures and Movies directories, and I hope you aren't storing the PDFs in either of those folders. This is the paths.xml file I have in my app:

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

    Thank you,
    I store the PDFs in a folder named GeneratedReportsunder the folder files.
    This is the path:

    Android/data/MY_PACKAGE_ID/files/GeneratedReports

    I have tried with your file_paths.xml but doesnt't works for me, throw this error:

    Java.Lang.IllegalArgumentException: Failed to find configured root that contains /storage/emulated/0/Android/data/it.volos.SgatEvo/files/GeneratedReports/maptec_2019-03-20T15-08-56_0a62183f-5551-4bc0-8c35-740748327df7_Report.pdf
    
  • JoeMankeJoeManke USMember ✭✭✭✭✭
    edited March 21

    What is the value of your FilePath variable when you pass it into OpenPreviewFile()? And can you also post the code where you are creating the PDFs?

  • Hikari91Hikari91 ITMember ✭✭

    @JoeManke said:
    What is the value of your FilePath variable when you pass it into OpenPreviewFile()? And can you also post the code where you are creating the PDFs?

    Hi,
    the FilePath value is
    /storage/emulated/0/Android/data/it.volos.SgatEvo/files/GeneratedReports/myPDF.pdf.
    I create the PDFs using Syncfusion's library, but the problem also occurs if I load inside the app a PDF from my pc or downloaded from internet.

  • JoeMankeJoeManke USMember ✭✭✭✭✭
    edited March 22

    That looks like you're writing the PDFs in external storage, so I guess you do need an external-files-paths tag in file_paths.xml. Probably this: <external-files-path name="reports" path="GeneratedReports" />

    Alternatively, I would recommend you stop writing files in external storage and instead put them in your internal storage using this: Java.IO.File file = context.GetFileStreamPath(filename);

  • Hikari91Hikari91 ITMember ✭✭
    edited March 25

    @JoeManke said:
    That looks like you're writing the PDFs in external storage, so I guess you do need an external-files-paths tag in file_paths.xml. Probably this: <external-files-path name="reports" path="GeneratedReports" />

    Alternatively, I would recommend you stop writing files in external storage and instead put them in your internal storage using this: Java.IO.File file = context.GetFileStreamPath(filename);

    Hi,
    thank you for the reply.

    I have tried to write file in internal storage but it doesn't works.
    I have changed the saving path from
    global::Android.App.Application.Context.GetExternalFilesDir(null).AbsolutePath;
    to
    global::Android.App.Application.Context.GetFileStreamPath("").AbsolutePath;

    and the file_paths.xml to
    <files-path name="files" path="."/>

    Now the full path are:
    Save:
    /data/user/0/MY_PACKAGE/files/GeneratedReports/myPdf.pdf
    when I try to open it:
    content://MY_PACKAGE.fileprovider/files/GeneratedReports/myPdf.pdf

    With external storage settings:
    Save path:
    global::Android.App.Application.Context.GetExternalFilesDir(null).AbsolutePath;

    in file_paths.xml:
    <external-files-path name="files" path="GeneratedReports" />

    Full save path:
    /storage/emulated/0/Android/data/MY_PACKAGE/files/GeneratedReports/myPdf.pdf

    Open path:
    content://it.volos.SgatEvo.fileprovider/files/myPdf.pdf

  • Hikari91Hikari91 ITMember ✭✭
    edited March 25 Accepted Answer

    Hi,
    I found the problem.

    Inside the file_paths.xml, the "path" value of the nodes must be started with "/",
    so with this node:
    <files-path name="GeneratedReports" path="/GeneratedReports" />

    my code works!

    Thank you all!

    EDIT:
    Also I need to remove MyIntent.SetFlags(ActivityFlags.ClearWhenTaskReset | ActivityFlags.NewTask);

Sign In or Register to comment.