Cómo agregar programaticamente una cuenta personalizada en android?

Estoy intentando crear una cuenta para mi aplicación, donde podré tener mis contactos en mi cuenta como facebook, viber, whatsapp, etc. Quiero que mi cuenta esté visible en la sección de cuenta de la configuración también. ¿Algunas ideas? He googled mucho, pero no pude encontrar una respuesta correcta por dónde empezar. Por favor ayuda. Lo que he intentado crear una cuenta es la siguiente. Lo que me lleva a un error.

Account account = new Account("Title", "com.package.nom"); String password = "password"; AccountManager accountManager = (AccountManager) MainPanel.this.getSystemService( ACCOUNT_SERVICE); accountManager.addAccountExplicitly(account, password, null); 

Es necesario configurar varios componentes para poder crear una cuenta mediante programación. Necesitas:

  • Un AccountAuthenticator
  • Un servicio para proporcionar acceso a AccountAuthenticator
  • Algunos permisos

El autenticador

El autenticador es un objeto que hará el mapeo entre el tipo de cuenta y la autoridad (es decir, el usuario linux) que tienen derechos para administrarlo.

La declaración de un autenticador se realiza en xml:

  • Cree un archivo res/xml/authenticator.xml

Con el siguiente contenido:

 <?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"/> 

Observe el accountType: debe ser reutilizado en código cuando se crea la cuenta. Los iconos y la etiqueta serán utilizados por la aplicación "Configuración" para mostrar las cuentas de ese tipo.

Implementación de AccountAuthenticator

Debe extender AbstractAccountAuthenticator para hacer eso. Esto será utilizado por una aplicación de terceros para acceder a los datos de la cuenta.

El siguiente ejemplo no permite ningún acceso a aplicaciones de terceros y por lo tanto la implementación de cada método es 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. } } 

El servicio que expone el tipo de cuenta

Cree un servicio para manipular las cuentas de ese tipo:

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

Declare el servicio en su manifiesto :

 <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> 

Aquí, el filtro y los metadatos referidos al recurso xml que declaran el autenticador son los puntos clave.

Los permisos

En su manifiesto asegúrese de declarar los siguientes permisos

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

(No todos los requeridos para el código de ejemplo presentado en esta publicación, pero probablemente tendrá más código sobre la administración de cuentas y al final todos ellos serán útiles)

Crear una cuenta en código

Ahora que todo está listo, crea una cuenta con el siguiente código. Tenga en cuenta el boolean devuelto por addAccountExplicitly informándole sobre el éxito o el error.

  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"); } 

Consejos finales

No instale su aplicación en un almacenamiento externo

Si su aplicación está instalada en un almacenamiento externo, existe una buena probabilidad de que Android elimine los datos de su cuenta cuando se desmonte la tarjeta sd (ya que el autenticador para esa cuenta ya no será accesible). Así que para evitar esta pérdida (en cada reinicio!) Debe instalar la aplicación que declara el autenticador sólo en el almacenamiento interno:

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

En caso de problemas

Lea los registros con cuidado, El AccountManger está produciendo muchos registros para ayudarle a depurar su código.

