Arquitectura de la información

Android 8.0 introdujo una nueva arquitectura de la información para la app de Configuración con el objetivo de simplificar la forma en que se organiza la configuración y que a los usuarios les resulte más fácil encontrar con rapidez la configuración que necesitan para personalizar sus dispositivos Android. Android 9 introdujo algunas mejoras para proporcionar más funciones de configuración y facilitar la implementación.

Ejemplos y fuente

Actualmente, la mayoría de las páginas de Configuración se implementan con el nuevo framework. Un buen ejemplo es DisplaySettings: packages/apps/Settings/src/com/android/settings/DisplaySettings.java

A continuación, se indican las rutas de acceso de los archivos para los componentes importantes:

  • CategoryKey: packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java
  • DashboardFragmentRegistry: packages/apps/Settings/src/com/android/settings/dashboard/DashboardFragmentRegistry.java
  • DashboardFragment: packages/apps/Settings/src/com/android/settings/dashboard/DashboardFragment.java
  • AbstractPreferenceController: frameworks/base/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java
  • BasePreferenceController (introducido en Android 9): packages/apps/Settings/src/com/android/settings/core/BasePreferenceController.java

Implementación

Se recomienda a los fabricantes de dispositivos que adapten la arquitectura de información de Configuración existente y que inserten páginas de configuración adicionales según sea necesario para admitir funciones específicas del socio. Mover las preferencias de una página heredada (implementada como SettingsPreferencePage) a una página nueva (implementada con DashboardFragment) puede ser complicado. Es probable que la preferencia de la página heredada no se implemente con un PreferenceController.

Por lo tanto, cuando muevas una preferencia de una página heredada a una nueva, debes crear un PreferenceController y mover el código al controlador antes de instanciarlo en el nuevo DashboardFragment. Las APIs que requiere PreferenceController se describen en su nombre y se documentan en Javadoc.

Se recomienda agregar una prueba de unidades para cada PreferenceController. Si el cambio se envía a AOSP, se requiere una prueba de unidades. Para obtener más información sobre cómo escribir pruebas basadas en Robolectric, consulta el archivo readme packages/apps/Settings/tests/robotests/README.md.

Arquitectura de la información de estilo de complemento

Cada elemento de configuración se implementa como una Preference. Una preferencia se puede mover fácilmente de una página a otra.

Para facilitar el desplazamiento de varios parámetros de configuración, Android 8.0 introdujo un fragmento host de estilo de complemento que contiene elementos de configuración. Los elementos de configuración se modelan como controladores de estilo de complemento. Por lo tanto, una página de configuración se construye con un solo fragmento de host y varios controladores de configuración.

DashboardFragment

DashboardFragment es el host de los controladores de preferencias de estilo de complemento. El fragmento hereda de PreferenceFragment y tiene hooks para expandir y actualizar tanto las listas de preferencias estáticas como las dinámicas.

Preferencias estáticas

Una lista de preferencias estática se define en XML con la etiqueta <Preference>. Una implementación de DashboardFragment usa el método getPreferenceScreenResId() para definir qué archivo XML contiene la lista estática de preferencias que se mostrará.

Preferencias dinámicas

Un elemento dinámico representa una tarjeta con intención, que conduce a una actividad externa o interna. Por lo general, la intención lleva a una página de configuración diferente. Por ejemplo, el elemento de configuración "Google" en la página principal de Configuración es un elemento dinámico. Los elementos dinámicos se definen en AndroidManifest (se analiza a continuación) y se cargan a través de un FeatureProvider (definido como DashboardFeatureProvider).

Los parámetros de configuración dinámicos son más pesados que los configurados de forma estática, por lo que, normalmente, los desarrolladores deberían implementar el parámetro de configuración como estático. Sin embargo, el parámetro de configuración dinámico puede ser útil cuando se cumple alguna de las siguientes condiciones:

  • El parámetro de configuración no se implementa directamente en la app de Configuración (por ejemplo, no se inyecta un parámetro de configuración implementado por apps del OEM o del operador).
  • El parámetro de configuración debería aparecer en la página principal de Configuración.
  • Ya tienes una Activity para el parámetro de configuración y no quieres implementar la configuración estática adicional.

Para configurar una actividad como un parámetro dinámico, haz lo siguiente:

  • Marca la actividad como un parámetro de configuración dinámico agregando un intent-filter a la actividad.
  • Indicarle a la app de Configuración a qué categoría pertenece La categoría es una constante definida en CategoryKey.
  • Opcional: Agrega un texto de resumen cuando se muestre el parámetro de configuración.

