1

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:

android.os.FileUriExposedException: file:///storage/emulated/0/test.txt exposed beyond app through Intent.getData()

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?

user11935527
  • 101
  • 1
  • 7
  • You do not have to declare a class inheriting from FileProvider. You can use FileProvider directly. In the code it is only adding one line. In the manifest file declare the provider. Well.. in Java. – blackapps Dec 18 '19 at 11:28
  • 3
    `TJFileProvider` is located in `Androidapi.JNI.Support` – Dalija Prasnikar Dec 18 '19 at 11:53

1 Answers1

2

I have migrated my project from Delphi 10.4 to Delphi 11.1 and TJFileProvider is flagged up as an undeclared identifier as well.

TJcontent_FileProvider.JavaClass.getUriForFile(....)

instead of

TJFileProvider.JavaClass.getUriForFile(....)

I hope this helps

mesutuk
  • 402
  • 3
  • 17