Arquitectura informacional

Android 8.0 introdujo una nueva arquitectura de información para la aplicación Configuración para simplificar la forma en que se organizan las configuraciones y facilitar a los usuarios encontrar rápidamente configuraciones para personalizar sus dispositivos Android. Android 9 introdujo algunas mejoras para proporcionar más funciones de configuración y una implementación más sencilla.

Ejemplos y fuente

La mayoría de las páginas de Configuración se implementan actualmente utilizando el nuevo marco. Un buen ejemplo es DisplaySettings: packages/apps/Settings/src/com/android/settings/DisplaySettings.java

Las rutas de archivos para componentes importantes se enumeran a continuación:

  • Clave de categoría : 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 anima a los fabricantes de dispositivos a adaptar la arquitectura de información de configuración existente e insertar páginas de configuración adicionales según sea necesario para adaptarse a funciones específicas de los socios. Mover las preferencias de una página heredada (implementada como SettingsPreferencePage ) a una nueva página (implementada usando DashboardFragment ) puede resultar complicado. Es probable que la preferencia de la página heredada no se implemente con PreferenceController .

Entonces, al mover preferencias de una página heredada a una página nueva, debe crear un PreferenceController y mover el código al controlador antes de crear una instancia en el nuevo DashboardFragment . Las API que requiere PreferenceController se describen en su nombre y están documentadas en Javadoc.

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

Arquitectura de información estilo complemento

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

Para facilitar el movimiento de múltiples configuraciones, Android 8.0 introdujo un fragmento de host estilo complemento que contiene elementos de configuración. Los elementos de configuración se modelan como controladores estilo complemento. Por lo tanto, una página de configuración se construye mediante un único fragmento de host y múltiples controladores de configuración.

Fragmento del tablero

DashboardFragment es el anfitrión de controladores de preferencias estilo complemento. El fragmento hereda de PreferenceFragment y tiene enlaces para expandir y actualizar tanto las listas de preferencias estáticas como las listas de preferencias dinámicas.

Preferencias estáticas

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

Preferencias dinámicas

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

Las configuraciones dinámicas son más pesadas que las configuraciones configuradas estáticamente, por lo que normalmente los desarrolladores deberían implementar la configuración como estática. Sin embargo, la configuración dinámica puede resultar útil cuando se cumple alguna de las siguientes condiciones:

  • La configuración no se implementa directamente en la aplicación Configuración (como inyectar una configuración implementada por aplicaciones OEM/Operador).
  • La configuración debería aparecer en la página de inicio de Configuración.
  • Ya tiene una Actividad para la configuración y no desea implementar la configuración estática adicional.

Para configurar una Actividad como una configuración dinámica, haga lo siguiente:

  • Marque la actividad como una configuración dinámica agregando un filtro de intención a la actividad.
  • Dígale a la aplicación Configuración a qué categoría pertenece. La categoría es una constante, definida en CategoryKey .
  • Opcional: agregue texto de resumen cuando se muestre la configuración.

Aquí hay un ejemplo tomado de la aplicación 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 tanto de XML estático como de configuraciones dinámicas definidas en AndroidManifest . Ya sea que los PreferenceController estén definidos en código Java o en XML, DashboardFragment administra la lógica de manejo de cada configuración a través de PreferenceController (que se analiza a continuación). Luego se muestran en la interfaz de usuario como una lista mixta.

Controlador de preferencias

Existen diferencias entre implementar 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, incluida la visualización, actualización, indexación de búsqueda, etc.

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

Hay varias subclases de BasePreferenceController , cada una de las cuales se asigna a un estilo de interfaz de usuario específico que la aplicación Configuración admite de forma predeterminada. Por ejemplo, TogglePreferenceController tiene una API que asigna directamente cómo el usuario debe interactuar con una interfaz de usuario de preferencias basada en alternancia.

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

Una restricción en la implementación BasePreferenceController (y sus subclases como TogglePreferenceController ) es que la firma del constructor debe coincidir con cualquiera de los siguientes:

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

Al instalar una preferencia en el fragmento, el panel proporciona un método para adjuntar un PreferenceController antes del momento 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 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 marco de configuración qué elementos están disponibles durante la búsqueda.

