0

I'm trying to get my device email address, I used Java2OP to convert AccountManager class to object pascal. However, I tried to get the email address using the following code:

 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;

I get Access violation at address 415E5254, accessing address 0000002C! any idea guys?

 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;

I've add some of the classes from AccountManager.pas, the JAccount Class does not have the name property, it is in the JAccountClass, but the code works, and I'm still getting the Access violation error.

RBA
  • 12,337
  • 16
  • 79
  • 126
ColdZer0
  • 233
  • 1
  • 4
  • 19
  • 1
    It would help to know which line is throwing the access violation... – J... Mar 21 '16 at 11:55
  • maybe this line `accounts := TJavaObjectArray.Wrap(jAM.getAccountsByType(StringToJString('com.google')));` or this one `mmLog.Lines.Add(jstringtostring( jAcc.name));` , i cant compile without these. @J... – ColdZer0 Mar 21 '16 at 12:16
  • ...so how did you get an access violation if it didn't compile? You shouldn't have to guess here. When you get the access violation, break into the debugger and look at which line the instruction pointer is pointing at - then tell us what that line is. – J... Mar 21 '16 at 12:18
  • it dose compile successfully, i meant deleting those lines, i have a problem currently with the debugger in delphi, so im just guessing!!. tanks Mr. @J... – ColdZer0 Mar 21 '16 at 13:40
  • Hi. What are dependency files for adding the JAccount and JAccountManagerClass – test12345 Aug 28 '16 at 04:10

4 Answers4

3

Since there have been some comments about issues using the code snippets I thought it might be helpful to throw in a full unit (albeit containing minimal import definitions) to try and belay the problems and confusion.

Here is a helper unit that works in Delphi XE8 through to Delphi 10.1 Berlin (I can't check earlier versions, but in principle it should be OK):

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.

This can be used in a fashion akin to this:

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;

The permissions checking code comes from this helper unit:

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.
blong
  • 2,145
  • 13
  • 23
2

getAccountsByType() returns an array of Account objects, not an array of class types. And check for nil pointers.

Try this instead:

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;
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • Many thanks for having a look at my post. However, i still got the error Access violation at address 415E5254, accessing address 0000002C ! and i cant break into debugger unfortunately. @Remy Lebeau – ColdZer0 Mar 27 '16 at 14:19
  • You need to debug your code. If you can't use the actual debugger, you will have to output everything to a log. Either way, you need to analyze your variable values. AVs near address 0 usually imply a nil pointer is being accessed. – Remy Lebeau Mar 27 '16 at 14:23
  • how to output everything to a log?,I'll post the output here. My delphi10 has problem with the debugger currently!. I've add some of the classes form AccountManager.pas if its helps matbe, Thanks. @Remy Lebeau – ColdZer0 Mar 27 '16 at 14:43
  • @Ali.A you are already outputting to a log (`mmLog`). Unless THAT is where your AV is occuring. In which case, fix that, or else [output to Android's native log instead](http://www.fmxexpress.com/ten-top-tips-for-debugging-apps-in-delphi-xe7-firemonkey-on-android-and-ios/). – Remy Lebeau Mar 27 '16 at 14:45
  • Alright, I'll try it and i"ll inform you soon with the result. Thanks – ColdZer0 Mar 27 '16 at 14:51
  • i finally managed to get the debugger works, i got this error in the event log ` First chance exception at $415E3254. Exception class Segmentation fault (11). Process Project1.apk (12305)` , – ColdZer0 Mar 29 '16 at 18:10
  • segfault(11) is the Linux (Android is based on Linux) equivalent of an Access Violation. Now that you have the debugger working, you should be able to analyze the code located at memory address $415E3254 and see how it is accessing an invalid memory pointer. – Remy Lebeau Mar 29 '16 at 18:26
  • i tried my best, the AV occurs in this line `jAcc := accounts.Items[0];` – ColdZer0 Mar 29 '16 at 20:34
  • Hmm, this is not the first time I have seen that issue reported, it has happened before: [XE5 Android - How can I get phone number ???](http://codeverge.com/embarcadero.delphi.firemonkey/xe5-android-how-can-i-get-phone/1056426). Maybe it is a problem with Embarcadero's JNI bridge that has never been addressed. – Remy Lebeau Mar 29 '16 at 20:43
  • yeah, i bookmarked that page before, i think the problem could be in the JAccount class, cause it dosent have `name` property, so i changed it to `JAccountClass` then i got the AV ! – ColdZer0 Mar 29 '16 at 20:48
  • You can't use `JAccountClass` in this situation since Android is not providing an array of class types, it is providing an array of objects. In the Android API, the `Account.name` (and `Account.type`) field is not a `static` class field, it is an object instance field, so it belongs in `JAccount`, not in `JAccountClass`. It sounds like Java2OP is generating bad code. You should file a bug report with Embarcadero. – Remy Lebeau Mar 29 '16 at 22:16
1

You should use the name property of the Account and not asking to convert the object to a string.

mmLog.Lines.Add(jstringtostring( jAcc.name));
Nicolas Dusart
  • 1,867
  • 18
  • 26
1

copy {class} function _Getname: JString; cdecl; to JAccount class, then use this code:

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;
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
ColdZer0
  • 233
  • 1
  • 4
  • 19