如何在 Delphi 10 中获取设备电子邮件地址

How to get device email address in Delphi 10

我正在尝试获取我的设备电子邮件地址,我使用 Java2OP 将 AccountManager class 转换为 object pascal。但是,我尝试使用以下代码获取电子邮件地址:

 jAm: JAccountManager;
 accounts: TJavaObjectArray<JAccountClass>;
 jAcc: JAccountClass;
 begin

  jAM := TJAccountManager.JavaClass.get(SharedActivityContext);
  accounts := TJavaObjectArray<JAccountClass>.Wrap(jAM.getAccountsByType(StringToJString('com.google')));

  mmLog.Lines.Add('Length Accounts: ' + Inttostr(accounts.Length));

  if accounts.Length > 0 then begin
     jAcc := accounts.Items[0];
     mmLog.Lines.Add(jstringtostring( jAcc.name));
  end else begin
     mmLog.Lines.Add('no accounts available');
  end;

我在地址 415E5254 访问冲突,访问地址 0000002C! 伙计们有什么想法吗?

 JAccountClass = interface(JObjectClass)
['{94EE6861-F326-489F-8919-E20B39E3D9C1}']
{class} function _GetCREATOR: JParcelable_Creator; cdecl;
{class} function _Getname: JString; cdecl;
{class} function _Gettype: JString; cdecl;
{class} function init(name: JString; &type: JString): JAccount; cdecl; overload;//Deprecated
{class} function init(init: JParcel): JAccount; cdecl; overload;//Deprecated
{class} function describeContents: Integer; cdecl;
{class} function equals(o: JObject): Boolean; cdecl;
{class} property CREATOR: JParcelable_Creator read _GetCREATOR;
{class} property name: JString read _Getname;
{class} property &type: JString read _Gettype;
end;

  [JavaSignature('android/accounts/Account')]
 JAccount = interface(JObject)
['{71476381-8B6E-471F-9189-9857ECD7508C}']
function hashCode: Integer; cdecl;
function toString: JString; cdecl;
procedure writeToParcel(dest: JParcel; flags: Integer); cdecl;
end;
TJAccount = class(TJavaGenericImport<JAccountClass, JAccount>) end;

