Cómo optimizar tu app para el autocompletado

Las apps que usan vistas estándar funcionan con Autofill Framework sin necesitar una configuración especial. Sin embargo, puedes optimizar el funcionamiento de tu app con el marco de trabajo. Para obtener un instructivo guiado, consulta el codelab Cómo optimizar tu app para el autocompletado.

Cómo configurar el entorno de autocompletado

En esta sección, se describe cómo configurar la función básica de autocompletado para tu app.

Cómo configurar un servicio de autocompletado

Debes configurar el servicio de autocompletado en tu dispositivo para que tu app use Autofill Framework. Aunque la mayoría de los teléfonos y tablets que ejecutan Android 8.0 (API nivel 26) y versiones posteriores incluyen un servicio de autocompletado, te recomendamos que primero utilices un servicio de prueba en tu app, como el que se muestra en el ejemplo de Android Autofill Framework. Cuando uses un emulador, configura explícitamente un servicio de autocompletado, ya que es posible que el emulador no incluya un servicio predeterminado.

Cuando hayas instalado el servicio de autocompletado de prueba desde la app de muestra, habilítalo. Para ello, navega a Configuración > Sistema > Idiomas y entrada > Avanzado > Ayuda con métodos de entrada > Servicio de autocompletado.

Para obtener más información sobre cómo configurar un emulador para probar el autocompletado, consulta Cómo probar tu app con autocompletado.

Cómo proporcionar sugerencias de autocompletado

El servicio de autocompletado intenta determinar el tipo de cada vista mediante la heurística. Sin embargo, si tu app se basa en estas heurísticas, el comportamiento del autocompletado puede cambiar inesperadamente a medida que la actualizas. Para asegurarte de que el servicio de autocompletado identifique de manera correcta los factores de forma de tu app, proporciona sugerencias de autocompletado.

Puedes configurar estas sugerencias con el atributo android:autofillHints. En el siguiente ejemplo, se configura una sugerencia de "contraseña" en un EditText:

    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:autofillHints="password" />
    

También puedes configurar sugerencias de manera programática con el método setAutofillHints(), como se muestra en el siguiente ejemplo:

Kotlin

    val password = findViewById<EditText>(R.id.password)
    password.setAutofillHints(View.AUTOFILL_HINT_PASSWORD)
    

Java

    EditText password = findViewById(R.id.password);
    password.setAutofillHints(View.AUTOFILL_HINT_PASSWORD);
    

Constantes de sugerencias predefinidas

Autofill Framework no valida las sugerencias; tan solo se pasan sin cambio o validación al servicio de autocompletado. Si bien puedes usar cualquier valor, las clases View y HintConstants de AndroidX contienen listas de constantes de sugerencias compatibles oficialmente.

Mediante una combinación de estas constantes, puedes compilar diseños para situaciones comunes de autocompletado:

Credenciales de la cuenta

Cuando se autocompletan las credenciales de la cuenta, un formulario de acceso puede incluir sugerencias, como AUTOFILL_HINT_USERNAME y AUTOFILL_HINT_PASSWORD.

Cuando se crea una cuenta nueva, o cuando alguien cambia su nombre de usuario y contraseña, puedes usar AUTOFILL_HINT_NEW_USERNAME y AUTOFILL_HINT_NEW_PASSWORD.

Información de tarjeta de crédito

Cuando solicites información de tarjetas de crédito, puedes usar sugerencias como AUTOFILL_HINT_CREDIT_CARD_NUMBER y AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE.

Para las fechas de vencimiento de las tarjetas de crédito, realiza una de las siguientes acciones:

Dirección física

Para autocompletar una dirección física, puedes usar sugerencias como las siguientes:

Nombres de personas

Para autocompletar nombres de personas, puedes usar sugerencias como las siguientes:

Números de teléfono

Para números de teléfono, puedes usar lo siguiente:

Contraseña de un solo uso (OTP)

Para una contraseña de un solo uso en una vista única, puedes usar AUTOFILL_HINT_SMS_OTP.

Cuando se usan varias vistas y cada una se mapea a un solo dígito de la OTP, puedes usar el método generateSmsOptHintForCharacterPosition() para generar sugerencias por carácter.

Cómo marcar campos como importantes para el autocompletado