Este es un ejemplo tomado de la app de Configuración para DisplaySettings.

<activity android:name="Settings$DisplaySettingsActivity"
                   android:label="@string/display_settings"
                   android:icon="@drawable/ic_settings_display">
             <!-- Mark the activity as a dynamic setting -->
              <intent-filter>
                     <action android:name="com.android.settings.action.IA_SETTINGS" />
              </intent-filter>
             <!-- Tell Settings app which category it belongs to -->
              <meta-data android:name="com.android.settings.category"
                     android:value="com.android.settings.category.ia.homepage" />
             <!-- Add a summary text when the setting is displayed -->
              <meta-data android:name="com.android.settings.summary"
                     android:resource="@string/display_dashboard_summary"/>
             </activity>

En el momento de la renderización, el fragmento solicitará una lista de preferencias de la configuración estática en XML y de la configuración dinámica definida en AndroidManifest. Ya sea que los PreferenceController se definan en código Java o en XML, DashboardFragment administra la lógica de control de cada parámetro de configuración a través de PreferenceController (se explica a continuación). Luego, se muestran en la IU como una lista mixta.

PreferenceController

Existen diferencias entre la implementación de PreferenceController en Android 9 y Android 8.x, como se describe en esta sección.

PreferenceController en la versión de Android 9

Un PreferenceController contiene toda la lógica para interactuar con la preferencia, lo que incluye la visualización, la actualización, la indexación de búsqueda, etcétera.

La interfaz de PreferenceController se define como BasePreferenceController. Por ejemplo, consulta el código en packages/apps/Settings/src/com/android/settings/core/ BasePreferenceController.java

Existen varias subclases de BasePreferenceController, cada una asignada a un estilo de IU específico que la app de Configuración admite de forma predeterminada. Por ejemplo, TogglePreferenceController tiene una API que se asigna directamente a la forma en que el usuario debe interactuar con una IU de preferencias basada en botones de activación.

BasePreferenceController tiene APIs como getAvailabilityStatus(), displayPreference(), handlePreferenceTreeClicked(),, etc. La documentación detallada de cada API se encuentra en la clase de interfaz.

Una restricción para implementar BasePreferenceController (y sus subclases, como TogglePreferenceController) es que la firma del constructor debe coincidir con una de las siguientes opciones:

  • public MyController(Context context, String key) {}
  • public MyController(Context context) {}

Mientras se instala una preferencia en el fragmento, el panel proporciona un método para adjuntar un PreferenceController antes del tiempo de visualización. En el momento de la instalación, el controlador se conecta al fragmento para que todos los eventos relevantes futuros se envíen al controlador.

DashboardFragment mantiene una lista de PreferenceController en la pantalla. En el onCreate() del fragmento, se invocan todos los controladores para el método getAvailabilityStatus() y, si devuelve verdadero, se invoca displayPreference() para procesar la lógica de visualización. getAvailabilityStatus() también es importante para indicarle al framework de Settings qué elementos están disponibles durante la búsqueda.

PreferenceController en versiones de Android 8.x

Un PreferenceController contiene toda la lógica para interactuar con la preferencia, incluida la visualización, la actualización, la indexación de búsqueda, etcétera.

En correspondencia con las interacciones de preferencias, la interfaz de PreferenceController tiene las APIs isAvailable(), displayPreference(), handlePreferenceTreeClicked(), etcétera. En la clase de interfaz, se puede encontrar documentación detallada sobre cada API.

Mientras se instala una preferencia en el fragmento, el panel proporciona un método para adjuntar un PreferenceController antes del tiempo de visualización. En el momento de la instalación, el controlador se conecta al fragmento para que todos los eventos relevantes futuros se envíen al controlador.

DashboardFragment mantiene una lista de PreferenceControllers en la pantalla. En el onCreate() del fragmento, se invocan todos los controladores para el método isAvailable() y, si devuelve verdadero, se invoca displayPreference() para procesar la lógica de visualización.

Usa DashboardFragment

Cómo mover una preferencia de la página A a la B

Si la preferencia se incluye de forma estática en el archivo XML de preferencias de la página original, sigue el procedimiento de movimiento Estático para tu versión de Android que se indica a continuación. De lo contrario, sigue el procedimiento de movimiento dinámico para tu versión de Android.

