How to Add Programmatically a Custom Account in Android

How to add programmatically a custom account in android?

You need to setup multiple components to be able to create an account programmatically. You need:

  • an AccountAuthenticator
  • a Service to provide access to the AccountAuthenticator
  • some permissions

The authenticator

The authenticator is an object that will make the mapping between the account type and the autority (i.e. the linux-user) that have rights to manage it.

Declaring an authenticator is done in xml :

  • create a file res/xml/authenticator.xml

with the following content :

<?xml version="1.0" encoding="utf-8"?>
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
android:accountType="com.company.demo.account.DEMOACCOUNT"
android:icon="@drawable/ic_launcher"
android:smallIcon="@drawable/ic_launcher"
android:label="@string/my_custom_account"/>

Note the accountType : it must be reused in code when you create the Account.
The icons and label will be used by the "Settings" app to display the accounts of that type.

Implementing the AccountAuthenticator

You must extends AbstractAccountAuthenticator to do that. This will be use by third party app to access Account data.

The following sample don't allow any access to 3rd-party app and so the implementation of each method is trivial.

public class CustomAuthenticator extends AbstractAccountAuthenticator {

public CustomAuthenticator(Context context) {
super(context);
}

@Override
public Bundle addAccount(AccountAuthenticatorResponse accountAuthenticatorResponse, String s, String s2, String[] strings, Bundle bundle) throws NetworkErrorException {
return null; //To change body of implemented methods use File | Settings | File Templates.
}

@Override
public Bundle editProperties(AccountAuthenticatorResponse accountAuthenticatorResponse, String s) {
return null; //To change body of implemented methods use File | Settings | File Templates.
}

@Override
public Bundle confirmCredentials(AccountAuthenticatorResponse accountAuthenticatorResponse, Account account, Bundle bundle) throws NetworkErrorException {
return null; //To change body of implemented methods use File | Settings | File Templates.
}

@Override
public Bundle getAuthToken(AccountAuthenticatorResponse accountAuthenticatorResponse, Account account, String s, Bundle bundle) throws NetworkErrorException {
return null; //To change body of implemented methods use File | Settings | File Templates.
}

@Override
public String getAuthTokenLabel(String s) {
return null; //To change body of implemented methods use File | Settings | File Templates.
}

@Override
public Bundle updateCredentials(AccountAuthenticatorResponse accountAuthenticatorResponse, Account account, String s, Bundle bundle) throws NetworkErrorException {
return null; //To change body of implemented methods use File | Settings | File Templates.
}

@Override
public Bundle hasFeatures(AccountAuthenticatorResponse accountAuthenticatorResponse, Account account, String[] strings) throws NetworkErrorException {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
}

The Service exposing the Account Type

Create a Service to manipulate the Accounts of that type :

public class AuthenticatorService extends Service {
@Override
public IBinder onBind(Intent intent) {
CustomAuthenticator authenticator = new CustomAuthenticator(this);
return authenticator.getIBinder();
}
}

Declare the service in your manifest :

<service android:name="com.company.demo.account.AuthenticatorService" android:exported="false">
<intent-filter>
<action android:name="android.accounts.AccountAuthenticator"/>
</intent-filter>
<meta-data
android:name="android.accounts.AccountAuthenticator"
android:resource="@xml/authenticator"/>
</service>

Here, the filter and the meta-data referring to the xml resource declaring the authenticator are the key points.

The permissions

In your manifest be sure to declare the following permissions

<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS"/>
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS"/>

(not all required for the sample code presented in this post, but you will probably have some more code about account management and at the end all of them will be useful)

Create an account in code

Now that everything is ready you create an account with the following code. Note the boolean returned by addAccountExplicitly informing you about the success or failure.

