Simon
Simon

Reputation: 415

FileProvider Xamarin not displaying file

I’m trying to display a PDF file using a file provider. However when the PDF reader opens it does not contain the content of the file. The file is stored in the internal storage of the application within the files folder.

Manifest:

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

File_paths:

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

pdfViewRenderer.cs:

string libraryPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal);   
var path = Path.Combine(libraryPath, fileName);


string application = "application/pdf";
Java.IO.File javaFile = new Java.IO.File(fileName);//externalPath  


Android.Net.Uri androidURI = FileProvider.GetUriForFile(Forms.Context.ApplicationContext, "com.mycom.myapp.fileprovider", javaFile);


Intent intent = new Intent(Intent.ActionView);
intent.SetDataAndType(androidURI, Filetype); 
intent.AddFlags(ActivityFlags.GrantReadUriPermission);
intent.SetFlags(ActivityFlags.NoHistory);
intent.SetFlags(ActivityFlags.ClearWhenTaskReset);


Forms.Context.StartActivity(Intent.CreateChooser(intent, "Open PDF"));

Upvotes: 1

Views: 3741

Answers (1)

SushiHangover
SushiHangover

Reputation: 74094

You can not directly access a file within the application's .apk, the `.apk' does not present itself as a filesystem.

You can copy an AndroidAsset to your application cache directory and then grant FileProvider permission to a file-based Uri to allowed external applications to access that file within your app's sandbox.

Add a provider within your AndroidManifest's application element:

<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.sushihangover.artificialgravitycalc"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
    android:name="android.support.FILE_PROVIDER_PATHS"
    android:resource="@xml/cache_path" />

Then add an xml file whose filename matches the provider's android:resource: to your Resources/xml that contains the path that you are granting access to:

<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <cache-path name="appcache" path="." />
</paths>

Note: You might have to add the xml directory to your Resources folder as by default it is not added via the basic Xamarin.Android project template. (and it must be named xml, lowercase)

Add your .pdf to the Assets directory and make sure that it is flagged as a AndroidAsset build type:

enter image description here

Now you can copy that Asset to your app's cache directory, create a FileProvider-based Uri, grant read access to it, and try to open it. In this case you need at least one installed application that publishes that it handles the application/pdf mime-type:

var pdfAssetName = "PlayScriptLanguageSpecification.pdf";
var savePath = Path.GetTempFileName();
using (var assetStream = Application.Context.Assets.Open(pdfAssetName))
using (var fileStream = new FileStream(savePath, FileMode.Create, FileAccess.Write))
{
    byte[] buffer = new byte[1024];
    int readCount = 0;
    do
    {
        readCount = assetStream.Read(buffer, 0, buffer.Length);
        fileStream.Write(buffer, 0, readCount);
    } while (readCount > 0);
}
var targetIntent = new Intent(Intent.ActionView);
var file = new Java.IO.File(savePath);
var uri = FileProvider.GetUriForFile(this, PackageName, file);
targetIntent.SetDataAndType(uri, "application/pdf");
targetIntent.SetFlags(ActivityFlags.NoHistory);
targetIntent.SetFlags(ActivityFlags.GrantReadUriPermission);
Intent intent = Intent.CreateChooser(targetIntent, "Open File");
StartActivity(intent);

Upvotes: 1

Related Questions