delphi安卓動態許可權申請

delphi中间件發表於2024-05-18

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.

  

相關文章