Puedes indicarle al sistema si los campos individuales de tu app se deben incluir en una estructura de vistas para fines de autocompletado. De forma predeterminada, la vista usa el modo IMPORTANT_FOR_AUTOFILL_AUTO, que le permite a Android usar sus heurísticas para determinar si la vista es importante para el autocompletado.

Puedes configurar la importancia mediante el atributo android:importantForAutofill:

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:importantForAutofill="no" />
    

El valor de importantForAutofill puede ser cualquiera de los definidos en android:importantForAutofill:

auto
Deja que el sistema Android use su heurística para determinar si la vista es importante para el autocompletado.
no
Esta vista no es importante para el autocompletado.
noExcludeDescendants
Esta vista y sus elementos secundarios no son importantes para el autocompletado.
yes
Esta vista es importante para el autocompletado.
yesExcludeDescendants
Esta vista es importante para el autocompletado, pero sus elementos secundarios no lo son.

También puedes usar el método setImportantForAutofill():

Kotlin

    val captcha = findViewById<TextView>(R.id.captcha)
    captcha.setImportantForAutofill(View.IMPORTANT_FOR_AUTOFILL_NO)
    

Java

    TextView captcha = findViewById(R.id.captcha);
    captcha.setImportantForAutofill(View.IMPORTANT_FOR_AUTOFILL_NO);
    

Hay casos en los que una vista, una estructura de vistas o toda la actividad no son importantes para el autocompletado:

  • Por lo general, el campo CAPTCHA en una actividad de acceso no es importante para el autocompletado. En casos como ese, puedes marcar la vista como IMPORTANT_FOR_AUTOFILL_NO.
  • En una vista en la que el usuario crea contenido, como un editor de texto o de hoja de cálculo, por lo general, la estructura de la vista completa no es importante para el autocompletado. En esos casos, puedes marcar la vista como IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS para asegurarte de que todos los elementos secundarios también estén marcados como no importantes para el autocompletado.
  • En algunas actividades dentro de juegos, como las que muestran contenido, ninguna de las vistas de las actividades es importante para el autocompletado. Puedes marcar la vista raíz como IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS a fin de asegurarte de que todas las vistas de la actividad estén marcadas como no importantes para el autocompletado.

Cómo asociar datos de un sitio web y de apps para dispositivos móviles

Los servicios de autocompletado, como Autocompletar con Google, pueden compartir datos de acceso de usuarios entre navegadores y dispositivos Android después de asociar la app y un sitio web. Cuando un usuario selecciona el mismo servicio de autocompletado en ambas plataformas, el acceso a tu app web hace que sus credenciales de acceso estén disponibles para el autocompletado cuando accedan a tu app para Android correspondiente.

Para asociar tu app para Android con tu sitio web, debes alojar un vínculo de recursos digitales con la relación delegate_permission/common.get_login_creds en tu sitio. Luego, debes declarar la asociación en el archivo AndroidManifest.xml de tu app. Para obtener instrucciones detalladas sobre cómo asociar tu sitio web con tu app para Android, consulta Cómo habilitar el acceso automático en todas las apps y sitios web.

Cómo completar un flujo de trabajo de autocompletado

En esta sección, se describen situaciones específicas en las que puedes seguir algunos pasos para mejorar la funcionalidad de autocompletado de los usuarios de tu app.

Cómo determinar si está habilitada la función de autocompletado

Puedes implementar la funcionalidad de autocompletado adicional en tu app, o incluso en sus vistas particulares, si el autocompletado está disponible para el usuario. Por ejemplo, TextView muestra una entrada de autocompletado en el menú ampliado si el autocompletado está habilitado para el usuario. Para verificar si está habilitado, llama al método isEnabled() del objeto AutofillManager.

Los usuarios pueden habilitar o inhabilitar el autocompletado, así como cambiar el servicio de autocompletado, navegando a Configuración > Sistema > Idiomas y entrada > Avanzado > Ayuda con métodos de entrada > Servicio de autocompletado. Tu app no puede anular la configuración de autocompletado del usuario.

Para asegurarte de que tu experiencia de registro y acceso sea óptima para los usuarios sin autocompletado, procura implementar Smart Lock para contraseñas.

Cómo forzar una solicitud de autocompletado