JAccountManagerClass = interface(JObjectClass)
['{96273844-2D84-47F0-BFD5-14B73402F843}']
{class} function _GetACTION_AUTHENTICATOR_INTENT: JString; cdecl;
{class} function _GetAUTHENTICATOR_ATTRIBUTES_NAME: JString; cdecl;
{class} function _GetAUTHENTICATOR_META_DATA_NAME: JString; cdecl;
{class} function _GetERROR_CODE_BAD_ARGUMENTS: Integer; cdecl;
{class} function _GetERROR_CODE_BAD_AUTHENTICATION: Integer; cdecl;
{class} function _GetERROR_CODE_BAD_REQUEST: Integer; cdecl;
{class} function _GetERROR_CODE_CANCELED: Integer; cdecl;
{class} function _GetERROR_CODE_INVALID_RESPONSE: Integer; cdecl;
{class} function _GetERROR_CODE_NETWORK_ERROR: Integer; cdecl;
{class} function _GetERROR_CODE_REMOTE_EXCEPTION: Integer; cdecl;
{class} function _GetERROR_CODE_UNSUPPORTED_OPERATION: Integer; cdecl;
{class} function _GetKEY_ACCOUNTS: JString; cdecl;
{class} function _GetKEY_ACCOUNT_AUTHENTICATOR_RESPONSE: JString; cdecl;
{class} function _GetKEY_ACCOUNT_MANAGER_RESPONSE: JString; cdecl;
{class} function _GetKEY_ACCOUNT_NAME: JString; cdecl;
{class} function _GetKEY_ACCOUNT_TYPE: JString; cdecl;
{class} function _GetKEY_ANDROID_PACKAGE_NAME: JString; cdecl;
{class} function _GetKEY_AUTHENTICATOR_TYPES: JString; cdecl;
{class} function _GetKEY_AUTHTOKEN: JString; cdecl;
{class} function _GetKEY_AUTH_FAILED_MESSAGE: JString; cdecl;
{class} function _GetKEY_AUTH_TOKEN_LABEL: JString; cdecl;
{class} function _GetKEY_BOOLEAN_RESULT: JString; cdecl;
{class} function _GetKEY_CALLER_PID: JString; cdecl;
{class} function _GetKEY_CALLER_UID: JString; cdecl;
{class} function _GetKEY_ERROR_CODE: JString; cdecl;
{class} function _GetKEY_ERROR_MESSAGE: JString; cdecl;
{class} function _GetKEY_INTENT: JString; cdecl;
{class} function _GetKEY_PASSWORD: JString; cdecl;
{class} function _GetKEY_USERDATA: JString; cdecl;
{class} function _GetLOGIN_ACCOUNTS_CHANGED_ACTION: JString; cdecl;
{class} function addAccount(accountType: JString; authTokenType: JString; requiredFeatures: TJavaObjectArray<JString>; addAccountOptions: JBundle; activity: JActivity; callback: JAccountManagerCallback; handler: JHandler): JAccountManagerFuture; cdecl;//Deprecated
{class} procedure clearPassword(account: JAccount); cdecl;//Deprecated
{class} function confirmCredentials(account: JAccount; options: JBundle; activity: JActivity; callback: JAccountManagerCallback; handler: JHandler): JAccountManagerFuture; cdecl;//Deprecated
{class} function editProperties(accountType: JString; activity: JActivity; callback: JAccountManagerCallback; handler: JHandler): JAccountManagerFuture; cdecl;//Deprecated
{class} function get(context: JContext): JAccountManager; cdecl;
{class} function getAccountsByTypeAndFeatures(&type: JString; features: TJavaObjectArray<JString>; callback: TJavaObjectArray<JAccountManagerCallback>; handler: JHandler): TJavaObjectArray<JAccountManagerFuture>; cdecl;
{class} function getAccountsByTypeForPackage(&type: JString; packageName: JString): TJavaObjectArray<JAccount>; cdecl;
{class} function getAuthToken(account: JAccount; authTokenType: JString; options: JBundle; activity: JActivity; callback: JAccountManagerCallback; handler: JHandler): JAccountManagerFuture; cdecl; overload;
{class} function getAuthenticatorTypes: TJavaObjectArray<JAuthenticatorDescription>; cdecl;
{class} function getPassword(account: JAccount): JString; cdecl;
{class} function getUserData(account: JAccount; key: JString): JString; cdecl;
{class} function newChooseAccountIntent(selectedAccount: JAccount; allowableAccounts: JArrayList; allowableAccountTypes: TJavaObjectArray<JString>; alwaysPromptForAccount: Boolean; descriptionOverrideText: JString; addAccountAuthTokenType: JString; addAccountRequiredFeatures: TJavaObjectArray<JString>; addAccountOptions: JBundle): JIntent; cdecl;
{class} function peekAuthToken(account: JAccount; authTokenType: JString): JString; cdecl;
{class} function removeAccount(account: JAccount; callback: JAccountManagerCallback; handler: JHandler): JAccountManagerFuture; cdecl;
{class} procedure removeOnAccountsUpdatedListener(listener: JOnAccountsUpdateListener); cdecl;
{class} function updateCredentials(account: JAccount; authTokenType: JString; options: JBundle; activity: JActivity; callback: JAccountManagerCallback; handler: JHandler): JAccountManagerFuture; cdecl;
{class} property ACTION_AUTHENTICATOR_INTENT: JString read _GetACTION_AUTHENTICATOR_INTENT;
{class} property AUTHENTICATOR_ATTRIBUTES_NAME: JString read _GetAUTHENTICATOR_ATTRIBUTES_NAME;
{class} property AUTHENTICATOR_META_DATA_NAME: JString read _GetAUTHENTICATOR_META_DATA_NAME;
{class} property ERROR_CODE_BAD_ARGUMENTS: Integer read _GetERROR_CODE_BAD_ARGUMENTS;
{class} property ERROR_CODE_BAD_AUTHENTICATION: Integer read _GetERROR_CODE_BAD_AUTHENTICATION;
{class} property ERROR_CODE_BAD_REQUEST: Integer read _GetERROR_CODE_BAD_REQUEST;
{class} property ERROR_CODE_CANCELED: Integer read _GetERROR_CODE_CANCELED;
{class} property ERROR_CODE_INVALID_RESPONSE: Integer read _GetERROR_CODE_INVALID_RESPONSE;
{class} property ERROR_CODE_NETWORK_ERROR: Integer read _GetERROR_CODE_NETWORK_ERROR;
{class} property ERROR_CODE_REMOTE_EXCEPTION: Integer read _GetERROR_CODE_REMOTE_EXCEPTION;
{class} property ERROR_CODE_UNSUPPORTED_OPERATION: Integer read _GetERROR_CODE_UNSUPPORTED_OPERATION;
{class} property KEY_ACCOUNTS: JString read _GetKEY_ACCOUNTS;
{class} property KEY_ACCOUNT_AUTHENTICATOR_RESPONSE: JString read _GetKEY_ACCOUNT_AUTHENTICATOR_RESPONSE;
{class} property KEY_ACCOUNT_MANAGER_RESPONSE: JString read _GetKEY_ACCOUNT_MANAGER_RESPONSE;
{class} property KEY_ACCOUNT_NAME: JString read _GetKEY_ACCOUNT_NAME;
{class} property KEY_ACCOUNT_TYPE: JString read _GetKEY_ACCOUNT_TYPE;
{class} property KEY_ANDROID_PACKAGE_NAME: JString read _GetKEY_ANDROID_PACKAGE_NAME;
{class} property KEY_AUTHENTICATOR_TYPES: JString read _GetKEY_AUTHENTICATOR_TYPES;
{class} property KEY_AUTHTOKEN: JString read _GetKEY_AUTHTOKEN;
{class} property KEY_AUTH_FAILED_MESSAGE: JString read _GetKEY_AUTH_FAILED_MESSAGE;
{class} property KEY_AUTH_TOKEN_LABEL: JString read _GetKEY_AUTH_TOKEN_LABEL;
{class} property KEY_BOOLEAN_RESULT: JString read _GetKEY_BOOLEAN_RESULT;
{class} property KEY_CALLER_PID: JString read _GetKEY_CALLER_PID;
{class} property KEY_CALLER_UID: JString read _GetKEY_CALLER_UID;
{class} property KEY_ERROR_CODE: JString read _GetKEY_ERROR_CODE;
{class} property KEY_ERROR_MESSAGE: JString read _GetKEY_ERROR_MESSAGE;
{class} property KEY_INTENT: JString read _GetKEY_INTENT;
{class} property KEY_PASSWORD: JString read _GetKEY_PASSWORD;
{class} property KEY_USERDATA: JString read _GetKEY_USERDATA;
{class} property LOGIN_ACCOUNTS_CHANGED_ACTION: JString read _GetLOGIN_ACCOUNTS_CHANGED_ACTION;
end;