Aquí un código cortado lo estoy haciendo (lo siento por commetns alemán) no se olvide de establecer los permisos propper en el archivo de manifiesto.

 /** * ueberprueft, ob es den account fuer diese app schon gibt und legt ihn * gegebenenfalls an. * * @param none * @return void */ public void verifyAccount() { if (debug) Log.i(TAG, "verifyAccount() "); boolean bereitsAngelegt = false; String accountType; accountType = this.getPackageName(); AccountManager accountManager = AccountManager .get(getApplicationContext()); Account[] accounts = accountManager.getAccounts(); for (int i = 0; i < accounts.length; i++) { if (debug) Log.v(TAG, accounts[i].toString()); if ((accounts[i].type != null) && (accounts[i].type.contentEquals(accountType))) { bereitsAngelegt = true; if (debug) Log.v(TAG, "verifyAccount(): bereitsAngelegt " + accounts[i].type); } } if (!bereitsAngelegt) { if (debug) Log.v(TAG, "verifyAccount(): !bereitsAngelegt "); // This is the magic that addes the account to the Android Account // Manager AccountManager accMgr = AccountManager.get(this); String password = "some_password"; if (debug) Log.d(TAG, "verifyAccount(): ADD: accountName: " + Konst.accountName + " accountType: " + accountType + " password: " + password); final Account account = new Account(Konst.accountName, accountType); if (debug) Log.v(TAG, "verifyAccount(): nach final Account account "); try { accMgr.addAccountExplicitly(account, password, null); } catch (Exception e1) { if (debug) Log.v(TAG, "verifyAccount(): Exception e1 " + e1.toString()); this.finish(); } if (debug) Log.v(TAG, "verifyAccount(): nach accMgr.addAccountExplicitly() "); } else { if (debug) Log.v(TAG, "verifyAccount(): bereitsAngelegt "); } } // end of public void verifyAccount() 

espero que esto ayude un poco.

He escrito una biblioteca para esto, que te libera de hacer las tareas necesarias para la gestión de cuentas android, como la definición de un servicio vinculado, xml autenticador, etc Trabajar con eso es en 5 sencillos pasos:

Paso 1

Añada esto a las dependencias de build.gradle de la aplicación:

 compile 'com.digigene.android:account-authenticator:1.3.0' 

Paso 2

Defina su tipo de cuenta de autenticación como una cadena en strings.xml :

 <string name="auth_account_type">DigiGene</string> 

Reemplace 'DigiGene' con su propio tipo de cuenta. Esto es lo que aparece en las cuentas de Android en esta captura de pantalla .

Paso 3

Diseñe su diseño de registro para registrar a los usuarios (por ejemplo, esta imagen ):

 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.digigene.authenticatortest.MainActivity"> <EditText android:id="@+id/account_name" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center_horizontal" android:hint="User Name" /> <EditText android:id="@+id/password" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@+id/account_name" android:gravity="center_horizontal" android:hint="Password" android:inputType="textPassword" /> <Button android:id="@+id/register" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@+id/password" android:text="register" android:onClick="startAuthentication"/> </RelativeLayout> 

Y crear una nueva clase, digamos MyRegistrationActivity.java , con el siguiente código:

 import com.digigene.accountauthenticator.activity.RegistrationActivity; public class MyRegistrationActivity extends RegistrationActivity { private EditText accountNameEditText, passwordEditText; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.registration_layout); accountNameEditText = (EditText) findViewById(R.id.account_name); passwordEditText = (EditText) findViewById(R.id.password); } public void startAuthentication(View view) { register(accountNameEditText.getText().toString(), passwordEditText.getText().toString(), null, null); } } 

Etapa 4

Haga un diseño de entrada como aquí :

 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.digigene.authenticatortest.MainActivity"> <EditText android:id="@+id/account_name" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center_horizontal" android:hint="User Name" /> <Button android:id="@+id/register" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@+id/account_name" android:text="Sign in" android:onClick="signIn"/> <Button android:id="@+id/add" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@+id/register" android:text="Add user" android:onClick="addUser"/> </RelativeLayout> 

Este diseño va con la siguiente clase:

 import com.digigene.accountauthenticator.AuthenticatorManager; public class MainActivity extends Activity { EditText accountNameEditText; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); accountNameEditText = (EditText) findViewById(R.id.account_name); } public void signIn(View view) { AuthenticatorManager authenticatorManager = new AuthenticatorManager(MainActivity.this, getString(R.string.auth_account_type), this, MyRegistrationActivity.class, MyInterfaceImplementation.class); String authTokenType = "REGULAR_USER"; AuthenticatorManager.authenticatorManager = authenticatorManager; authenticatorManager.getAccessToken(accountNameEditText.getText().toString(), authTokenType, null); } public void addUser(View view) { AuthenticatorManager authenticatorManager = new AuthenticatorManager(MainActivity.this, getString(R.string.auth_account_type), this, MyRegistrationActivity.class, MyInterfaceImplementation.class); String authTokenType = "REGULAR_USER"; AuthenticatorManager.authenticatorManager = authenticatorManager; authenticatorManager.addAccount(authTokenType, null, null); } } 

Paso 5

