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
What Exactly Is a Context in Java
Android: How to Handle Button Click
Get a JSON Object from a Http Response
Jar Mismatch Found 2 Versions of Android-Support-V4.Jar in the Dependency List
Key Hash for Facebook Android Sdk
Capture Screen of Glsurfaceview to Bitmap
Recyclerview Not Call Oncreateviewholder
Android App in Eclipse: Edit Text Not Showing on Graphical Layout
Deserializing Generic Types with Gson
More Efficient Way for Pausing Loop Wanted
Android: Realm + Retrofit 2 + Gson
How to Read Firestore Sub-Collection and Pass It to Firestorerecycleroptions
Android Fragmentmanager Backstackrecord.Run Throwing Nullpointerexception
Linearlayoutmanager Setreverselayout() == True But Items Stack from Bottom