    AccountManager accountManager = AccountManager.get(this); //this is Activity
Account account = new Account("MyAccount","com.company.demo.account.DEMOACCOUNT");
boolean success = accountManager.addAccountExplicitly(account,"password",null);
if(success){
Log.d(TAG,"Account created");
}else{
Log.d(TAG,"Account creation failed. Look at previous logs to investigate");
}

Final tips

Don't install your app on external storage

If your app is installed on external storage, there are good chance that Android delete your Account data when sdcard is unmounted (since the authenticator for that account will not be accessible anymore). So to avoid this loss (on every reboot !!!) you must install the App declaring the authenticator on internal storage only :

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
android:installLocation="internalOnly"
...

In case of trouble

Read the logs carefully, The AccountManger is outputing many logs to help you to debug your code.

create custom account Android

Did you checkout the sample sync adapter app? It's a little complicated and convoluted, but if you stick with it you can get things working. I've got a slightly simpler implementation in an android app I've been working on if you want to check that out as well.

Accounts are one of those things that I really wish android made a lot simpler. It used to be a poorly documented yet critical feature. They documentation seems to be getting better though.

Add account automatically

There is Android AtLeap library which contains helper classes to use Account Authenticator. Have a look at it https://github.com/blandware/android-atleap

how to add google account programmatically in android with one click in background?

Basically this would be impossible to do because adding an new google account without the prior information of the user itself would definetely be a kind of malware or theft app.

Therefore according to Google this would lead to violate the user freedom and its trust towards Android operating system.

But you can add new google account and can ask user to enter the password for that account. So there will be no permission violation.

How do I create an Android App with an account on user's device ?

You are looking for the AccountManager
http://developer.android.com/reference/android/accounts/AccountManager.html

How to add programmatically a custom account in android?

Custom Account Type with Android AccountManager

Android decoupling came to bite me again. It appears that both apps needed to also have a sync_adapter.xml like:

<!-- The attributes in this XML file provide configuration information for the SyncAdapter. -->
<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
android:contentAuthority="mypackage"
android:accountType="mypackage.account"
android:supportsUploading="true"
android:userVisible="true"
android:allowParallelSyncs="false"
android:isAlwaysSyncable="true"/>

and connect that to the sync service in the AndroidManifest.xml:

<!-- Data sync service that provides the SyncAdapter to the SyncManager framework. The SyncAdapter is used to
maintain that the set of data on the device is a subset of the data on the server -->
<service android:exported="true" android:name=".data.sync.SyncService" tools:ignore="ExportedService">
<intent-filter>
<action android:name="android.content.SyncAdapter"/>
</intent-filter>
<meta-data android:name="android.content.SyncAdapter" android:resource="@xml/sync_adapter"/>
</service>

For completeness, my Service is implemented as follows:

/**
* Service that provides sync functionality to the SyncManager through the {@link SyncAdapter}.
*/
public class SyncService extends Service {

@Override
public void onCreate() {
synchronized (_sync_adapter_lock) {
if (_sync_adapter == null)
_sync_adapter = new SyncAdapter(getApplicationContext(), false);
}
}

@Override
public IBinder onBind(Intent intent) {
return _sync_adapter.getSyncAdapterBinder();
}

private static final Object _sync_adapter_lock = new Object();
private static SyncAdapter _sync_adapter = null;
}

and the SyncAdapter:

/**
* Sync adapter for KeepandShare data.
*/
public class SyncAdapter extends AbstractThreadedSyncAdapter {

public SyncAdapter(Context context, boolean should_auto_initialize) {
super(context, should_auto_initialize);

//noinspection ConstantConditions,PointlessBooleanExpression
if (!BuildConfig.DEBUG) {
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread thread, Throwable throwable) {
Log.e("Uncaught sync exception, suppressing UI in release build.", throwable);
}
});
}
}

@Override
public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider,
SyncResult sync_result) {
// TODO: implement sync
}
}

Even though I'm not actually syncing any data (the apps are not even linked to any server right now), the Android framework appears to be using the settings of the SyncAdapter to figure out which account authenticator respond to the add account request.



Related Topics



Leave a reply



Submit