Cómo crear un autenticador de stub

El framework del adaptador de sincronización prevé que tu adaptador de sincronización transfiere datos entre el almacenamiento del dispositivo asociado con una cuenta y el almacenamiento del servidor que requiere acceso. Por este motivo, el framework espera que proporciones un componente llamado autenticador como parte de tu adaptador de sincronización. Este componente se conecta a las cuentas de Android y al marco de trabajo de autenticación, y proporciona una interfaz estándar para manejar las credenciales del usuario, como la información de acceso.

Incluso si tu app no usa cuentas, debes proporcionar un componente de autenticación. Si no usas cuentas o acceso al servidor, se ignora la información que controla el autenticador, por lo que puedes proporcionar un componente de autenticador que contenga implementaciones del método de stub. También debes proporcionar un Service vinculado que permita que el framework del adaptador de sincronización llame a los métodos del autenticador.

En esta lección, se muestra cómo definir todas las partes de un autenticador de stub que necesitas para cumplir con los requisitos del framework del adaptador de sincronización. Si necesitas proporcionar un autenticador real que controle cuentas de usuario, lee la documentación de referencia de AbstractAccountAuthenticator.

Cómo agregar un componente de autenticador de stub

Para agregar un componente de autenticador de stub a tu app, crea una clase que extienda AbstractAccountAuthenticator y, luego, crea stubs de los métodos requeridos. Para ello, muestra null o arroja una excepción.

En el siguiente fragmento, se muestra un ejemplo de una clase de autenticador de stub:

Kotlin

/*
 * Implement AbstractAccountAuthenticator and stub out all
 * of its methods
 */
class Authenticator(context: Context) // Simple constructor
    : AbstractAccountAuthenticator(context) {

    // Editing properties is not supported
    override fun editProperties(r: AccountAuthenticatorResponse, s: String): Bundle {
        throw UnsupportedOperationException()
    }

    // Don't add additional accounts
    @Throws(NetworkErrorException::class)
    override fun addAccount(
            r: AccountAuthenticatorResponse,
            s: String,
            s2: String,
            strings: Array<String>,
            bundle: Bundle
    ): Bundle?  = null

    // Ignore attempts to confirm credentials
    @Throws(NetworkErrorException::class)
    override fun confirmCredentials(
            r: AccountAuthenticatorResponse,
            account: Account,
            bundle: Bundle
    ): Bundle?  = null

    // Getting an authentication token is not supported
    @Throws(NetworkErrorException::class)
    override fun getAuthToken(
            r: AccountAuthenticatorResponse,
            account: Account,
            s: String,
            bundle: Bundle
    ): Bundle {
        throw UnsupportedOperationException()
    }

    // Getting a label for the auth token is not supported
    override fun getAuthTokenLabel(s: String): String {
        throw UnsupportedOperationException()
    }

    // Updating user credentials is not supported
    @Throws(NetworkErrorException::class)
    override fun updateCredentials(
            r: AccountAuthenticatorResponse,
            account: Account,
            s: String,
            bundle: Bundle
    ): Bundle {
        throw UnsupportedOperationException()
    }

    // Checking features for the account is not supported
    @Throws(NetworkErrorException::class)
    override fun hasFeatures(
            r: AccountAuthenticatorResponse,
            account: Account,
            strings: Array<String>
    ): Bundle {
        throw UnsupportedOperationException()
    }
}

Java

/*
 * Implement AbstractAccountAuthenticator and stub out all
 * of its methods
 */
public class Authenticator extends AbstractAccountAuthenticator {
    // Simple constructor
    public Authenticator(Context context) {
        super(context);
    }
    // Editing properties is not supported
    @Override
    public Bundle editProperties(
            AccountAuthenticatorResponse r, String s) {
        throw new UnsupportedOperationException();
    }
    // Don't add additional accounts
    @Override
    public Bundle addAccount(
            AccountAuthenticatorResponse r,
            String s,
            String s2,
            String[] strings,
            Bundle bundle) throws NetworkErrorException {
        return null;
    }
    // Ignore attempts to confirm credentials
    @Override
    public Bundle confirmCredentials(
            AccountAuthenticatorResponse r,
            Account account,
            Bundle bundle) throws NetworkErrorException {
        return null;
    }
    // Getting an authentication token is not supported
    @Override
    public Bundle getAuthToken(
            AccountAuthenticatorResponse r,
            Account account,
            String s,
            Bundle bundle) throws NetworkErrorException {
        throw new UnsupportedOperationException();
    }
    // Getting a label for the auth token is not supported
    @Override
    public String getAuthTokenLabel(String s) {
        throw new UnsupportedOperationException();
    }
    // Updating user credentials is not supported
    @Override
    public Bundle updateCredentials(
            AccountAuthenticatorResponse r,
            Account account,
            String s, Bundle bundle) throws NetworkErrorException {
        throw new UnsupportedOperationException();
    }
    // Checking features for the account is not supported
    @Override
    public Bundle hasFeatures(
        AccountAuthenticatorResponse r,
        Account account, String[] strings) throws NetworkErrorException {
        throw new UnsupportedOperationException();
    }
}

Cómo vincular el autenticador con el marco de trabajo