[JavaSignature('android/accounts/AccountManager')]
JAccountManager = interface(JObject)
['{9FA4077B-4628-433C-BAFC-9EB299DA9C98}']
function addAccountExplicitly(account: JAccount; password: JString; userdata: JBundle): Boolean; cdecl;//Deprecated
procedure addOnAccountsUpdatedListener(listener: JOnAccountsUpdateListener; handler: JHandler; updateImmediately: Boolean); cdecl;//Deprecated
function blockingGetAuthToken(account: JAccount; authTokenType: JString; notifyAuthFailure: Boolean): JString; cdecl;//Deprecated
function getAccounts: TJavaObjectArray<JAccount>; cdecl;
function getAccountsByType(&type: JString): TJavaObjectArray<JAccount>; cdecl;
function getAuthToken(account: JAccount; authTokenType: JString; notifyAuthFailure: Boolean; callback: JAccountManagerCallback; handler: JHandler): JAccountManagerFuture; cdecl; overload;//Deprecated
function getAuthToken(account: JAccount; authTokenType: JString; options: JBundle; notifyAuthFailure: Boolean; callback: JAccountManagerCallback; handler: JHandler): JAccountManagerFuture; cdecl; overload;
function getAuthTokenByFeatures(accountType: JString; authTokenType: JString; features: TJavaObjectArray<JString>; activity: JActivity; addAccountOptions: JBundle; getAuthTokenOptions: JBundle; callback: JAccountManagerCallback; handler: JHandler): JAccountManagerFuture; cdecl;
function hasFeatures(account: JAccount; features: TJavaObjectArray<JString>; callback: JAccountManagerCallback; handler: JHandler): JAccountManagerFuture; cdecl;
procedure invalidateAuthToken(accountType: JString; authToken: JString); cdecl;
procedure setAuthToken(account: JAccount; authTokenType: JString; authToken: JString); cdecl;
procedure setPassword(account: JAccount; password: JString); cdecl;
procedure setUserData(account: JAccount; key: JString; value: JString); cdecl;
end;
TJAccountManager = class(TJavaGenericImport<JAccountManagerClass, JAccountManager>) end;

