2

Preface: I'm using C# with Xamarin, but the workflow is the same. I want to open/display (offline) HTML files (which are linked with other HTML, CSS and JavaScript files) in a browser on Android. This is the index.html:

<!DOCTYPE html>
<html data-mc-runtime-file-type="Default" class="_Skins_HTML5___Top_Navigation">
    <head>
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" /><title></title>
        <script type="text/javascript" src="Resources/Scripts/custom.modernizr.js">
        </script>
        <script type="text/javascript" src="Resources/Scripts/jquery.min.js">
        </script>
        <script type="text/javascript" src="Resources/Scripts/plugins.min.js">
        </script>
        <script type="text/javascript" src="Resources/Scripts/require.min.js">
        </script>
        <script type="text/javascript" src="Resources/Scripts/require.config.js">
        </script>
        <script type="text/javascript" src="Resources/Scripts/MadCapAll.js">
        </script>
    </head>
    <body>
    </body>
</html>

The files are located in the internal app storage /data/data/com.xxx.xxx/files/help and they are placed in different subfolders like following:

drwxrwxr-x 4 root root   4096 2018-04-26 09:22 Content
drwxrwxr-x 2 root root   4096 2018-04-26 09:22 Data
-rw-rw-rw- 1 root root  11327 2018-01-12 17:24 HTML5 - Top Navigation.mclog
drwxrwxr-x 3 root root   4096 2018-04-26 09:22 Resources
drwxrwxr-x 4 root root   4096 2018-04-26 09:22 Skins
-rw-rw-rw- 1 root root 344798 2018-01-12 17:24 csh.js
-rw-rw-rw- 1 root root    951 2018-01-12 17:24 index.html
-rw-rw-rw- 1 root root 348211 2018-01-12 17:24 index.js
-rw-rw-rw- 1 root root   1269 2018-01-12 17:24 index.mcwebhelp
-rw-rw-rw- 1 root root   2622 2018-01-12 17:24 index_CSH.html

I'm using the FileProvider for this (similar defined as in the Android Manifest):

[ContentProvider(new[] { "${applicationId}.html.fileprovider" }, Name = "com.xxx.xxx.HelpFileProvider", GrantUriPermissions = true, Exported = false)]
[MetaData("android.support.FILE_PROVIDER_PATHS", Resource = "@xml/help_html_provider_paths")]
class HelpFileProvider : FileProvider
{

}

I added the path in a XML-file (help_html_provider_paths.xml):

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

To open the index.html file from outside of the app, a Uri needs to be created:

Android.Net.Uri contentUri = FileProvider.GetUriForFile(_context, _context.PackageName + ".html.fileprovider", newFile);

But that's only for the index.html, trying to open it leads to an empty screen and the following error (multiple of them):

01-01 01:36:38.096 E/DatabaseUtils( 4924): Writing exception to parcel
01-01 01:36:38.096 E/DatabaseUtils( 4924): java.lang.SecurityException: Permission Denial: reading com.xxx.xxx.HelpFileProvider uri content://com.xxx.xxx.html.fileprovider/help/Resources/Scripts/jquery.min.js from pid=4989, uid=10052 requires the provider be exported, or grantUriPermission()
01-01 01:36:38.096 E/DatabaseUtils( 4924):  at android.content.ContentProvider.enforceReadPermissionInner(ContentProvider.java:608)
01-01 01:36:38.096 E/DatabaseUtils( 4924):  at android.content.ContentProvider$Transport.enforceReadPermission(ContentProvider.java:483)
01-01 01:36:38.096 E/DatabaseUtils( 4924):  at android.content.ContentProvider$Transport.enforceFilePermission(ContentProvider.java:474)
01-01 01:36:38.096 E/DatabaseUtils( 4924):  at android.content.ContentProvider$Transport.openTypedAssetFile(ContentProvider.java:419)
01-01 01:36:38.096 E/DatabaseUtils( 4924):  at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:313)
01-01 01:36:38.096 E/DatabaseUtils( 4924):  at android.os.Binder.execTransact(Binder.java:565)

I know - that means that the other files can't opened because they have no URI, only the index.html.

Then I tried different approaches (getting all file URIs and send them with ClipData) with no success like following:

public void OpenHtml(string directory, string file) // "PATH/TO/help/" and "index.html"
{
    try
    {
        Java.IO.File filePath = new File(_context.FilesDir.AbsolutePath, directory);
        Java.IO.File newFile = new File(filePath, file);

        List<File> allFiles = new List<File>();
        Listfiles(filePath.AbsolutePath, allFiles); // Get recursiv all files from each subfolder

        Android.Net.Uri contentUri = FileProvider.GetUriForFile(_context, _context.PackageName + ".html.fileprovider", newFile);
        ClipData clipData = ClipData.NewUri(_context.ContentResolver, "com.xxx.xxx", contentUri);

        allFiles.ForEach(file1 =>
        {
            Android.Net.Uri uri = FileProvider.GetUriForFile(_context, _context.PackageName + ".html.fileprovider", file1);
            clipData.AddItem(new ClipData.Item(uri));
        });

        var intent = new Intent(Intent.ActionView, contentUri);

        intent.ClipData = clipData;
        intent.AddFlags(ActivityFlags.GrantReadUriPermission);
        _context.StartActivity(intent);
    }
    catch (ActivityNotFoundException ex)
    {
        Logger.Error("No Browser installed!!", ex);
        throw new ApplicationException();
    }
    catch (Exception ex)
    {
        Logger.Error("Failed to display html", ex);
    }
}

Did I forget something? Or is this special case not possible because the links in the html file doesn't match the URI?

Fabman22
  • 183
  • 12

0 Answers0