Jump to content
Jose Morango

Android 8.0 Permissions errors using Delphi Rio 10.3

Recommended Posts

Hi Guys,
I'm using  Delphi rio 10.3 Comunity edition to test some small android apps.
I'm trying to access the camera but I got error  CAMERA_EXTERNAL_STORAGE. WRITE_EXTERNAL_STORAGE have not been granted.

In my project options I Have chose those options selected in the USES PERMISSIONS,

My device have Android 8.0  ,

So it seems that rio 10.3 do not resolve android problems of the Tokyo version.

 

Any ideas?

Share this post


Link to post

You need to explicitly request those permissions at runtime. Check out the CameraComponent demo in:

 

\Users\Public\Documents\Embarcadero\Studio\20.0\Samples\Object Pascal\Mobile Snippets\CameraComponent

 

Also, there's no such permission as CAMERA_EXTERNAL_STORAGE. I expect you mean READ_EXTERNAL_STORAGE

Edited by Dave Nottage
  • Like 2

Share this post


Link to post

Quick Example :

 

private 

{$IFDEF ANDROID}
  fReadStorage:=JStringToString(TJManifest_permission.JavaClass.READ_EXTERNAL_STORAGE);
  fWriteStorage:=JStringToString(TJManifest_permission.JavaClass.WRITE_EXTERNAL_STORAGE);
  fBlueTooth:=JStringToString(TJManifest_permission.JavaClass.BLUETOOTH);
{$ENDIF}


procedure Tfrm????.FormShow(Sender: TObject);
begin
{$IF DEFINED(IOS) or DEFINED(ANDROID)}
PermissionsService.RequestPermissions([fReadStorage,fWriteStorage,fBlueTooth], RequestPermissionsResult, DisplayRationale);
{$ENDIF}
end;

 

procedure Tfrm????.RequestPermissionsResult(Sender: TObject; const APermissions: TArray<string>; const AGrantResults: TArray<TPermissionStatus>);
begin
  if (Length(AGrantResults) = 3) and (AGrantResults[0] = TPermissionStatus.Granted) and (AGrantResults[1] = TPermissionStatus.Granted) and (AGrantResults[2] = TPermissionStatus.Granted) then
     // execute procedure ...  as before on form show ...
  else Begin
    TDialogService.ShowMessage('No ....');
    Application.Terminate;
  End;
  // če ne bi dovolili permission bi moral showerror
end;
{$ENDIF}

 

{$IF DEFINED(IOS) or DEFINED(ANDROID)}
procedure Tfrm????..DisplayRationale(Sender: TObject; const APermissions: TArray<string>; const APostRationaleProc: TProc);
var
  I: Integer;
  RationaleMsg: string;
begin
  for I := 0 to High(APermissions) do
  begin
    if APermissions = fReadStorage then
      RationaleMsg := RationaleMsg + 'Aplikacija zahteva branje pomnilnika' + SLineBreak + SLineBreak
    else if APermissions = fWriteStorage then
      RationaleMsg := RationaleMsg + 'Aplikacija zahteva pisanje pomnilnika'+ SLineBreak + SLineBreak
   else if APermissions = fBlueTooth then
      RationaleMsg := RationaleMsg + 'Aplikacija zahteva BlueTooth';
  end;
  TDialogService.ShowMessage(RationaleMsg,
    procedure(const AResult: TModalResult)
    begin
      APostRationaleProc;
    end)
end;
{$ENDIF}

Edited by mausmb
  • Like 1

Share this post


Link to post
17 hours ago, Dave Nottage said:

You need to explicitly request those permissions at runtime. Check out the CameraComponent demo in:

 

\Users\Public\Documents\Embarcadero\Studio\20.0\Samples\Object Pascal\Mobile Snippets\CameraComponent

 

Also, there's no such permission as CAMERA_EXTERNAL_STORAGE. I expect you mean READ_EXTERNAL_STORAGE

Hi Dave, thanks for the info, It's all working fine now.

Share this post


Link to post
16 hours ago, mausmb said:

Quick Example :

 

private 

{$IFDEF ANDROID}
  fReadStorage:=JStringToString(TJManifest_permission.JavaClass.READ_EXTERNAL_STORAGE);
  fWriteStorage:=JStringToString(TJManifest_permission.JavaClass.WRITE_EXTERNAL_STORAGE);
  fBlueTooth:=JStringToString(TJManifest_permission.JavaClass.BLUETOOTH);
{$ENDIF}


