3

I'm trying to get a directory files list in order to have a list and, later, choose one under android and delphi 11.0. After severals long search (here) and unsuccessful tries at the end i found this post.

Delphi Rio fails to read external storage with READ_EXTERNAL_STORAGE permissions set

Where Dalija gave this code:

uses
  System.Permissions,
  Androidapi.Helpers,
  Androidapi.JNI.App,
  Androidapi.JNI.OS,
  ...

procedure TMainForm.AddFiles;
var
  LFiles: TArray<string>;
  LFile: string;
begin
  LFiles := TDirectory.GetFiles(TPath.GetSharedDownloadsPath);
  for LFile in LFiles do
    begin
      Memo1.Lines.Add(LFile);
    end;
end;

procedure TMainForm.Button1Click(Sender: TObject);
begin
  PermissionsService.RequestPermissions([JStringToString(TJManifest_permission.JavaClass.READ_EXTERNAL_STORAGE)],
    procedure(const APermissions: TArray<string>; const AGrantResults: TArray<TPermissionStatus>)
    begin
      if (Length(AGrantResults) = 1) and (AGrantResults[0] = TPermissionStatus.Granted) then
        begin
          Memo1.Lines.Add('GRANTED');
          AddFiles;
        end
      else
        begin
          Memo1.Lines.Add('NOT GRANTED');
        end;
    end)

This code works great under delphi 10.4 update 2 but not under delphi 11.0

Why ??

I'm not able to run it on 11.0 (with new procedure's syntax, i.e APermissions: TArray<string> in delphi 10.4 now is APermissions: TClassicStringDynArray in delphi 11)

Thank's for any help

Daniele

