Jose Morango 2 Posted November 30, 2018 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
Dave Nottage 557 Posted November 30, 2018 (edited) 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 November 30, 2018 by Dave Nottage 2 Share this post Link to post
mausmb 13 Posted November 30, 2018 (edited) 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 November 30, 2018 by mausmb 1 Share this post Link to post
Jose Morango 2 Posted December 1, 2018 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
Jose Morango 2 Posted December 1, 2018 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
Rollo62 536 Posted December 2, 2018 (edited) 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 December 2, 2018 by Rollo62 Share this post Link to post
mausmb 13 Posted December 2, 2018 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
Markus Kinzler 174 Posted December 3, 2018 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
pcplayer99 11 Posted July 15, 2019 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
pcplayer99 11 Posted July 15, 2019 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
Remy Lebeau 1396 Posted July 16, 2019 (edited) 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 July 16, 2019 by Remy Lebeau Share this post Link to post
pcplayer99 11 Posted July 26, 2019 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
Remy Lebeau 1396 Posted July 26, 2019 (edited) 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 July 26, 2019 by Remy Lebeau Share this post Link to post
Eli M. 38 Posted July 27, 2019 The PhotoEditorDemo has permissions code that works for the camera. Share this post Link to post
pcplayer99 11 Posted July 27, 2019 (edited) 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 July 27, 2019 by pcplayer99 Share this post Link to post
Dave Nottage 557 Posted July 27, 2019 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
pcplayer99 11 Posted July 29, 2019 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
pcplayer99 11 Posted July 29, 2019 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
Dave Nottage 557 Posted July 29, 2019 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