Para que el framework del adaptador de sincronización acceda a tu autenticador, debes crearle un Service vinculado. Este servicio proporciona un objeto vinculante de Android que permite que el framework llame a tu autenticador y se pasen datos entre ambos.

En el siguiente fragmento, se muestra cómo definir el Service vinculado:

Kotlin

/**
* A bound Service that instantiates the authenticator
* when started.
*/
class AuthenticatorService : Service() {

    // Instance field that stores the authenticator object
    private lateinit var mAuthenticator: Authenticator

    override fun onCreate() {
        // Create a new authenticator object
        mAuthenticator = Authenticator(getApplicationContext())
    }

    /*
     * When the system binds to this Service to make the RPC call
     * return the authenticator's IBinder.
     */
    override fun onBind(intent: Intent?): IBinder = mAuthenticator.iBinder
}

Java

/**
 * A bound Service that instantiates the authenticator
 * when started.
 */
public class AuthenticatorService extends Service {
    ...
    // Instance field that stores the authenticator object
    private Authenticator mAuthenticator;
    @Override
    public void onCreate() {
        // Create a new authenticator object
        mAuthenticator = new Authenticator(getApplicationContext());
    }
    /*
     * When the system binds to this Service to make the RPC call
     * return the authenticator's IBinder.
     */
    @Override
    public IBinder onBind(Intent intent) {
        return mAuthenticator.getIBinder();
    }
}

Cómo agregar el archivo de metadatos del autenticador

Para conectar el componente autenticador al adaptador de sincronización y a los frameworks de la cuenta, debes proporcionar metadatos que describan el componente. Estos metadatos declaran el tipo de cuenta que creaste para tu adaptador de sincronización y declaran los elementos de la interfaz de usuario que el sistema muestra si deseas que el tipo de cuenta sea visible para el usuario. Declara estos metadatos en un archivo en formato XML almacenado en el directorio /res/xml/ del proyecto de tu app. Puedes nombrar el archivo como quieras, aunque generalmente se llama authenticator.xml.

Este archivo en formato XML contiene un solo elemento <account-authenticator> que tiene los siguientes atributos:

android:accountType
El framework del adaptador de sincronización requiere que cada adaptador de sincronización tenga un tipo de cuenta, en forma de nombre de dominio. El framework usa el tipo de cuenta como parte de la identificación interna del adaptador de sincronización. Para los servidores que requieren acceso, el tipo de cuenta y una cuenta de usuario se envían al servidor como parte de las credenciales de acceso.

Si el servidor no requiere acceso, debes proporcionar un tipo de cuenta. Para el valor, usa un nombre de dominio que controles. Si bien el framework lo usa para administrar tu adaptador de sincronización, el valor no se envía a tu servidor.

android:icon
Es un elemento que apunta a un recurso de un elemento de diseño que contiene un ícono. Si haces visible el adaptador de sincronización especificando el atributo android:userVisible="true" en res/xml/syncadapter.xml, debes proporcionar este recurso de ícono. Aparece en la sección Cuentas de la app de configuración del sistema.
android:smallIcon
Es un elemento que apunta a un recurso de un elemento de diseño que contiene una versión pequeña del ícono. Se puede usar este recurso en lugar de android:icon en la sección Cuentas de la app de Configuración del sistema, según el tamaño de la pantalla.
android:label
Es una cadena localizable que identifica el tipo de cuenta para los usuarios. Si haces visible el adaptador de sincronización especificando el atributo android:userVisible="true" en res/xml/syncadapter.xml, debes proporcionar esta string. Aparece en la sección Cuentas de la app de Configuración del sistema, junto al ícono que definiste para el autenticador.

En el siguiente fragmento de código, se muestra el archivo XML del autenticador que creaste anteriormente:

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

Cómo declarar el autenticador en el manifiesto

En un paso anterior, creaste un Service vinculado que vincula el autenticador con el framework del adaptador de sincronización. Para identificar este servicio en el sistema, debes declararlo en el manifiesto de tu app. Para ello, agrega el siguiente elemento <service> como elemento secundario de <application>:

    <service
            android:name="com.example.android.syncadapter.AuthenticatorService">
        <intent-filter>
            <action android:name="android.accounts.AccountAuthenticator"/>
        </intent-filter>
        <meta-data
            android:name="android.accounts.AccountAuthenticator"
            android:resource="@xml/authenticator" />
    </service>

El elemento <intent-filter> configura un filtro que se activa mediante la acción de intent android.accounts.AccountAuthenticator, que envía el sistema para ejecutar el autenticador. Cuando se activa el filtro, el sistema inicia AuthenticatorService, el Service vinculado que proporcionaste para unir el autenticador.

El elemento <meta-data> declara los metadatos del autenticador. El atributo android:name vincula los metadatos con el framework de autenticación. El elemento android:resource especifica el nombre del archivo de metadatos del autenticador que creaste anteriormente.

Además de un autenticador, el adaptador de sincronización también requiere un proveedor de contenido. Si tu app aún no utiliza un proveedor de contenido, ve a la siguiente lección para obtener información sobre cómo crear un proveedor de contenido de stub; de lo contrario, consulta Cómo crear un adaptador de sincronización.