procedure Tfrm????.FormShow(Sender: TObject);
begin
{$IF DEFINED(IOS) or DEFINED(ANDROID)}
PermissionsService.RequestPermissions([fReadStorage,fWriteStorage,fBlueTooth], RequestPermissionsResult, DisplayRationale);
{$ENDIF}
end;

 

procedure Tfrm????.RequestPermissionsResult(Sender: TObject; const APermissions: TArray<string>; const AGrantResults: TArray<TPermissionStatus>);
begin
  if (Length(AGrantResults) = 3) and (AGrantResults[0] = TPermissionStatus.Granted) and (AGrantResults[1] = TPermissionStatus.Granted) and (AGrantResults[2] = TPermissionStatus.Granted) then
     // execute procedure ...  as before on form show ...
  else Begin
    TDialogService.ShowMessage('No ....');
    Application.Terminate;
  End;
  // če ne bi dovolili permission bi moral showerror
end;
{$ENDIF}

 

{$IF DEFINED(IOS) or DEFINED(ANDROID)}
procedure Tfrm????..DisplayRationale(Sender: TObject; const APermissions: TArray<string>; const APostRationaleProc: TProc);
var
  I: Integer;
  RationaleMsg: string;
begin
  for I := 0 to High(APermissions) do
  begin
    if APermissions = fReadStorage then
      RationaleMsg := RationaleMsg + 'Aplikacija zahteva branje pomnilnika' + SLineBreak + SLineBreak
    else if APermissions = fWriteStorage then
      RationaleMsg := RationaleMsg + 'Aplikacija zahteva pisanje pomnilnika'+ SLineBreak + SLineBreak
   else if APermissions = fBlueTooth then
      RationaleMsg := RationaleMsg + 'Aplikacija zahteva BlueTooth';
  end;
  TDialogService.ShowMessage(RationaleMsg,
    procedure(const AResult: TModalResult)
    begin
      APostRationaleProc;
    end)
end;
{$ENDIF}

Thank you very mush  🙂

Share this post


Link to post

I think it should be good practice to request permission short before desired access,

Not to request this generally in FormCreate or FormShow.

If you do so rhe request will popup directly after app launched, maybe not even fully prepared, and users (like me) will see this quite suspicious.

Edited by Rollo62

Share this post


Link to post
43 minutes ago, Rollo62 said:

I think it should be good practice to request permission short before desired access,

Not to request this generally in FormCreate or FormShow.

If you do so rhe request will popup directly after app launched, maybe not even fully prepared, and users (like me) will see this quite suspicious.

Yes and No - depending on functionality.   Almost all APP request permission when installed/started. It's tricky in Delphi to do that with new approach.  

If you're for example reading i.e. setup. login, token,... from storage ... you don't have other option. if you just want access when app is up&running then is better to request permission when needed.

 

br,

Marjan 

Share this post


Link to post

Permissions needed for sure can be asked at program start. A permission only needed for special tasks should be asked at first use.

Share this post


Link to post

In Delphi rio 10.3 Comunity edition, permission framework is in System.Permissions.pas.

 

But, there are some bugs ? There are two method that we can request permission to call it. But the parameter "AOnDisplayRationale" has never be used by these two methods.

 

procedure TPermissionsService.RequestPermissions(const APermissions: TArray<string>;
  const AOnRequestPermissionsResult: TRequestPermissionsResultEvent; AOnDisplayRationale: TDisplayRationaleEvent);
var
  GrantResults: TArray<TPermissionStatus>;
  I: Integer;
begin
  SetLength(GrantResults, Length(APermissions));
  for I := Low(GrantResults) to High(GrantResults) do
    GrantResults[I] := TPermissionStatus.Granted;
  AOnRequestPermissionsResult(Self, APermissions, GrantResults)
end;

procedure TPermissionsService.RequestPermissions(const APermissions: TArray<string>;
  const AOnRequestPermissionsResult: TRequestPermissionsResultProc; AOnDisplayRationale: TDisplayRationaleProc);
var
  GrantResults: TArray<TPermissionStatus>;
  I: Integer;
begin
  SetLength(GrantResults, Length(APermissions));
  for I := Low(GrantResults) to High(GrantResults) do
    GrantResults[I] := TPermissionStatus.Granted;
  AOnRequestPermissionsResult(APermissions, GrantResults)
end;

 

Share this post


Link to post

There is a demo named BLEScanner with Delphi.

In this demo, you can find some code about permission:

procedure TForm6.btnStartScanClick(Sender: TObject);
begin
  PermissionsService.RequestPermissions([FLocationPermission], RequestPermissionsResult, DisplayRationale);
end;

procedure TForm6.DisplayRationale(Sender: TObject; const APermissions: TArray<string>; const APostRationaleProc: TProc);
begin
  TDialogService.ShowMessage('We need to be given permission to discover BLE devices',
    procedure(const AResult: TModalResult)
    begin
      APostRationaleProc;
    end)
end;
  
 procedure TForm6.RequestPermissionsResult(Sender: TObject; const APermissions: TArray<string>; const AGrantResults: TArray<TPermissionStatus>);
begin
  // 1 permissions involved: ACCESS_COARSE_LOCATION
  if (Length(AGrantResults) = 1) and (AGrantResults[0] = TPermissionStatus.Granted) then
    StartBLEDiscovery
  else
    TDialogService.ShowMessage('Cannot start BLE scan as the permission has not been granted');
end;

 

But because the bug, DisplayRationale never be called.

Share this post


Link to post
On 7/15/2019 at 3:42 AM, pcplayer99 said:

There are two method that we can request permission to call it. But the parameter "AOnDisplayRationale" has never be used by these two methods.

I don't have Rio installed to look at, but the implementations you have shown are not invoking any Android APIs to request permissions from the user.  That makes me think you are looking at implementation code which is meant to run on Android versions prior to v6.0, in which case it makes sense that AOnDisplayRationale would not be called since permissions would be implicitly granted by app manifest only, which would explain why the implementation code you show is populating the GrantResults array with TPermissionStatus.Granted regardless of what is being requested, and ignoring AOnDisplayRationale.  There is no simply reason to display a rationale prompt to the user in that situation.

 

So, have you looked at the implementation code that is meant to run on Androidd 6.0+?  Does THAT code call AOnDisplayRationale as expected?

 

On a side note, application code should be calling PermissionsService.IsPermissionGranted() or PermissionsService.IsEveryPermissionGranted() before calling PermissionsService.RequestPermissions(), eg:

     
procedure TForm6.btnStartScanClick(Sender: TObject);
begin
  if PermissionsService.IsPermissionGranted(FLocationPermission) then
    StartBLEDiscovery
  else
    PermissionsService.RequestPermissions([FLocationPermission], RequestPermissionsResult, DisplayRationale);
end;

 

Edited by Remy Lebeau

Share this post


Link to post

On my Android Phone, Android version is 9.

I tested BLEScanner  demo, the method "TForm6.DisplayRationale" never be called when I call "PermissionsService.RequestPermissions([FLocationPermission], RequestPermissionsResult, DisplayRationale);"

Share this post


Link to post

You didn't answer my question:

 

"So, have you looked at the implementation code that is meant to run on Androidd 6.0+?  Does THAT code call AOnDisplayRationale as expected?"

Edited by Remy Lebeau

Share this post


Link to post

I run test app on Android 9, under debug mode, step by step, press F7 when it stoped on this: PermissionsService.RequestPermissions([FLocationPermission], RequestPermissionsResult, DisplayRationale);

But it can not go into the implementation code, it go into some ASM code in function _InstClear(var Dest: TObject): Pointer; in  system unit.

 

In the same condition, if I press Ctr + Left click RequestPermissions, it go into:

 

procedure TPermissionsService.RequestPermissions(const APermissions: TArray<string>;
  const AOnRequestPermissionsResult: TRequestPermissionsResultEvent; AOnDisplayRationale: TDisplayRationaleEvent);

 

So, check this procedure in System.Permissions, there is no code to call AOnDisplayRationale.

Edited by pcplayer99

Share this post


Link to post
13 hours ago, pcplayer99 said:

So, check this procedure in System.Permissions, there is no code to call AOnDisplayRationale.

You need to trace through the TAndroidPermissionsService.InternalRequestPermissions method in the System.Android.Permissions unit. Note that the rationale is shown only when permissions are not granted by the user.

Share this post


Link to post

Maybe this issue in my Android phone, is that the phone system show a policy dialogbox and I have selected OK, so it is the reason the AOnDisplayRationale can not call.

Share this post


Link to post

I did a test, when the system dialog box show, I select "No", then AOnDisplayRationale show my dialog box.

Share this post


Link to post
4 hours ago, pcplayer99 said:

I select "No", then AOnDisplayRationale show my dialog box.

Right; that would be because, as I said earlier, it is "shown only when permissions are not granted by the user"

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×