Cómo crear un autenticador de stub

El marco de trabajo del adaptador de sincronización asume 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 esta razón, el marco de trabajo espera que proporciones un componente llamado autenticador como parte de tu adaptador de sincronización. Este componente se conecta con las cuentas de Android y el marco de trabajo de autenticación, y proporciona una interfaz estándar para manejar las credenciales de los usuarios, 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 de stub. También debes proporcionar un Service vinculado que permita que el marco de trabajo 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 satisfacer los requisitos del marco de trabajo del adaptador de sincronización. Si debes 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 cancela los métodos requeridos, ya sea mostrando un null o lanzando 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 marco de trabajo del adaptador de sincronización acceda a tu autenticador, debes crearle un servicio vinculado. Este servicio proporciona un objeto vinculante de Android que permite que el marco de trabajo llame a tu autenticador y se transfieran datos entre ambos.

Como el marco de trabajo inicia este Service la primera vez que necesita acceder al autenticador, también puedes usar el servicio para crear una instancia del autenticador llamando al constructor del autenticador en el método Service.onCreate() del servicio.

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(this)
        }

        /*
         * 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(this);
        }
        /*
         * 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 del adaptador de sincronización con el marco de trabajo, 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 XML almacenado en el directorio /res/xml/ del proyecto de tu app. Puedes asignarle cualquier nombre al archivo, aunque generalmente se lo llama authenticator.xml.

Este archivo XML incluye un solo elemento XML <account-authenticator>, que tiene los siguientes atributos:

android:accountType
El marco de trabajo del adaptador de sincronización requiere que cada adaptador de sincronización tenga un tipo de cuenta, que usa como parte de la identificación interna del adaptador de sincronización. Para los servidores que requieren acceso, se envía el tipo de cuenta, junto con una cuenta de usuario, 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 sobre el que tengas control. Si bien el marco de trabajo lo usará para administrar tu adaptador de sincronización, no se enviará el valor a tu servidor.

android:icon
Es un elemento que apunta a un recurso de elemento de diseño que contiene un ícono. Si haces visible el adaptador de sincronización especificando el atributo android:userVisible="true" como res/xml/syncadapter.xml, deberás proporcionar este recurso de ícono. Aparece en la sección Accounts de la app de configuración del sistema.
android:smallIcon
Es un elemento que apunta a un recurso de elemento de diseño que contiene una versión pequeña del ícono. Se puede usar 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 string localizable que identifica el tipo de cuenta para los usuarios. Si haces visible el adaptador de sincronización especificando el atributo android:userVisible="true" como res/xml/syncadapter.xml, deberás 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 marco del adaptador de sincronización. Para identificar este servicio en el sistema, debes declararlo en el manifiesto de tu app agregando un objeto <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> establece un filtro que se activa por la acción del intent android.accounts..AccountAuthenticator, que envió 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, android:name vincula los metadatos con el marco de trabajo de autenticación y android:resource especifica el nombre del archivo de metadatos del autenticador que creaste.

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.