我添加了一些来自 AccountManager.pas 的 classes,JAccount Class 没有 name 属性,它在 JAccountClass 中,但代码有效,但我仍然遇到访问冲突错误。

您应该使用 Accountname 属性 而不是要求将对象转换为字符串。

mmLog.Lines.Add(jstringtostring( jAcc.name));

getAccountsByType() returns Account 对象数组,而不是 class 类型数组。并检查零指针。

试试这个:

var
  jAm: JAccountManager;
  accounts: TJavaObjectArray<JAccount>;
  jAcc: JAccount;
begin
  jAM := TJAccountManager.JavaClass.get(SharedActivityContext);
  if jAM <> nil then begin
    accounts := TJavaObjectArray<JAccount>.Wrap(jAM.getAccountsByType(StringToJString('com.google')));
    if accounts <> nil then begin
      mmLog.Lines.Add('Length Accounts: ' + IntToStr(accounts.Length));
      if accounts.Length > 0 then begin
        jAcc := accounts.Items[0];
        mmLog.Lines.Add(JStringtoString(jAcc.name));
      end else begin
        mmLog.Lines.Add('no accounts available');
      end;
    end;
  end else begin
    mmLog.Lines.Add('no accounts found');
  end;
else begin
  mmLog.Lines.Add('no account manager available');
end;

{class} function _Getname: JString; cdecl;复制到JAccountclass,然后使用此代码:

var
  jAm: JAccountManager;
  accounts: TJavaObjectArray<JAccount>;
  jAcc: JAccount;
begin
  jAM := TJAccountManager.JavaClass.get(SharedActivityContext);
  if jAM <> nil then begin
    accounts := TJavaObjectArray<JAccount>.Wrap(jAM.getAccountsByType(StringToJString('com.google')));
    if accounts <> nil then begin
      mmLog.Lines.Add('Length Accounts: ' + IntToStr(accounts.Length));
      if accounts.Length > 0 then begin
        jAcc := accounts.Items[0];
        mmLog.Lines.Add(JStringtoString(jAcc._Getname));
      end else begin
        mmLog.Lines.Add('no accounts available');
      end;
    end;
  end else begin
    mmLog.Lines.Add('no accounts found');
  end;
end;

由于有一些关于使用代码片段的问题的评论,我认为放入一个完整的单元(尽管包含最少的导入定义)可能有助于解决问题和混淆。

这是一个可以在 Delphi XE8 到 Delphi 10.1 Berlin 中工作的辅助单元(我无法检查早期版本,但原则上应该没问题):

unit AccountEmailsU;

interface

function GetAccountEmails(const AccountType: String): TArray<String>;

implementation

uses
  Androidapi.Helpers,
  Androidapi.Jni,
{$IF Declared(RTLVersion) and (RTLVersion >= 31)}
  // Delphi 10.1 Berlin adds in full imports for the accounts classes
  Androidapi.JNI.Accounts;
{$ELSE}
  Androidapi.JNIBridge,
  Androidapi.JNI.App,
  Androidapi.JNI.GraphicsContentViewText,
  Androidapi.JNI.JavaTypes,
  Androidapi.JNI.Os;

type
// ===== Forward declarations =====

  JAccount = interface;//android.accounts.Account
  JAccountManager = interface;//android.accounts.AccountManager