A veces, es posible que tengas que forzar una solicitud de autocompletado para que ocurra en respuesta a una acción del usuario. Por ejemplo, TextView ofrece un elemento de menú de autocompletado cuando el usuario mantiene presionada la vista. En el siguiente ejemplo de código, se muestra cómo forzar una solicitud de autocompletado:

Kotlin

    fun eventHandler(view: View) {
        val afm = requireContext().getSystemService(AutofillManager::class.java)
        afm?.requestAutofill(view)
    }
    

Java

    public void eventHandler(View view) {
        AutofillManager afm = context.getSystemService(AutofillManager.class);
        if (afm != null) {
            afm.requestAutofill(view);
        }
    }
    

También puedes usar el método cancel() para cancelar el contexto de autocompletado actual. Por ejemplo, esto puede ser útil si tienes un botón que borra los campos en una página de acceso.

Cómo usar el tipo correcto de autocompletado para los datos en los controles del selector

Los selectores son útiles en algunas situaciones de autocompletado, ya que proporcionan una IU que permite que los usuarios cambien el valor de un campo que almacena datos de hora o fecha. Por ejemplo, en un formulario de tarjeta de crédito, un selector de fecha permite que los usuarios ingresen o cambien la fecha de vencimiento de su tarjeta de crédito. Sin embargo, debes usar otra vista, como EditText, para mostrar datos cuando no esté visible el selector.

De forma nativa, un objeto EditText espera autocompletar datos de tipo AUTOFILL_TYPE_TEXT. Si estás usando un tipo diferente de datos, debes crear una vista personalizada que herede contenido de EditText e implemente los métodos obligatorios para procesar el tipo de datos correspondiente. Por ejemplo, si tienes un campo de datos, implementa los métodos con una lógica que actualmente procese valores de tipo AUTOFILL_TYPE_DATE.

Cuando especificas el tipo de datos de autocompletado, el servicio de autocompletado puede crear una representación adecuada de los datos que se muestran en la vista. Para obtener más información, consulta Cómo usar los selectores con autocompletado.

Cómo finalizar el contexto de autocompletado

Autofill Framework guarda la entrada del usuario para usarla más tarde y muestra el diálogo "¿Save for autofill?" cuando finaliza el contexto de autocompletado. Por lo general, el contexto de autocompletado finaliza cuando se completa una actividad. Sin embargo, hay algunas situaciones en las que debes notificar al marco de trabajo de forma explícita; por ejemplo, si usas la misma actividad, pero diferentes fragmentos para las pantallas de acceso y de contenido. En estas situaciones especiales, puedes finalizar explícitamente el contexto llamando a AutofillManager.commit().

Compatibilidad para vistas personalizadas

Las vistas personalizadas pueden especificar los metadatos expuestos en Autofill Framework mediante la API de autocompletado. Algunas vistas actúan como un contenedor de elementos secundarios virtuales, como las vistas que contienen una IU procesada por OpenGL. Estas vistas deben usar la API para especificar la estructura de la información que se usa en la app antes de que puedan trabajar con Autofill Framework.

Si tu app usa vistas personalizadas, debes tener en cuenta las siguientes situaciones:

  • La vista personalizada proporciona una estructura de vistas estándar o predeterminada.
  • La vista personalizada tiene una estructura virtual o una estructura de vistas que no está disponible para Autofill Framework.

Vistas personalizadas con estructura de vistas estándar

Las vistas personalizadas pueden definir los metadatos que el autocompletado necesita para funcionar. Debes asegurarte de que tu vista personalizada administre los metadatos de forma correcta para trabajar con Autofill Framework. Tu vista personalizada debe realizar las siguientes acciones:

  • Procesar el valor de autocompletado que el marco de trabajo envía a tu app
  • Proporcionar el tipo y el valor de autocompletado en el marco de trabajo

Cuando se activa el autocompletado, Autofill Framework llama a autofill() en tu vista y envía el valor que se debe usar. Debes implementar autofill() para especificar la forma en que tu vista personalizada procesa el valor de autocompletado.

La vista debe especificar un tipo y valor de autocompletado anulando los métodos getAutofillType() y getAutofillValue(), respectivamente. Si agregas este código, te aseguras de que tu vista pueda proporcionar los tipos y valores de autocompletado adecuados para el marco de trabajo.