Éste es el último paso en el que se implementan los métodos necesarios para conectarse al servidor para fines de registro y de inicio de sesión y después. A continuación, a diferencia de un caso real, las conexiones del servidor se burlan, sólo para demostrar la funcionalidad de la biblioteca. Usted puede reemplazar la siguiente implementación con su propia real.

 import com.digigene.accountauthenticator.AbstractInterfaceImplementation; import com.digigene.accountauthenticator.AuthenticatorManager; import com.digigene.accountauthenticator.result.RegisterResult; import com.digigene.accountauthenticator.result.SignInResult; import com.digigene.accountauthenticator.result.SignUpResult; public class MyInterfaceImplementation extends AbstractInterfaceImplementation { public static int accessTokenCounter = 0; public static int refreshTokenCounter = 0; public static int demoCounter = 0; public static int accessTokenNo = 0; public static int refreshTokenNo = 0; public final int ACCESS_TOKEN_EXPIRATION_COUNTER = 2; public final int REFRESH_TOKEN_EXPIRATION_COUNTER = 5; public final int DEMO_COUNTER = 15; @Override public String[] userAccessTypes() { return new String[]{"REGULAR_USER", "SUPER_USER"}; } @Override public void doAfterSignUpIsUnsuccessful(Context context, Account account, String authTokenType, SignUpResult signUpResult, Bundle options) { Toast.makeText(context, "Sign-up was not possible due to the following:\n" + signUpResult .errMessage, Toast.LENGTH_LONG).show(); AuthenticatorManager.authenticatorManager.addAccount(authTokenType, null, options); } @Override public void doAfterSignInIsSuccessful(Context context, Account account, String authTokenType, String authToken, SignInResult signInResult, Bundle options) { demoCounter = demoCounter + 1; Toast.makeText(context, "User is successfully signed in: \naccessTokenNo=" + accessTokenNo + "\nrefreshTokenNo=" + refreshTokenNo + "\ndemoCounter=" + demoCounter, Toast.LENGTH_SHORT).show(); } @Override public SignInResult signInToServer(Context context, Account account, String authTokenType, String accessToken, Bundle options) { accessTokenCounter = accessTokenCounter + 1; SignInResult signInResult = new SignInResult(); signInResult.isSuccessful = true; synchronized (this) { try { this.wait(2000); } catch (InterruptedException e) { e.printStackTrace(); } } if ((accessTokenCounter > ACCESS_TOKEN_EXPIRATION_COUNTER || demoCounter > DEMO_COUNTER)) { signInResult.isSuccessful = false; signInResult.isAccessTokenExpired = true; if (demoCounter < DEMO_COUNTER) { signInResult.errMessage = "Access token is expired"; return signInResult; } } return signInResult; } @Override public SignUpResult signUpToServer(Context context, Account account, String authTokenType, String refreshToken, Bundle options) { SignUpResult signUpResult = new SignUpResult(); synchronized (this) { try { this.wait(2000); } catch (InterruptedException e) { e.printStackTrace(); } } refreshTokenCounter = refreshTokenCounter + 1; signUpResult.isSuccessful = true; signUpResult.accessToken = "ACCESS_TOKEN_NO_" + accessTokenNo; signUpResult.refreshToken = "REFRESH_TOKEN_NO_" + refreshTokenNo; if (demoCounter > DEMO_COUNTER) { signUpResult.isSuccessful = false; signUpResult.errMessage = "You have reached your limit of using the demo version. " + "Please buy it for further usage"; return signUpResult; } if (refreshTokenCounter > REFRESH_TOKEN_EXPIRATION_COUNTER) { refreshTokenCounter = 0; signUpResult.isSuccessful = false; signUpResult.errMessage = "User credentials have expired, please login again"; return signUpResult; } if (accessTokenCounter > ACCESS_TOKEN_EXPIRATION_COUNTER) { accessTokenCounter = 0; accessTokenNo = accessTokenNo + 1; signUpResult.accessToken = "ACCESS_TOKEN_NO_" + accessTokenNo; } return signUpResult; } @Override public RegisterResult registerInServer(Context context, Account account, String password, String authTokenType, String[] requiredFeatures, Bundle options) { RegisterResult registerResult = new RegisterResult(); registerResult.isSuccessful = false; synchronized (this) { try { this.wait(2000); } catch (InterruptedException e) { e.printStackTrace(); } } if (true) { // password is checked here and, if true, refresh token is generated for the // user refreshTokenNo = refreshTokenNo + 1; accessTokenNo = accessTokenNo + 1; registerResult.isSuccessful = true; registerResult.refreshToken = "REFRESH_TOKEN_NO_" + refreshTokenNo; } return registerResult; } @Override public boolean setDoesCallbackRunInBackgroundThread() { return false; } } 

Resultados

A continuación se muestra la biblioteca en acción. Puede encontrar el tutorial completo aquí y cómo AccountManager en android funciona en estos tres mensajes de mi sitio web: parte 1 , parte 2 , parte 3 .

Una aplicación de ejemplo que utiliza la biblioteca

  • ¿Se puede sincronizar un Android AsyncTask doInBackground para serializar la ejecución de la tarea?
  • Android JobScheduler se ejecuta demasiado a menudo cuando se utiliza setPeriodic ()
  • Reproducción de audio sincronizada en dispositivos Android distintos
  • Espere los datos async retrive de Firebase en android
  • ¿Cómo sincronizar el acceso a sqlite db entre el primer plano y el proceso en segundo plano?
  • Monitor de estado de subprocesos. ¿Cómo depurar esto? ¿Qué lo causa?
  • ¿Cómo configurar correctamente el syncAdapter?
  • Cómo conectar un teléfono HTC android a la PC como dispositivo de depuración
  • Android Studio 1.0 'runProguard' vs 'minifyEnabled'
  • Android sync / descargar marco
  • Mejor manera de sincronizar contactos
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.