delphi安卓動態許可權申請
安卓8及以上版本,除了原來的靜態許可權申請以外,還需要動態許可權申請。
delphi10.3開始支援安卓動態許可權申請。
delphi11開始官方改變了安卓動態許可權申請的引數型別,導致原來編寫的程式碼,編碼報錯。
下面的程式碼,可以很好地解決許可權問題。兼顧了delphi10.3和delphi11以後版本。
{sensor 2022-07-12 週二 老吳QQ:910731685 Android 動態申請許可權控制元件 參考:https://developer.android.google.cn/reference/android/Manifest.permission } unit uAndroid_Permissions_Component; interface uses System.Types, //TClassicStringDynArray System.Permissions, //TClassicPermissionStatusDynArray FMX.DialogService, System.UITypes, //TModalResult System.TypInfo, {$IFDEF DEBUG} FMX.Dialogs, {$ENDIF} System.SysUtils, System.Classes; const Permission_Prefix = 'android.permission.'; //Android 許可權字串的字首部分 type //如果授權申請成功, GrantedResult=true,NoGranteds無效,否則 GrantedResult=false,NoGranteds表示沒有授權的專案列表 TApplyResult = procedure(const Sender : TObject; GrantedResult : Boolean; NoGranteds : TArray<string>) of object; TPermission = ( ACCESS_COARSE_LOCATION, //可單獨申請 獲取大致位置資訊Allows an app to access approximate location. ACCESS_FINE_LOCATION, //可單獨申請 獲取精確位置資訊Allows an app to access precise location. ACCESS_BACKGROUND_LOCATION, //Androidapi.JNI.Os 沒有包含這個許可權,實際是支援的。這個許可權必須要求ACCESS_COARSE_LOCATION 和 ACCESS_FINE_LOCATION 許可權 ACCESS_MEDIA_LOCATION, //可單獨申請 Allows an application to access any geographic locations persisted in the user's shared collection //ACCESS_MOCK_LOCATION, //(obsolete) Android目前已經不支援 ACTIVITY_RECOGNITION, // 獲取裝置中的健身運動資訊 Allows an application to recognize physical activity. ADD_VOICEMAIL, // Allows an application to add voicemails into the system. ANSWER_PHONE_CALLS, // 接聽或結束通話電話、監聽通話狀態 //AUTHENTICATE_ACCOUNTS, // (obsolete) Android目前已經不支援 BODY_SENSORS, //獲取您的生命體徵相關資料 BODY_SENSORS_BACKGROUND, // API 33 新增加 後臺獲取您的生命體徵相關資料 CALL_PHONE, CAMERA, //CONYINUE_A_CALL_STARTED_IN_ANOTHER_APP, // Android目前已經不支援 Continue a call started in another app GET_ACCOUNTS, //獲取手機賬戶 MANAGE_ACCOUNTS, // Android目前已經不支援 Manage accounts (obsolete) //PROCESS_OUTGOING_CALLS, // READ_CALENDAR, //讀取日曆中的日程資訊 READ_CALL_LOG, //讀取通話記錄 READ_CONTACTS, //讀取聯絡人資訊 READ_EXTERNAL_STORAGE, //讀取裝置上的照片及檔案 //READ_HISTORY_BOOKMARKS, //Android目前已經不支援 Read history bookmarks (obsolete) READ_PHONE_NUMBERS, READ_PHONE_STATE, READ_SMS, RECEIVE_MMS, RECEIVE_SMS, RECEIVE_WAP_PUSH, RECORD_AUDIO, SEND_SMS, USE_SIP, WRITE_CALENDAR, WRITE_CALL_LOG, WRITE_CONTACTS, WRITE_EXTERNAL_STORAGE ); TPermissions = set of TPermission; TAndroid_Permission = class(TComponent) private FOnApplyResult : TApplyResult; FPermissions : TPermissions; FPermissionsStr: TArray<string>; procedure SetFPermissions(value: TPermissions); // **** 關於 Android 許可權相關 **** procedure DisplayRationale(Sender: TObject; const APermissions: TClassicStringDynArray; const APostRationaleProc: TProc); {$IF CompilerVersion >= 35.0} // after Delphi 11 Alexandria procedure AndroidPermissionRequestResult(Sender: TObject; const APermissions: TClassicStringDynArray; const AGrantResults: TClassicPermissionStatusDynArray); {$ELSE} // before Delphi 11 Alexandria procedure AndroidPermissionRequestResult(const APermissions: TArray<string>; const AGrantResults: TArray<TPermissionStatus>); {$ENDIF} protected { Protected declarations } public constructor Create(AOwner: TComponent); override; destructor Destroy; override; //申請許可權 procedure Apply; //檢測許可權是否已經授予 function IsPermissionGranted(APermission : string): Boolean; published property Permissions: TPermissions read FPermissions write SetFPermissions; property OnApplyResult : TApplyResult read FOnApplyResult write FOnApplyResult; end; //procedure Register; implementation //{$R 'Android_Permission.dcr'} //procedure Register; //begin // RegisterComponents('LW', [TAndroid_Permission]); //end; { TAndroid_Permission } {$IF CompilerVersion >= 35.0} procedure TAndroid_Permission.AndroidPermissionRequestResult(Sender: TObject; const APermissions: TClassicStringDynArray; const AGrantResults: TClassicPermissionStatusDynArray); var i,count : integer; NoGranteds : TArray<string>; begin // 判斷是否申請的授權都已經批准 SetLength(NoGranteds,0); count := Length(FPermissionsStr); for i := 0 to count - 1 do //逐條判斷是否已經成功授權 begin {$IFDEF DEBUG} showmessage(GetEnumName(typeInfo(TPermissionStatus), Ord(AGrantResults[i])) + ' : '#13#10 + FPermissionsStr[i]); {$ENDIF} if AGrantResults[i] <> TPermissionStatus.Granted then begin SetLength(NoGranteds,Length(NoGranteds) + 1); NoGranteds[Length(NoGranteds) - 1] := FPermissionsStr[i]; end; end; if Length(NoGranteds) = 0 then begin if Assigned(FOnApplyResult) then //此時授權已經全部成功,可以觸發授權成功事件 try FOnApplyResult(Self,True,[]); //防止使用者事件出錯,導致控制元件錯誤 except on E: Exception do raise Exception.Create('OnApplyResult: ' + E.message); end; end else if Assigned(FOnApplyResult) then //此時授權已經全部成功,可以觸發授權成功事件 try FOnApplyResult(Self,False,NoGranteds); //防止使用者事件出錯,導致控制元件錯誤 except on E: Exception do raise Exception.Create('OnApplyResult: ' + E.message); end; end; {$ELSE} procedure AndroidPermissionRequestResult(const APermissions: TArray<string>; const AGrantResults: TArray<TPermissionStatus>); var i,count : integer; NoGranteds : TArray<string>; begin // 判斷是否申請的授權都已經批准 SetLength(NoGranteds,0); count := Length(FPermissionsStr); for i := 0 to count - 1 do //逐條判斷是否已經成功授權 begin if AGrantResults[i] <> TPermissionStatus.Granted then begin SetLength(NoGranteds,Length(NoGranteds) + 1); NoGranteds[Length(NoGranteds) - 1] := FPermissionsStr[i]; end; end; if Length(NoGranteds) = 0 then begin if Assigned(FOnApplyResult) then //此時授權已經全部成功,可以觸發授權成功事件 try FOnApplyResult(Self,True,[]); //防止使用者事件出錯,導致控制元件錯誤 except on E: Exception do raise Exception.Create('OnApplyResult: ' + E.message); end; end else if Assigned(FOnApplyResult) then //此時授權已經全部成功,可以觸發授權成功事件 try FOnApplyResult(Self,False,NoGranteds); //防止使用者事件出錯,導致控制元件錯誤 except on E: Exception do raise Exception.Create('OnApplyResult: ' + E.message); end; end; {$ENDIF} procedure TAndroid_Permission.Apply; var Permission : TPermission; i : Integer; {$IFDEF DEBUG} count : integer; S : string; {$ENDIF} PermissionStr : string; begin {$IFNDEF ANDROID} Exit; //只有android 環境下動態申請許可權才有效 {$ENDIF} //確認只有 10.3 及以上的版本編譯才有效 {$IF CompilerVersion < 33.0} //10.3 Rio Exit; //10.3 以下不支援 {$ELSE} if (TOSVersion.Major <= 6) then Exit; //只有Android 7 以上才支援動態許可權,實際上7沒有反應,8才真正支援動態許可權申請 {$ENDIF} SetLength(FPermissionsStr,0); //初始化當前許可權陣列 //構造許可權字串陣列,最大許可權256個,目前是沒有超過的 for i := 0 to 255 do begin Permission := TPermission(i); if not (Permission in FPermissions) then continue; PermissionStr := GetEnumName(typeInfo(TPermission), i); if (PermissionStr = '') or (PermissionStr = 'uAndroid_Permissions_Component') then Break; //增加許可權陣列字串 SetLength(FPermissionsStr,Length(FPermissionsStr) + 1); FPermissionsStr[Length(FPermissionsStr) - 1] := Permission_Prefix + PermissionStr; end; //如果沒有選擇授權,則直接退出 if Length(FPermissionsStr) = 0 then Exit; {$IFDEF DEBUG} count := Length(FPermissionsStr); S := ''; for i := 0 to count - 1 do S := S + FPermissionsStr[i] + #13#10; ShowMessage(S) ; {$ENDIF} //申請許可權 PermissionsService.RequestPermissions (FPermissionsStr, AndroidPermissionRequestResult, DisplayRationale); end; constructor TAndroid_Permission.Create(AOwner: TComponent); begin inherited; end; destructor TAndroid_Permission.Destroy; begin inherited; end; procedure TAndroid_Permission.DisplayRationale(Sender: TObject; const APermissions: TClassicStringDynArray; const APostRationaleProc: TProc); var I: Integer; RationaleMsg: string; begin for I := 0 to High(APermissions) do begin if APermissions[I] = FPermissionsStr[i] then RationaleMsg := RationaleMsg + 'App needs [' + FPermissionsStr[i] + '] Right' + SLineBreak + SLineBreak; end; // Show an explanation to the user *asynchronously* - don't block this thread waiting for the user's response! // After the user sees the explanation, invoke the post-rationale routine to request the permissions TDialogService.ShowMessage(RationaleMsg, procedure(const AResult: TModalResult) begin APostRationaleProc; end) end; function TAndroid_Permission.IsPermissionGranted(APermission: string): Boolean; begin if Trim(APermission) = '' then Exit(False); try Result := PermissionsService.IsPermissionGranted(APermission); except on E: Exception do Result := False; end; end; procedure TAndroid_Permission.SetFPermissions(value: TPermissions); begin FPermissions := value; end; end.