Por último, el autocompletado no debe llenar la vista si el usuario no puede proporcionar un valor para la vista en su estado actual (por ejemplo, si está inhabilitada). En esos casos, getAutofillType() debe mostrar AUTOFILL_TYPE_NONE, getAutofillValue() debe mostrar null y autofill() no debe hacer nada.

Los siguientes casos requieren pasos adicionales para funcionar correctamente en el marco de trabajo:

  • La vista personalizada se puede editar.
  • La vista personalizada contiene datos sensibles.

La vista personalizada se puede editar

Si se puede editar la vista, debes notificar a Autofill Framework acerca de los cambios llamando a notifyValueChanged() en el objeto AutofillManager.

La vista personalizada contiene datos sensibles.

Si la vista contiene información de identificación personal (PII), como direcciones de correo electrónico, números de tarjeta de crédito y contraseñas, se debe marcar como tal. En general, las vistas cuyo contenido proviene de recursos estáticos no contienen datos sensibles, pero las vistas cuyo contenido se configura de forma dinámica pueden tenerlos. Por ejemplo, una etiqueta que dice Ingresa tu nombre de usuario no contiene datos sensibles, mientras que una etiqueta que dice Hola, Juan sí lo hace. Para marcar si la vista contiene datos sensibles o no, implementa onProvideAutofillStructure() y llama a setDataIsSensitive() en el objeto ViewStructure.

En el siguiente ejemplo de código, se muestra cómo marcar los datos en la estructura de vistas como sensibles o no:

Kotlin

    override fun onProvideAutofillStructure(structure: ViewStructure, flags: Int) {
        super.onProvideAutofillStructure(structure, flags)

        // Content that comes from static resources generally isn't sensitive.
        val sensitive = !contentIsSetFromResources()
        structure.setDataIsSensitive(sensitive)
    }
    

Java

    @Override
    public void onProvideAutofillStructure(ViewStructure structure, int flags) {
        super.onProvideAutofillStructure(structure, flags);

        // Content that comes from static resources generally isn't sensitive.
        boolean sensitive = !contentIsSetFromResources();
        structure.setDataIsSensitive(sensitive);
    }
    

Si la vista solo acepta valores predefinidos, puedes usar el método setAutofillOptions() a fin de configurar las opciones que se pueden usar para autocompletar la vista. En particular, las vistas cuyo tipo de autocompletado es AUTOFILL_TYPE_LIST deben usar este método, ya que el servicio de autocompletado funciona mejor si conoce las opciones disponibles para llenar la vista.

Las vistas que usan un adaptador, como un Spinner, son un caso similar. Por ejemplo, un ícono giratorio que proporciona años creados de forma dinámica (según el año actual) para usar en campos de vencimiento de tarjetas de crédito puede implementar el método getAutofillOptions() de la interfaz Adapter para ofrecer una lista de años.

Las vistas que usan un ArrayAdapter también pueden proporcionar listas de valores. ArrayAdapter configura automáticamente las opciones de autocompletado para los recursos estáticos. Sin embargo, si proporcionas los valores de forma dinámica, debes anular getAutofillOptions().

Vistas personalizadas con estructura virtual

Autofill Framework requiere una estructura de vistas para poder editar y guardar la información en la IU de tu app. En algunas situaciones, la estructura de vistas no está disponible para el marco de trabajo:

  • La app usa un motor de procesamiento de bajo nivel, como OpenGL, para procesar la IU.
  • La app usa una instancia de Canvas para dibujar la IU.

En esos casos, puedes especificar una estructura de vistas implementado onProvideAutofillVirtualStructure() y siguiendo estos pasos:

  1. Aumenta el recuento de elementos secundarios de la estructura de vistas llamando a addChildCount().
  2. Para agregar un elemento secundario, llama a newChild().
  3. Configura el ID de autocompletado para el elemento secundario llamando a setAutofillId().
  4. Configura las propiedades relevantes, como el valor y el tipo de autocompletado.
  5. Si los datos del elemento secundario virtual son sensibles, debes pasar true a setDataIsSensitive() o false.

En el siguiente fragmento de código, se muestra cómo crear un nuevo elemento secundario en la estructura virtual:

Kotlin

    override fun onProvideAutofillVirtualStructure(structure: ViewStructure, flags: Int) {

        super.onProvideAutofillVirtualStructure(structure, flags)

        // Create a new child in the virtual structure.
        structure.addChildCount(1)
        val child = structure.newChild(childIndex)

        // Set the autofill ID for the child.
        child.setAutofillId(structure.autofillId!!, childVirtualId)

        // Populate the child by providing properties such as value and type.
        child.setAutofillValue(childAutofillValue)
        child.setAutofillType(childAutofillType)

        // Some children can provide a list of values. For example, if the child is
        // a spinner.
        val childAutofillOptions = arrayOf<CharSequence>("option1", "option2")
        child.setAutofillOptions(childAutofillOptions)

        // Just like other types of views, mark the data as sensitive, if
        // appropriate.
        val sensitive = !contentIsSetFromResources()
        child.setDataIsSensitive(sensitive)
    }
    

Java

    @Override
    public void onProvideAutofillVirtualStructure(ViewStructure structure, int flags) {

        super.onProvideAutofillVirtualStructure(structure, flags);

        // Create a new child in the virtual structure.
        structure.addChildCount(1);
        ViewStructure child =
                structure.newChild(childIndex);

        // Set the autofill ID for the child.
        child.setAutofillId(structure.getAutofillId(), childVirtualId);

        // Populate the child by providing properties such as value and type.
        child.setAutofillValue(childAutofillValue);
        child.setAutofillType(childAutofillType);

        // Some children can provide a list of values. For example, if the child is
        // a spinner.
        CharSequence childAutofillOptions[] = { "option1", "option2" };
        child.setAutofillOptions(childAutofillOptions);

        // Just like other types of views, mark the data as sensitive, if
        // appropriate.
        boolean sensitive = !contentIsSetFromResources();
        child.setDataIsSensitive(sensitive);
    }
    

Cuando cambian los elementos de una estructura virtual, debes notificar al marco mediante la realización de las siguientes tareas:

  • Si cambia el foco de los elementos secundarios, llama a notifyViewEntered() y notifyViewExited() en el objeto AutofillManager.
  • Si cambia el valor de un elemento secundario, llama a notifyValueChanged() en el objeto AutofillManager.
  • Si la jerarquía de vistas ya no está disponible porque el usuario completó un paso del flujo de trabajo (por ejemplo, accedió mediante un formulario de acceso), llama a commit() en el objeto AutofillManager.
  • Si la jerarquía de vistas ya no es válida porque el usuario canceló un paso del flujo de trabajo (por ejemplo, hizo clic en un botón que borra un formulario de acceso), llama a cancel() en el objeto AutofillManager.

Cómo usar devoluciones de llamada en eventos de autocompletado

Si tu app proporciona sus propias vistas de autocompletado, necesitas un mecanismo que le indique que habilite o inhabilite las vistas en respuesta a los cambios en la prestación de autocompletado de la IU. Autofill Framework proporciona este mecanismo en la forma de AutofillCallback.

Esta clase proporciona el método onAutofillEvent(View, int), al que la app llama después de un cambio en el estado de autocompletado asociado a una vista. También hay una versión sobrecargada de este método, que incluye un parámetro childId que tu app puede usar con vistas virtuales. Los estados disponibles se definen como constantes en la devolución de llamada.

Puedes registrar una devolución de llamada con un método registerCallback() de la clase AutofillManager. En el siguiente ejemplo de código, se muestra cómo declarar una devolución de llamada para eventos de autocompletado:

Kotlin

    val afm = context.getSystemService(AutofillManager::class.java)

    afm?.registerCallback(object : AutofillManager.AutofillCallback() {
        // For virtual structures, override
        // onAutofillEvent(View view, int childId, int event) instead.
        override fun onAutofillEvent(view: View, event: Int) {
            super.onAutofillEvent(view, event)
            when (event) {
                EVENT_INPUT_HIDDEN -> {
                    // The autofill affordance associated with the view was hidden.
                }
                EVENT_INPUT_SHOWN -> {
                    // The autofill affordance associated with the view was shown.
                }
                EVENT_INPUT_UNAVAILABLE -> {
                    // Autofill isn't available.
                }
            }

        }
    })
    