Daniele
  • 51
  • 3
  • "*I'm not able to run it on 11.0*" - why not? What is the actual problem? Do you get a compiler error? A runtime error? Bad results? You need to be more specific. – Remy Lebeau May 11 '22 at 14:44
  • On a side note, you should call `PermissionsService.IsPermissionGranted()` before you call `PermissionsService.RequestPermissions()`. No need to request permissions if you already have them. – Remy Lebeau May 11 '22 at 14:48
  • @Remy:The main problem is that all seems to go in a rigth way... The code (Dalija code) is compiled without any error but when it run on a device (in my case Samsung S10+ with last android 12) LFile is empty no result. Back to delphi 10.4 the LFile contains the file list. More, i can't debug because, without any reasons, F9 stop to work! The brackpoint does not work anymore.... but this is another problem – Daniele May 12 '22 at 08:52
  • `LFile` (singular) is an element of `LFiles` (plural). I can't imagine `GetFiles()` returning an array of blank strings. So, did you mean that `LFiles` is empty instead? Did you check that `GetSharedDownloadsPath` is returning a correct path? – Remy Lebeau May 12 '22 at 14:52
  • Remy thank's for your reply. This is the matter ..... same code different enviroments (delphi 10.4 and 11) different result. Under 10.4 i got the file list, under 11 (chanching TArray with TClassicStringDynArray and others changes) in same directory (TPath.GetSharedDownloadsPath and return same path as delphi10.4) the list is empty. I'm going crazy but .... i continue to search a way out. !!! – Daniele May 13 '22 at 09:08
  • You did not answer my question - is it really `LFile` *inside the loop* that is empty, or is it really `LFiles` *outside the loop* that is empty? Is `GetFiles()` returning an array whose length is 0, or is it returning an array whose length is > 0 but the strings are empty? There is a big difference. – Remy Lebeau May 13 '22 at 16:34
  • 1
    Hi Remy, excuse me .... i thougth to answer you (The list is empty). The array (LFiles) is empty (IntToStr(High(LFiles) return always, for any directory, -1) using delphi 11; Same code with Delphi 10.4.2 return a number (files present into a directory (any) ). I'll try on a pc where is installed delphi 11.1 To be Honest i do not understand what happend in delphi 11. Thank you for your help .... – Daniele May 16 '22 at 14:58
  • In that case, assuming you have adequate permissions to access the folder, then this sounds like a regression that should be [reported to Embarcadero](https://quality.embarcadero.com). – Remy Lebeau May 16 '22 at 15:35

1 Answers1

2

Embarcadero changed the function signatures with the release of Delphi 11. This will get you started with sorting that out.

{$if COMPILERVERSION > 34}
  {$define NEW_ALEX}
{$else}
  {$if COMPILERVERSION > 32}
    {$define NEW_RIO}
  {$endif}
{$endif}

  private
    FPermittoVibrate: Boolean;
    FVibratePermission: String;
    FPermitAccessFineLocation: Boolean;
    FAccessFineLocation: String;
    FPermitNetworkState: Boolean;
    FNetworkStatePermission: String;
    FPermitWifiState: Boolean;
    FWifiStatePermission: String;
    FPermitPhoneState: Boolean;
    FPhoneStatePermission: String;

  {$ifdef NEW_RIO}
  procedure PermissionResult(Sender: TObject;
    const APermissions: TArray<string>;
    const AGrantResults: TArray<TPermissionStatus>);
  procedure PermissionRequest(Sender: TObject;
    const APermissions: TArray<string>;
    const APostRationaleProc: TProc);
  {$endif}
  {$ifdef NEW_ALEX}
  procedure PermissionsResult(Sender: TObject;
    const APermissions: TClassicStringDynArray;
    const AGrantResults: TClassicPermissionStatusDynArray
  );
  procedure DisplayRationale(Sender: TObject;
    const APermissions: TClassicStringDynArray;
    const APostRationaleProc: TProc);
  {$endif}

{$ifdef NEW_RIO}
if TJBuild_VERSION.JavaClass.SDK_INT >= 23 then // 6 or higher
begin
  PermissionsService.RequestPermissions([
      FVibratePermission
    , FAccessFineLocation
    , FNetworkStatePermission
    , FWifiStatePermission
    , FPhoneStatePermission
    ],
    PermissionResult, PermissionRequest
    );
end;
{$endif}
{$ifdef NEW_ALEX}
if TJBuild_VERSION.JavaClass.SDK_INT >= 23 then // 6 or higher
begin
  PermissionsService.RequestPermissions([
      FVibratePermission
    , FAccessFineLocation
    , FNetworkStatePermission
    , FWifiStatePermission
    , FPhoneStatePermission
    ],
    PermissionsResult, DisplayRationale
    );
end;
{$endif}

{$ifdef NEW_RIO}

procedure TfrmMain.PermissionRequest(Sender: TObject;
  const APermissions: TArray<string>; const APostRationaleProc: TProc);
var
  I: Integer;
  RationaleMsg: string;
begin
  RationaleMsg := 'The app needs ';
  for I := 0 to High(APermissions) do
  begin
    if APermissions[I] = FVibratePermission then
      RationaleMsg := RationaleMsg + 'to vibrate the phone' + linefeed;
    if APermissions[I] = FAccessFineLocation then
      RationaleMsg := RationaleMsg + linefeed + 'to access fine location';
    if APermissions[I] = FNetworkStatePermission then
      RationaleMsg := RationaleMsg + linefeed + 'to access network state';
    if APermissions[I] = FWifiStatePermission then
      RationaleMsg := RationaleMsg + linefeed + 'to access wifi state';
    if APermissions[I] = FPhoneStatePermission then
      RationaleMsg := RationaleMsg + linefeed + 'to read phone state';
  end;
  TDialogService.ShowMessage(RationaleMsg,
    procedure(const AResult: TModalResult)
    begin
      APostRationaleProc;
    end)
end;

procedure TfrmMain.PermissionResult(Sender: TObject;
  const APermissions: TArray<string>;
  const AGrantResults: TArray<TPermissionStatus>);
var
  Permission: String;
  i: Integer;
begin
  for i := 0 to High(APermissions) do
  begin
    Permission := APermissions[i];
    if Permission = FVibratePermission then
      FPermitToVibrate := AGrantResults[i] = TPermissionStatus.Granted
    else if Permission = FAccessFineLocation then
      FPermitAccessFineLocation := AGrantResults[i] = TPermissionStatus.Granted
    else if Permission = FNetworkStatePermission then
      FPermitNetworkState := AGrantResults[i] = TPermissionStatus.Granted
    else if Permission = FWifiStatepermission then
      FPermitWifiState := AGrantResults[i] = TPermissionStatus.Granted
    else if Permission = FPhoneStatePermission then
      FPermitPhoneState := AGrantResults[i] = TPermissionStatus.Granted
      ;
  end;

end;

{$endif NEW_RIO}

{$ifdef NEW_ALEX}

procedure TfrmMain.PermissionsResult(Sender: TObject;
  const APermissions: TClassicStringDynArray;
  const AGrantResults: TClassicPermissionStatusDynArray
);
var
  Permission: String;
  i: Integer;
begin

  for i := 0 to High(APermissions) do
  begin
    Permission := APermissions[i];
    if Permission = FVibratePermission then
      FPermitToVibrate := AGrantResults[i] = TPermissionStatus.Granted
    else if Permission = FAccessFineLocation then
      FPermitAccessFineLocation := AGrantResults[i] = TPermissionStatus.Granted
    else if Permission = FNetworkStatePermission then
      FPermitNetworkState := AGrantResults[i] = TPermissionStatus.Granted
    else if Permission = FWifiStatepermission then
      FPermitWifiState := AGrantResults[i] = TPermissionStatus.Granted
    else if Permission = FPhoneStatePermission then
      FPermitPhoneState := AGrantResults[i] = TPermissionStatus.Granted
  end;

end;

procedure TfrmMain.DisplayRationale(Sender: TObject;
  const APermissions: TClassicStringDynArray;
  const APostRationaleProc: TProc);
var
  I: Integer;
  RationaleMsg: string;
begin
  RationaleMsg := 'The app needs ';
  for I := 0 to High(APermissions) do
  begin
    if APermissions[I] = FVibratePermission then
      RationaleMsg := RationaleMsg + 'to vibrate the phone' + SLineBreak;
    if APermissions[I] = FAccessFineLocation then
      RationaleMsg := RationaleMsg + SLineBreak + 'to access fine location';
    if APermissions[I] = FNetworkStatePermission then
      RationaleMsg := RationaleMsg + SLineBreak + 'to access network state';
    if APermissions[I] = FWifiStatePermission then
      RationaleMsg := RationaleMsg + SLineBreak + 'to access wifi state';
    if APermissions[I] = FPhoneStatePermission then
      RationaleMsg := RationaleMsg + SLineBreak + 'to read phone state';
  end;
  TDialogService.ShowMessage(RationaleMsg,
    procedure(const AResult: TModalResult)
    begin
      APostRationaleProc;
    end)
end;

{$endif NEW_ALEX}
Freddie Bell
  • 2,186
  • 24
  • 43
  • 1
    Thank's Freddie, i'll try your suggestion. As i wrote to Remy (under delphii 11) the problem i have is how i have not back the file list (in LFile) while i have under 10.4. Due the fact the code (in both ides with rigth code) is compiled without errors, it is very problably (under 11) i have some permission problem or manifest token missing .... I have to do some more work to understand.... – Daniele May 12 '22 at 08:58
  • @FreddieBell The code you have shown is needlessly duplicating a LOT of code just to account for a couple of changed types. You should be able to avoid all of that duplication by simply declaring a couple of type aliases for pre-11 versions, eg: `{$ifndef NEW_ALEX} type TClassicStringDynArray = TArray; TClassicPermissionStatusDynArray = TArray; {$endif}` And then get rid of the remaining `{$ifdef}`'s and use just the `NEW_ALEX`-based code by itself in all versions. – Remy Lebeau May 16 '22 at 15:55
  • @RemyLebeau There's also the changes to the `PermissionService.RequestPermissions` that must be accomodated! – Freddie Bell May 16 '22 at 17:08
  • 1
    @FreddieBell the only thing I see different between them is naming. In your code, you use `PermissionRequest()` and `PermissionResult()` for Rio but use `DisplayRationale()` and `PermissionsResult()` for Alexandria. But the two versions have *identical* body codes, and their signatures are *identical* if you abstract the different parameter types with aliases. So, I see no reason to `IFDEF` the entire code they way you have, it should be greatly simplified. – Remy Lebeau May 16 '22 at 17:19