I require a cross-platform function to open a file using any app on the device registered to be able to open the file type concerned, along the lines of the WinAPI function ShellExecute
in Windows. Using various code examples that I found on Stack-overflow I finally came up with the following code which compiles with Delphi 10.3.
uses
{$ifdef Android}
FMX.Helpers.Android, AndroidAPI.Helpers,
AndroidApi.JNI.GraphicsContentViewText,
AndroidApi.JNI.Net, AndroidApi.JNI.JavaTypes;
{$endif Android}
{$ifDef MSWindows}
Winapi.ShellAPI, Winapi.Windows;
{$endif MSWindows}
procedure OpenFile(FilePathname: string; DisplayError: Boolean);
const
COption= 0;
var
Extension: string;
{$ifdef Android}
Intent: JIntent;
URI: Jnet_Uri;
{$endif Android}
Result: integer;
begin
Extension:= LowerCase(ExtractFileExt(FilePathname));
{$ifdef Android}
URI := TJnet_Uri.JavaClass.parse(StringToJString('file:///' + FilePathname));
Intent:= TJIntent.Create;
Intent.setFlags(TJIntent.JavaClass.FLAG_GRANT_READ_URI_PERMISSION);
Intent.setAction(TJIntent.JavaClass.ACTION_VIEW);
case COption of
0: Intent.setData(URI);
1:
if Extension= '.pdf' then
Intent.setDataAndType(URI,StringToJString('application/pdf'))
else if Extension= '.txt' then
Intent.setDataAndType(URI,StringToJString('text/*'));
end;
try
SharedActivity.startActivity(Intent);
except
on E: Exception do
begin
if DisplayError then
ShowMessage('Error: ' + E.Message);
end;
end;
{$endif Android}
{$ifdef MSWindows}
Result:= ShellExecute(0, 'Open', PChar(FilePathname), '', '', SW_SHOWNORMAL);
{$endif MSWindows}
end;
Applying this function to a local .PDF file on an SD card on an Android 9 device, the following exception is thrown:
android.os.FileUriExposedException: file:////SDCard/test.pdf exposed beyond app through Intent.getData()
I have found on StackOverflow the following thread which addresses this problem:
However the idea of having to declare and use a class inheriting from the Java class File-provider in order to give access to a particular file or folder and make it accessible to other apps, and then document this in the AndroidManifest.xml seems greatly over-complicated, particularly considering that all the files in the SD card are public by definition.
My questions, therefore, are:
Is there a simpler way of dealing with this issue in Delphi FMX 10.3+?
If not, how does one go about creating a Object Pascal class in Delphi inheriting from a Java class? A code example would be appreciated.
Addendum:
From a link to this post I have found the following code by Dave Nottage:
procedure OpenPDFA(const AFileName: string);
var
LIntent: JIntent;
LAuthority: JString;
LUri: Jnet_Uri;
begin
LAuthority := StringToJString(JStringToString(TAndroidHelper.Context.getApplicationContext.getPackageName) + '.fileprovider');
LUri := TJFileProvider.JavaClass.getUriForFile(TAndroidHelper.Context, LAuthority, TJFile.JavaClass.init(StringToJString(AFileName)));
LIntent := TJIntent.JavaClass.init(TJIntent.JavaClass.ACTION_VIEW);
LIntent.setDataAndType(LUri, StringToJString('application/pdf'));
LIntent.setFlags(TJIntent.JavaClass.FLAG_GRANT_READ_URI_PERMISSION);
TAndroidHelper.Activity.startActivity(LIntent);
end;
However the class TJFileProvider
is flagged up in the Delphi compiler as an undeclared identifier. The uses
list in my code seems to be the same as in Dave Nottage's code. So where is TJFileProvider declared?