Java

    AutofillManager afm = getContext().getSystemService(AutofillManager.class);

    afm.registerCallback(new AutofillManager.AutofillCallback() {
        // For virtual structures, override
        // onAutofillEvent(View view, int childId, int event) instead.
        @Override
        public void onAutofillEvent(@NonNull View view, int event) {
            super.onAutofillEvent(view, event);
            switch (event) {
                case EVENT_INPUT_HIDDEN:
                    // The autofill affordance associated with the view was hidden.
                    break;
                case EVENT_INPUT_SHOWN:
                    // The autofill affordance associated with the view was shown.
                    break;
                case EVENT_INPUT_UNAVAILABLE:
                    // Autofill isn't available.
                    break;
            }
        }
    });
    

Cuando llegue el momento de quitar la devolución de llamada, usa el método unregisterCallback().

Cómo personalizar el elemento de diseño destacado del autocompletado

Cuando se autocompleta una vista, la plataforma dibuja un Drawable sobre la vista para indicar que se autocompletó su contenido. De forma predeterminada, el diseño destacado es un rectángulo sólido con un color translúcido apenas más oscuro que el color del tema que se usa para dibujar fondos. No es necesario cambiar el elemento de diseño, pero se puede personalizar mediante la anulación del elemento android:autofilledHighlight del tema que usa la aplicación o la actividad, como se muestra en este ejemplo:

res/values/styles.xml

<resources>
        <style name="MyAutofilledHighlight" parent="...">
            <item name="android:autofilledHighlight">@drawable/my_drawable</item>
        </style>
    </resources>
    

res/drawable/my_drawable.xml

<shape xmlns:android="http://schemas.android.com/apk/res/android">
        <solid android:color="#4DFF0000" />
    </shape>
    

AndroidManifest.xml

<application ...
        android:theme="@style/MyAutofilledHighlight">
    <!-- or -->
    <activity ...
        android:theme="@style/MyAutofilledHighlight">
    

Cómo autenticar para el autocompletado

Un servicio de autocompletado puede requerir que el usuario se autentique antes de que el servicio pueda completar campos en tu app, en cuyo caso el sistema Android inicia la actividad de autenticación del servicio como parte de la pila de tu actividad.

No necesitas actualizar tu app para admitir la autenticación, porque esta ocurre dentro del servicio. Sin embargo, debes asegurarte de que la estructura de vistas de la actividad se conserve cuando se reinicie la actividad (por ejemplo, creando la estructura de vistas en onCreate(), no en onStart() o onResume()).

Puedes verificar cómo se comporta tu app cuando un servicio de autocompletado requiere autenticación. Para ello, utiliza HeuristicsService del ejemplo AutofillFramework y configúralo para requerir autenticación de respuesta de completado. También puedes usar la muestra BadViewStructureCreationSignInActivity para emular esta cuestión.

Cómo asignar ID de autocompletado a vistas recicladas

Los contenedores que reciclan vistas, como la clase RecyclerView, son muy útiles para las apps que necesitan mostrar listas de desplazamiento de elementos basadas en grandes conjuntos de datos. A medida que el contenedor se desplaza, el sistema reutiliza las vistas del diseño, pero las vistas tienen contenido nuevo. Si se completa el contenido inicial de la vista, el servicio de autocompletado retiene el significado lógico de las vistas utilizando sus ID de autocompletado. Surge un problema cuando, a medida que el sistema reutiliza las vistas del diseño, los ID lógicos de las vistas siguen siendo iguales, lo que provoca que los datos de autocompletado incorrectos del usuario se asocien con un ID de autocompletado.

Para solucionar este problema en dispositivos que ejecutan Android 9 (API nivel 28) y versiones posteriores, puedes administrar explícitamente el ID de autocompletado de las vistas que usa RecyclerView con estos nuevos métodos:

  • El método getNextAutofillId() obtiene una nueva ID de autocompletado que es exclusiva de la actividad.
  • El método setAutofillId() configura el ID de autocompletado único y lógico de esta vista en la actividad.

Cómo abordar problemas conocidos

En esta sección, se presentan soluciones alternativas a problemas conocidos dentro de Autofill Framework.

Los diálogos que cambiaron de tamaño no se tienen en cuenta para el autocompletado

En Android 8.1 (API nivel 27) y versiones anteriores, si una vista en un diálogo cambia de tamaño después de mostrarse, no se considera para autocompletado. Esas vistas no están incluidas en el objeto AssistStructure que el sistema Android envía al servicio de autocompletado. Como resultado, el servicio no puede completar las vistas.

