androidxmldelphiandroid-intentfileprovider

Delphi cannot find fileprovider.xml building Android


I am trying to create a PDF document and then open it in an Android app. The code is based on the GitHub project https://github.com/AlessandroMartini/Delphi-Android-GeraPDF I am using Delphi in RAD Studio 11.

I have successfully worked around the permissions to allow my app to save the file that it created and am now stuck at trying to view the file I have created using Intent to allow the file to be opened in another app to view it.

In my main form I have the following:

  Intent := TJIntent.Create;
  Intent.setAction(TJIntent.JavaClass.ACTION_VIEW);

  { based on solution from https://stackoverflow.com/questions/54535225/android-os-fileuriexposedexception-filename-exposed-beyond-app-through-intent }
  var lfile := TJFile.JavaClass.init(FileNameJ);
  Intent.setFlags(TJIntent.JavaClass.FLAG_ACTIVITY_NO_HISTORY or
        TJIntent.JavaClass.FLAG_ACTIVITY_CLEAR_TOP);
  var LAuthority := TAndroidHelper.Context.getApplicationContext.getPackageName.concat(
        StringToJString('.fileprovider'));
  var Data := TJContent_FileProvider.JavaClass.getUriForFile(TAndroidHelper.Context,
        LAuthority,
        lFile);

  Intent.setDataAndType(Data, StringToJString('application/pdf'));

  TAndroidHelper.Activity.StartActivity(Intent);

This seems to be good so far.

My AndroidManifest.template.xml file is this, with the <provider></provider> section added to the default code:

<?xml version="1.0" encoding="utf-8"?>
<!-- BEGIN_INCLUDE(manifest) -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="%package%"
    android:versionCode="%versionCode%"
    android:versionName="%versionName%"
    android:installLocation="%installLocation%">
    <uses-sdk android:minSdkVersion="%minSdkVersion%" android:targetSdkVersion="%targetSdkVersion%" />
<%uses-permission%>
    <uses-feature android:glEsVersion="0x00020000" android:required="true"/>
    <queries>
<%queries-child-elements%>
    </queries>
    <application
        android:persistent="%persistent%"
        android:restoreAnyVersion="%restoreAnyVersion%"
        android:label="%label%"
        android:debuggable="%debuggable%"
        android:largeHeap="%largeHeap%"
        android:icon="%icon%"
        android:theme="%theme%"
        android:hardwareAccelerated="%hardwareAccelerated%"
        android:resizeableActivity="false"
        android:requestLegacyExternalStorage="true">
<%provider%>
        <provider android:name="android.support.v4.content.FileProvider"
            android:authorities="%package%.provider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/fileprovider" />
        </provider>
<%application-meta-data%>
<%uses-libraries%>
<%services%>
        <!-- Our activity is a subclass of the built-in NativeActivity framework class.
             This will take care of integrating with our NDK code. -->
        <activity
            android:name="com.embarcadero.firemonkey.FMXNativeActivity"
            android:label="%activityLabel%"
            android:configChanges="orientation|keyboard|keyboardHidden|screenSize"
            android:launchMode="singleTask">
            <!-- Tell NativeActivity the name of our .so -->
            <meta-data android:name="android.app.lib_name" android:value="%libNameValue%" />

            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
<%activity%>
<%receivers%>
    </application>
</manifest>
<!-- END_INCLUDE(manifest) -->

My fileprovider.xml file contains the following:

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

I am aware that the path value is not what I should be using, that I can fix later.

The problem comes after I have built this and try run it.

AndroidManifest.template.xml is in the root folder of my project source.

I have tried placing fileprovider.xml in the project root, ./res, ./xml and ./res/xml folders but I am getting a PAClient error indicating that fileprovider.xml can not be found.

[PAClient Error] Error: E2312 Unable to execute '"C:\Users\Public\Documents\Embarcadero\Studio\22.0\CatalogRepository\AndroidSDK-2525-22.0.42600.6491\build-tools\30.0.3\Aapt.exe" package -f -M "C:\Projects\Rad\JavaTest1\Android\Debug\TestPermissions\AndroidManifest.xml" -F "C:\Projects\Rad\JavaTest1\Android\Debug\TestPermissions\bin\TestPermissions-unaligned.apk" -I "C:\Users\Public\Documents\Embarcadero\Studio\22.0\CatalogRepository\AndroidSDK-2525-22.0.42600.6491\platforms\android-30\android.jar" -S "C:\Projects\Rad\JavaTest1\Android\Debug\TestPermissions\res" -A "C:\Projects\Rad\JavaTest1\Android\Debug\TestPermissions\assets" "C:\Projects\Rad\JavaTest1\Android\Debug\TestPermissions\library" "C:\Projects\Rad\JavaTest1\Android\Debug\TestPermissions\classes"' (Error 1)

[PAClient Error] Error: E2312 C:\Projects\Rad\JavaTest1\Android\Debug\TestPermissions\AndroidManifest.xml:38: error: Error: No resource found that matches the given name (at 'resource' with value '@xml/fileprovider').

Any ideas why fileprovider.xml is not found?


Solution

  • Found a solution from Dave Nottage at https://en.delphipraxis.net/topic/1361-android-how-to-call-a-tjintent/

    In Delphi 10.3 and newer, the provider section does not need to be manually added to AndroidManifest.template.xml.

    Change the Project Options, (right-click on the Project, then select Options).

    In Application->Entitlement List, check Secure File Sharing.

    If a provider section was manually added to AndroidManifest.template.xml then remove it.

    The fileprovider.xml file is perfectly happy to be in the project source root.

    Finally, make sure that the path indicated in fileprovider.xml is where the file(s) you want to share are located. In my case I am saving to '/storage/emulated/0/Download' so that is the value to put in fileprovider.xml.

    <?xml version="1.0" encoding="utf-8"?>
    <paths xmlns:android="http://schemas.android.com/apk/res/android">
        <cache-path name="external_files" path="/storage/emulated/0/Download"/>
    </paths>
    

    Still got one problem though. Although the file is being placed in the /storage/emulated/0/Download folder, and the various possible PDF readers on my test phone can find the file, they are not able to open it. I can navigate to the Download folder and open the file using the same viewers, so it looks like I still have some permission issues in my App.