I am showing pdf files and images in my app via a custom webview, which is showing a little html site which is located in my app assets folder. This "homepage" gets the file to be shown via get parameters. The file to be shown is inside my private apps documents folder. This worked fine before I had to target API level 30 or higher, which enforces the usage of scoped storage. Now the "homepage" can't access the file that's inside the private documents folder (access violation). I would like to know if there is a way to still use this method of showing pdf files and images inside your app via a webview with scoped storage enabled.
Here are the details how it's currently implemented:
In the OnElementChanged method of my android renderer for my custom webview (extends the Xamarin.Forms.WebView with a Uri Property) I set up my Android webView (the Control) and then load the uri to the image homepage or the pdf.js webviewer, with the file (image or pdf) in the get parameters in the uri:
protected override void OnElementChanged(ElementChangedEventArgs<WebView> e)
{
base.OnElementChanged(e);
if (e.NewElement != null)
{
var customWebView = Element as CustomWebView;
Control.Settings.AllowUniversalAccessFromFileURLs = true;
Control.Settings.SetSupportZoom(true);
Control.Settings.BuiltInZoomControls = true;
Control.Settings.DisplayZoomControls = true;
int width = MainApplication.intWidth;
string PathCopy = customWebView.Uri;
customWebView.Uri = customWebView.Uri.Replace(" ", "%20");
string Extension = Path.GetExtension(WebUtility.UrlEncode(customWebView.Uri)).ToUpper();
switch (Extension)
{
case ".PDF": Control.LoadUrl(string.Format("file:///android_asset/pdfjs/web/viewer.html?file={0}", WebUtility.UrlEncode(customWebView.Uri))); break;
default: Control.LoadUrl(string.Format("file:///android_asset/ImageHomepage/ImageHomepage.html?pfad={0}&width={1}", customWebView.Uri, width)); break;
}
}
}
For pdf files I'm using pdf.js (as shown in this example: https://github.com/xamarin/docs-archive/tree/master/Recipes/xamarin-forms/Controls/display-pdf) and for images this little html site:
<head>
<script>
function getURLParameter(name)
{
var value = decodeURIComponent((RegExp(name + '=' + '(.+?)(&|$)').exec(location.search) || [, ""])[1]);
return (value !== 'null') ? value : false;
}
</script>
</head>
<body>
<script language = "Javascript" type="text/javascript">
<!--
var param = getURLParameter('pfad');
var width = getURLParameter('width');
document.write('<img src="');document.write(param); document.write('" width="'); document.write(width); document.write('" style="width:100%" >');
//-->
</script>
</body>
The files to be shown are downloaded from my webservice and stored inside my "PrivateDocumentsFolder" which is declared like this:
public static string PrivateFolder = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
public static string PrivateDocumentsFolder = Path.Combine(PrivateFolder, "Dokumente");
So the files are stored at: /data/user/0/myPackageName/files/Dokumente, which is the private folder I also get when accessing the FilesDir property of my android context object.
Targetting API 29 with requestLegacyExternalStorage set to true works with this method. Opening the files with Xamarin.Essentials Launcher works with scoped storage enabled but I'd like to continue to show the files inside my app and not in an external app.
How can I use this method with API Level >29 and scoped storage enabled? Using Environment.SpecialFolder.MyDocuments should be the correct way if I understand this correctly, because it is inside my apps private scope and there shouldn't be a problem to access these files from inside my app.