Para evitar este problema, reemplaza la propiedad token de los parámetros de la ventana de diálogo con la propiedad token de la actividad que crea el diálogo. Después de validar la habilitación del autocompletado, guarda los parámetros de la ventana en el método onWindowAttributesChanged() de la clase que hereda de Dialog. Luego, reemplaza la propiedad token de los parámetros guardados con la propiedad token de la actividad superior en el método onAttachedToWindow().

En el siguiente fragmento de código, se muestra una clase que implementa la solución alternativa:

Kotlin

    class MyDialog(context: Context) : Dialog(context) {

        // Used to store the dialog window parameters.
        private var token: IBinder? = null

        private val isDialogResizedWorkaroundRequired: Boolean
            get() {
                if (Build.VERSION.SDK_INT != Build.VERSION_CODES.O || Build.VERSION.SDK_INT != Build.VERSION_CODES.O_MR1) {
                    return false
                }
                val autofillManager = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                    context.getSystemService(AutofillManager::class.java)
                } else {
                    null
                }
                return autofillManager?.isEnabled ?: false
            }

        override fun onWindowAttributesChanged(params: WindowManager.LayoutParams) {
            if (params.token == null && token != null) {
                params.token = token
            }

            super.onWindowAttributesChanged(params)
        }

        override fun onAttachedToWindow() {
            if (isDialogResizedWorkaroundRequired) {
                token = ownerActivity!!.window.attributes.token
            }

            super.onAttachedToWindow()
        }

    }
    

Java

    public class MyDialog extends Dialog {

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

        // Used to store the dialog window parameters.
        private IBinder token;

        @Override
        public void onWindowAttributesChanged(WindowManager.LayoutParams params) {
            if (params.token == null && token != null) {
                params.token = token;
            }

            super.onWindowAttributesChanged(params);
        }

        @Override
        public void onAttachedToWindow() {
            if (isDialogResizedWorkaroundRequired()) {
                token = getOwnerActivity().getWindow().getAttributes().token;
            }

            super.onAttachedToWindow();
        }

        private boolean isDialogResizedWorkaroundRequired() {
            if (Build.VERSION.SDK_INT != Build.VERSION_CODES.O
                    || Build.VERSION.SDK_INT != Build.VERSION_CODES.O_MR1) {
                return false;
            }
            AutofillManager autofillManager =
                    null;
            if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
                autofillManager = getContext().getSystemService(AutofillManager.class);
            }
            return autofillManager != null && autofillManager.isEnabled();
        }

    }
    

Para evitar operaciones innecesarias, el siguiente fragmento de código muestra cómo verificar si el autocompletado es compatible con el dispositivo y si está habilitado para el usuario actual, además de si se requiere esta solución alternativa:

Kotlin

    // AutofillExtensions.kt

    fun Context.isDialogResizedWorkaroundRequired(): Boolean {
        // After the issue is resolved on Android, you should check if the
        // workaround is still required for the current device.
        return isAutofillAvailable()
    }

    fun Context.isAutofillAvailable(): Boolean {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
            // The autofill framework is only available on Android 8.0
            // or higher.
            return false
        }

        val afm = getSystemService(AutofillManager::class.java)
        // Return true if autofill is supported by the device and enabled
        // for the current user.
        return afm != null && afm.isEnabled
    }
    

Java

    public class AutofillHelper {

        public static boolean isDialogResizedWorkaroundRequired(Context context) {
            // After the issue is resolved on Android, you should check if the
            // workaround is still required for the current device.
            return isAutofillAvailable(context);
        }

        public static boolean isAutofillAvailable(Context context) {
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
                // The autofill framework is only available on Android 8.0
                // or higher.
                return false;
            }

            AutofillManager afm = context.getSystemService(AutofillManager.class);
            // Return true if autofill is supported by the device and enabled
            // for the current user.
            return afm != null && afm.isEnabled();
        }
    }
    

Cómo probar tu app con autocompletado

La mayoría de las apps funcionan con servicios de autocompletado sin ningún cambio. Sin embargo, puedes optimizar tu app para garantizar que funcione de la mejor manera posible con esos servicios. Después de optimizar tu app, debes probarla para asegurarte de que funcione según lo previsto con los servicios de autocompletado.