PreferenceController en las versiones de Android 8.x

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

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

Al instalar una preferencia en el fragmento, el panel proporciona un método para adjuntar un PreferenceController antes del momento 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 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.

Usando DashboardFragment

Mover una preferencia de la página A a la B

Si la preferencia aparece estáticamente en el archivo XML de preferencias de la página original, siga el procedimiento de movimiento estático para su versión de Android a continuación. De lo contrario, siga el procedimiento de movimiento dinámico para su versión de Android.

Movimiento estático en Android 9

  1. Busque los archivos XML de preferencias para la página original y la página de destino. Puede encontrar esta información en el método getPreferenceScreenResId() de la página.
  2. Elimine la preferencia del XML de la página original.
  3. Agregue la preferencia al XML de la página de destino.
  4. Elimine el PreferenceController para esta preferencia de la implementación Java de la página original. Generalmente está en createPreferenceControllers() . El controlador podría declararse directamente en XML.

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

  5. Cree una instancia de PreferenceController en createPreferenceControllers() de la página de destino. Si PreferenceController está definido en XML en la página anterior, defínalo también en XML para la nueva página.

Movimiento dinámico en Android 9

  1. Encuentre qué categoría alberga la página original y la de destino. Puede encontrar esta información en DashboardFragmentRegistry .
  2. Abra el archivo AndroidManifest.xml que contiene la configuración que necesita mover y busque la entrada Actividad que representa esta configuración.
  3. Establezca el valor de metadatos de la actividad para com.android.settings.category en la clave de categoría de la nueva página.

Movimiento estático en las versiones de Android 8.x

  1. Busque los archivos XML de preferencias para la página original y la página de destino.
  2. Puede encontrar esta información en el método getPreferenceScreenResId() de la página.
  3. Elimine la preferencia en el XML de la página original.
  4. Agregue la preferencia al XML de la página de destino.
  5. Elimine 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. Cree una instancia de PreferenceController en getPreferenceControllers() de la página de destino.

Movimiento dinámico en las versiones de Android 8.x

  1. Encuentre qué categoría alberga la página original y la de destino. Puede encontrar esta información en DashboardFragmentRegistry .
  2. Abra el archivo AndroidManifest.xml que contiene la configuración que necesita mover y busque la entrada Actividad que representa esta configuración.
  3. Cambie el valor de metadatos de la actividad para com.android.settings.category y establezca el valor en la clave de categoría de la nueva página.

Crear una nueva preferencia en una página

Si la preferencia aparece estáticamente en el archivo XML de preferencias de la página original, siga el procedimiento estático siguiente. En caso contrario seguir el procedimiento dinámico .

Creando una preferencia estática

  1. Busque los archivos XML de preferencias para la página. Puede encontrar esta información en el método getPreferenceScreenResId() de la página.
  2. Agregue un nuevo elemento de preferencia en el XML. Asegúrese de que tenga una android:key única.
  3. Defina un PreferenceController para esta preferencia en el método getPreferenceControllers() de la página.
    • En Android 8.x y, opcionalmente, en Android 9, crea una instancia de un 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 exista un PreferenceController para ella. Puede reutilizar PreferenceController sin crear uno nuevo.

    • A partir de Android 9, puedes optar por declarar 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"/>
      

Creando una preferencia dinámica

  1. Encuentre qué categoría alberga la página original y la de destino. Puede encontrar esta información en DashboardFragmentRegistry .
  2. Crear una nueva actividad en AndroidManifest
  3. Agregue los metadatos necesarios a la nueva Actividad para definir la configuración. Establezca el valor de metadatos para com.android.settings.category en el mismo valor definido en el paso 1.

Crear una nueva página

  1. Cree un nuevo fragmento, heredando de DashboardFragment .
  2. Defina su categoría en DashboardFragmentRegistry .

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

  3. Siga los pasos para agregar las configuraciones necesarias para esta página. Para obtener más información, consulte la sección Implementación .

Validación

  • Ejecute las pruebas roboeléctricas en Configuración. Todas las pruebas nuevas y existentes deben pasar.
  • Cree e instale Configuración, luego abra manualmente la página que se está modificando. La página debería actualizarse inmediatamente.