Movimiento estático en Android 9

  1. Busca los archivos XML de preferencias de la página original y la página de destino. Puedes encontrar esta información en el método getPreferenceScreenResId() de la página.
  2. Quita la preferencia del XML de la página original.
  3. Agrega la preferencia al XML de la página de destino.
  4. Quita el PreferenceController de esta preferencia de la implementación en Java de la página original. Por lo general, se encuentra en createPreferenceControllers(). El controlador se puede declarar directamente en XML.

    Nota: Es posible que la preferencia no tenga un PreferenceController.

  5. Crea una instancia de PreferenceController en createPreferenceControllers() de la página de destino. Si el PreferenceController se define en XML en la página anterior, también se debe definir en XML para la página nueva.

Movimiento dinámico en Android 9

  1. Busca en qué categoría se encuentran la página original y la de destino. Puedes encontrar esta información en DashboardFragmentRegistry.
  2. Abre el archivo AndroidManifest.xml que contiene el parámetro de configuración que necesitas mover y busca la entrada de Activity que representa este parámetro.
  3. Establece el valor de los metadatos de la actividad para com.android.settings.category en la clave de categoría de la página nueva.

Movimiento estático en versiones de Android 8.x

  1. Busca los archivos XML de preferencias de la página original y la página de destino.
  2. Puedes encontrar esta información en el método getPreferenceScreenResId() de la página.
  3. Quita la preferencia en el XML de la página original.
  4. Agrega la preferencia al XML de la página de destino.
  5. Quita el PreferenceController para esta preferencia en la implementación de Java de la página original. Por lo general, está en getPreferenceControllers().
  6. Nota: Es posible que la preferencia no tenga un PreferenceController.

  7. Crea una instancia de PreferenceController en getPreferenceControllers() de la página de destino.

Movimiento dinámico en versiones de Android 8.x

  1. Busca en qué categoría se encuentran la página original y la de destino. Puedes encontrar esta información en DashboardFragmentRegistry.
  2. Abre el archivo AndroidManifest.xml que contiene el parámetro de configuración que necesitas mover y busca la entrada de Activity que representa este parámetro.
  3. Cambia el valor de metadatos de la actividad para com.android.settings.category y establece el punto de valor en la clave de categoría de la página nueva.

Crea una preferencia nueva en una página

Si la preferencia se incluye de forma estática en el archivo XML de preferencias de la página original, sigue el procedimiento estático que se indica a continuación. De lo contrario, sigue el procedimiento dinámico.

Cómo crear una preferencia estática

  1. Busca los archivos XML de preferencias de la página. Puedes encontrar esta información en el método getPreferenceScreenResId() de la página.
  2. Agrega un nuevo elemento Preference en el XML. Asegúrate de que tenga un android:key único.
  3. Define un PreferenceController para esta preferencia en el método getPreferenceControllers() de la página.
    • En Android 8.x y, de manera opcional, en Android 9, crea una instancia de PreferenceController para esta preferencia en el método createPreferenceControllers() de la página.

      Si esta preferencia ya existía en otros lugares, es posible que ya haya un PreferenceController para ella. Puedes reutilizar el PreferenceController sin compilar uno nuevo.

    • A partir de Android 9, puedes declarar el PreferenceController en XML junto a la preferencia. Por ejemplo:
      <Preference
              android:key="reset_dashboard"
              android:title="@string/reset_dashboard_title"
              settings:controller="com.android.settings.system.ResetPreferenceController"/>

Crea una preferencia dinámica

  1. Busca en qué categoría se encuentran la página original y la de destino. Puedes encontrar esta información en DashboardFragmentRegistry.
  2. Crea una actividad nueva en AndroidManifest
  3. Agrega los metadatos necesarios a la nueva actividad para definir el parámetro de configuración. Establece el valor de metadatos para com.android.settings.category en el mismo valor definido en el paso 1.

Crear página nueva

  1. Crea un fragmento nuevo que herede de DashboardFragment.
  2. Define su categoría en DashboardFragmentRegistry.

    Nota: Este paso es opcional. Si no necesitas ninguna preferencia dinámica en esta página, no es necesario que proporciones una clave de categoría.

  3. Sigue los pasos para agregar la configuración necesaria para esta página. Para obtener más información, consulta la sección Implementación.

Validación

  • Ejecuta las pruebas de Robolectric en Configuración. Todas las pruebas existentes y nuevas deberían aprobarse.
  • Compila e instala Configuración y, luego, abre manualmente la página que se está modificando. La página debería actualizarse de inmediato.