I am trying to open a pdf in a 3rd party app on the android device, but it's not possible. The applicaiton is built in xamarin forms with a shared project. I have used the solution provided here as a basis: Opening a PDF with Xamarin Forms.
//I changed the interface to a byte array because I want to download the pdf
public interface IDocumentView
{
void DocumentView(byte[] bytes,string name, string title);
}
//In my android project:
[assembly: Xamarin.Forms.Dependency(typeof(DocumentView))]
namespace FMSXMobileApp.Droid
{
public class DocumentView : IDocumentView
{
void IDocumentView.DocumentView(byte[] bytes, string name, string title)
{
var context = Xamarin.Forms.Forms.Context;
try
{
File extFile = new File(Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryDownloads), name);
File extDir = extFile.ParentFile;
// Copy file to external storage to allow other apps to access ist
if (System.IO.File.Exists(extFile.AbsolutePath))
System.IO.File.Delete(extFile.AbsolutePath);
System.IO.File.WriteAllBytes(extFile.AbsolutePath, bytes);
String mime = MimeTypeMap.GetFileExtensionFromUrl(extFile.AbsolutePath);
// if copying was successful, start Intent for opening this file
if (System.IO.File.Exists(extFile.AbsolutePath))
{
Intent intent = new Intent();
intent.SetAction(Android.Content.Intent.ActionView);
intent.SetDataAndType(Android.Net.Uri.FromFile(extFile), mime);
context.ApplicationContext.StartActivity(intent);
}
}
catch (ActivityNotFoundException anfe)
{
// android could not find a suitable app for this file
var alert = new AlertDialog.Builder(context);
alert.SetTitle("Error");
alert.SetMessage("No suitable app found to open this file");
alert.SetCancelable(false);
alert.SetPositiveButton("Okay", (object sender, DialogClickEventArgs e) => ((AlertDialog)sender).Hide());
alert.Show();
}
catch (Exception ex)
{
// another exception
var alert = new AlertDialog.Builder(context);
alert.SetTitle("Error");
alert.SetMessage("Error when opening document");
alert.SetCancelable(false);
alert.SetPositiveButton("Okay", (object sender, DialogClickEventArgs e) => ((AlertDialog)sender).Hide());
alert.Show();
}
}
}
}
//From my xamarin forms shared project I call the next statement:
DependencyService.Get<IDocumentView>().DocumentView(doc.File, "test.pdf", "Title of the view");
//In the xml folder of my android project (file_paths.xml):
<?xml version="1.0" encoding="utf-8" ?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-files-path name="root" path="/"/>
<external-files-path name="files" path="files" />
</paths>
//in the manifest I added:
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.mydomain.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
The applicaiton fails at this line:
System.IO.File.WriteAllBytes(extFile.AbsolutePath, bytes);
But the code doesn't run I get the error message: Error when opening document. What am I doing wrong? I'm using android 9.0 sdk 28.
Here is the stack trace:
System.UnauthorizedAccessException: Access to the path "/storage/emulated/0/Download/test.pdf" is denied.
at System.IO.FileStream..ctor (System.String path, System.IO.FileMode mode, System.IO.FileAccess access, System.IO.FileShare share, System.Int32 bufferSize, System.Boolean anonymous, System.IO.FileOptions options) [0x001b7] in <ff07eae8184a40a08e79049bbcb31a0e>:0
at System.IO.FileStream..ctor (System.String path, System.IO.FileMode mode, System.IO.FileAccess access, System.IO.FileShare share) [0x00000] in <ff07eae8184a40a08e79049bbcb31a0e>:0
at (wrapper remoting-invoke-with-check) System.IO.FileStream..ctor(string,System.IO.FileMode,System.IO.FileAccess,System.IO.FileShare)
at System.IO.File.InternalWriteAllBytes (System.String path, System.Byte[] bytes) [0x00000] in <ff07eae8184a40a08e79049bbcb31a0e>:0
at System.IO.File.WriteAllBytes (System.String path, System.Byte[] bytes) [0x00039] in <ff07eae8184a40a08e79049bbcb31a0e>:0
at FMSXMobileApp.Droid.DocumentView.FMSXMobileApp.Interfaces.IDocumentView.DocumentView (System.Byte[] bytes, System.String name, System.String title) [0x0003d] in D:\FMSXAPP\FMSXMobileApp\FMSXMobileApp.Android\DocumentView.cs:28
Finally I have found a solution that worked for me. The problem was that my app needed to request access to storage. I used this library for the requests https://www.nuget.org/packages/Plugin.Permissions/.
This is my code from the documents.xaml page:
In the MainActivity of my .android project I had to add this code:
This is my new DocumentView in mij .android project:
I didn't change anything in the manifest file. I did change my file_paths.xml file:
After this modifications it worked like a charm. I hope that this may help someone who is also struggeling to get it work.
As CrazyTim answered in https://stackoverflow.com/a/24796126/11992780
There are 4 scenarios :