// ===== Interface declarations =====

  JAccountClass = interface(JObjectClass)
    ['{94EE6861-F326-489F-8919-E20B39E3D9C1}']
  end;

  [JavaSignature('android/accounts/Account')]
  JAccount = interface(JObject)
    ['{71476381-8B6E-471F-9189-9857ECD7508C}']
    function _Getname: JString; cdecl;
    function _Gettype: JString; cdecl;
    property name: JString read _Getname;
    property &type: JString read _Gettype;
  end;
  TJAccount = class(TJavaGenericImport<JAccountClass, JAccount>) end;

  JAccountManagerClass = interface(JObjectClass)
    ['{96273844-2D84-47F0-BFD5-14B73402F843}']
    {class} function &get(context: JContext): JAccountManager; cdecl;
  end;

  [JavaSignature('android/accounts/AccountManager')]
  JAccountManager = interface(JObject)
    ['{9FA4077B-4628-433C-BAFC-9EB299DA9C98}']
    function getAccountsByType(type_: JString): TJavaObjectArray<JAccount>; cdecl;
  end;
  TJAccountManager = class(TJavaGenericImport<JAccountManagerClass, JAccountManager>) end;
{$ENDIF}

function GetAccountEmails(const AccountType: String): TArray<String>;
var
  AccountManager: JAccountManager;
  Accounts: TJavaObjectArray<JAccount>;
  Account: JAccount;
  AccountLoopCounter: Integer;
begin
{$IF RTLVersion >= 30}
  AccountManager := TJAccountManager.JavaClass.get(TAndroidHelper.Context);
{$ELSE}
  AccountManager := TJAccountManager.JavaClass.get(SharedActivityContext);
{$ENDIF}
  if AccountManager <> nil then
  begin
    Accounts := AccountManager.getAccountsByType(StringToJString(AccountType));
    if Accounts <> nil then
    begin
      SetLength(Result, Accounts.Length);
      for AccountLoopCounter := 0 to Pred(Accounts.Length) do
      begin
        //Account := Accounts.Items[AccountLoopCounter];
        Account := TJAccount.Wrap(Accounts.GetRawItem(AccountLoopCounter));
        Result[AccountLoopCounter] := JStringtoString(Account.name);
      end
    end;
  end;
end;

procedure RegisterTypes;
begin
  TRegTypes.RegisterType('AccountEmailsU.JAccount', TypeInfo(AccountEmailsU.JAccount));
  TRegTypes.RegisterType('AccountEmailsU.JAccountManager', TypeInfo(AccountEmailsU.JAccountManager));
end;

initialization
  RegisterTypes;
end.

这可以以类似于此的方式使用:

uses
{$IF RTLVersion >= 31}
  FMX.DialogService,
//{$ELSE}
//  FMX.Dialogs,
{$ENDIF}
  AccountEmailsU,
  MiscU;

procedure TForm1.btnGetAccountEmailsClick(Sender: TObject);
const
  AccountType = 'com.google';
var
  AccountNames: TArray<String>;
  AccountLoopCounter: Integer;
begin
  if not HasPermission('android.permission.GET_ACCOUNTS') then
{$IF RTLVersion >= 31}
    TDialogService.MessageDialog('App does not have the GET_ACCOUNTS permission',
      TMsgDlgType.mtError, [TMsgDlgBtn.mbCancel], TMsgDlgBtn.mbCancel, 0, nil)
{$ELSE}
    MessageDlg('App does not have the GET_ACCOUNTS permission',
      TMsgDlgType.mtError, [TMsgDlgBtn.mbCancel], 0)
{$ENDIF}
  else
  begin
    AccountNames := GetAccountEmails(AccountType);
    AccountsListBox.Items.Clear;
    for AccountLoopCounter := Low(AccountNames) to High(AccountNames) do
      AccountsListBox.Items.Add(AccountNames[AccountLoopCounter])
  end;
end;

权限检查代码来自这个辅助单元:

unit MiscU;

interface

function HasPermission(const Permission: string): Boolean;

implementation

uses
  FMX.Helpers.Android,
  Androidapi.Helpers,
  Androidapi.JNI.JavaTypes,
  Androidapi.JNI.GraphicsContentViewText;

function HasPermission(const Permission: string): Boolean;
begin
  //Permissions listed at http://d.android.com/reference/android/Manifest.permission.html
{$IF RTLVersion >= 30}
  Result := TAndroidHelper.Context.checkCallingOrSelfPermission(
{$ELSE}
  Result := SharedActivityContext.checkCallingOrSelfPermission(
{$ENDIF}
    StringToJString(Permission)) =
    TJPackageManager.JavaClass.PERMISSION_GRANTED
end;

end.