Para poder probar tu app, debes usar un emulador o un dispositivo físico que ejecute Android 8.0 (API nivel 26) o versiones posteriores. Para obtener más información sobre cómo crear un emulador, consulta Cómo crear y administrar dispositivos virtuales.

Cómo instalar un servicio de autocompletado

Antes de poder probar tu app con autocompletado, debes instalar otra app que ofrezca esos servicios. Puede usar una app de terceros para este propósito, pero es más fácil usar un servicio de autocompletado de muestra para que no tengas que registrarte en ningún servicio de terceros.

Puedes usar la muestra Android Autofill Framework para probar tu app con los servicios de autocompletado. La app de muestra proporciona un servicio de autocompletado y clases Activity de cliente que puedes usar para probar el flujo de trabajo antes de usarlo con tu app. En esta página, se hace referencia a la app de muestra android-AutofillFramework.

Después de instalar la app, debes habilitar el servicio de autocompletado en la configuración del sistema. Para habilitar el servicio, navega a Configuración> Sistema> Idiomas y entrada> Avanzado> Ayuda con métodos de entrada> Servicio de autocompletado.

Cómo analizar requisitos de datos

Para probar tu app con el servicio de autocompletado, este debe tener datos que se puedan usar para tal fin. El servicio también debe comprender qué tipo de datos se esperan en las vistas de tu app. Por ejemplo, si la aplicación tiene una vista que espera un nombre de usuario, el servicio debe tener un conjunto de datos que contenga un nombre de usuario y algún mecanismo para saber que la vista espera dichos datos.

Debes permitir que el servicio conozca qué tipo de datos se esperan en tus vistas mediante la configuración del atributo android:autofillHints. Algunos servicios usan heurísticas sofisticadas para determinar el tipo de datos, pero otros, como la app de muestra, confían en el desarrollador para proporcionar esta información. Tu app funciona mejor con los servicios de autocompletado si configuras el atributo android:autofillHints en las vistas que son relevantes para ello.

Cómo realizar la prueba

Después de analizar los requisitos de datos, puedes realizar tu prueba, que incluye guardar los datos de la prueba en el servicio de autocompletado y activar el autocompletado en tu app.

Cómo guardar datos en el servicio

Los siguientes pasos muestran cómo guardar datos en el servicio de autocompletado actualmente activo:

  1. Abre una app que contenga una vista que espere el tipo de datos que quieres usar durante tu prueba. La app de muestra android-AutofillFramework proporciona a la IU vistas que esperan varios tipos de datos, como números de tarjeta de crédito y nombres de usuario.
  2. Presiona la vista que tiene el tipo de datos que necesitas.
  3. Ingresa un valor en la vista.
  4. Presiona el botón de confirmación, como Acceder o Enviar.

    Por lo general, debes enviar el formulario antes de que el servicio intente guardar los datos.

  5. El sistema muestra un diálogo en el que solicita tu permiso para guardar los datos. El diálogo muestra el nombre del servicio que está activo actualmente.

    Verifica que sea el servicio que quieres utilizar en tu prueba y presiona Guardar.

Si Android no muestra el diálogo de permisos o si el servicio no es el que quieres usar en tu prueba, comprueba que el servicio esté activo en la configuración del sistema.

Cómo activar el autocompletado en tu app

Los siguientes pasos te muestran cómo activar el autocompletado en tu app:

  1. Abre la app y busca la actividad que tenga las vistas que quieras probar.
  2. Presiona la vista que se debe completar.
  3. El sistema debe mostrar la IU de autocompletado, que contiene los conjuntos de datos que pueden llenar la vista, como se muestra en la Figura 1.
  4. Presiona el conjunto de datos que contenga los datos que quieras usar. La vista debe mostrar los datos almacenados previamente en el servicio.
IU de autocompletado que muestra
Figura 1: IU de autocompletado que muestra los conjuntos de datos disponibles

Si Android no muestra la IU de autocompletado, puedes probar las siguientes opciones para solucionar problemas:

  • Comprueba que las vistas de tu app utilicen el valor correcto en el atributo android:autofillHints. Para obtener una lista de posibles valores para el atributo, consulta las constantes con el prefijo AUTOFILL_HINT en la clase View.
  • Comprueba que el atributo android:importantForAutofill esté configurado en un valor diferente a no en la vista que se debe completar, o configura un valor diferente a noExcludeDescendants en la vista o uno de sus elementos superiores.