Arquitetura de informação

O Android 8.0 introduziu uma nova arquitetura de informações para o aplicativo Configurações para simplificar a forma como as configurações são organizadas e tornar mais fácil para os usuários encontrarem rapidamente configurações para personalizar seus dispositivos Android. O Android 9 introduziu algumas melhorias para fornecer mais funcionalidades de configurações e implementação mais fácil.

Exemplos e fonte

A maioria das páginas em Configurações estão atualmente implementadas usando a nova estrutura. Um bom exemplo é DisplaySettings: packages/apps/Settings/src/com/android/settings/DisplaySettings.java

Os caminhos de arquivos para componentes importantes estão listados abaixo:

  • 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 (introduzido no Android 9): packages/apps/Settings/src/com/android/settings/core/BasePreferenceController.java

Implementação

Os fabricantes de dispositivos são incentivados a adaptar a arquitetura de informações de configurações existente e inserir páginas de configurações adicionais conforme necessário para acomodar recursos específicos do parceiro. Mover preferências de uma página herdada (implementada como SettingsPreferencePage ) para uma nova página (implementada usando DashboardFragment ) pode ser complicado. A preferência da página herdada provavelmente não é implementada com um PreferenceController .

Portanto, ao mover preferências de uma página legada para uma nova página, você precisa criar um PreferenceController e mover o código para o controlador antes de instanciá-lo no novo DashboardFragment . As APIs exigidas PreferenceController são descritas em seus nomes e documentadas em Javadoc.

É altamente recomendável adicionar um teste de unidade para cada PreferenceController . Se a alteração for enviada ao AOSP, será necessário um teste de unidade. Para obter mais informações sobre como escrever testes baseados em Robolectric, consulte o arquivo leia-me packages/apps/Settings/tests/robotests/README.md .

Arquitetura de informação estilo plug-in

Cada item de configuração é implementado como uma Preferência. Uma preferência pode ser facilmente movida de uma página para outra.

Para facilitar a movimentação de várias configurações, o Android 8.0 introduziu um fragmento de host no estilo de plug-in que contém itens de configuração. Os itens de configuração são modelados como controladores estilo plugin. Conseqüentemente, uma página de configurações é construída por um único fragmento de host e vários controladores de configuração.

Fragmento do painel

DashboardFragment é o host de controladores de preferência estilo plugin. O fragmento herda de PreferenceFragment e possui ganchos para expandir e atualizar listas de preferências estáticas e listas de preferências dinâmicas.

Preferências estáticas

Uma lista de preferências estáticas é definida em XML usando a tag <Preference> . Uma implementação DashboardFragment usa o método getPreferenceScreenResId() para definir qual arquivo XML contém a lista estática de preferências a serem exibidas.

Preferências dinâmicas

Um item dinâmico representa um bloco com intenção, levando a uma Atividade externa ou interna. Normalmente, a intenção leva a uma página de configuração diferente. Por exemplo, o item de configuração "Google" na página inicial de Configurações é um item dinâmico. Itens dinâmicos são definidos em AndroidManifest (discutido abaixo) e carregados por meio de um FeatureProvider (definido como DashboardFeatureProvider ).

As configurações dinâmicas são mais pesadas do que as configurações configuradas estaticamente, portanto, normalmente os desenvolvedores devem implementar a configuração como estática. No entanto, a configuração dinâmica pode ser útil quando qualquer uma das seguintes situações for verdadeira:

  • A configuração não é implementada diretamente no aplicativo Configurações (como injetar uma configuração implementada por aplicativos OEM/Carrier).
  • A configuração deve aparecer na página inicial de Configurações.
  • Você já tem uma atividade para a configuração e não deseja implementar a configuração estática extra.

Para configurar uma atividade como uma configuração dinâmica, faça o seguinte:

  • Marque a atividade como uma configuração dinâmica adicionando um filtro de intenção à atividade.
  • Informe ao aplicativo Configurações a qual categoria ele pertence. A categoria é uma constante, definida em CategoryKey .
  • Opcional: Adicione um texto de resumo quando a configuração for exibida.

Aqui está um exemplo retirado do aplicativo Configurações 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>

No momento da renderização, o fragmento solicitará uma lista de preferências do XML estático e das configurações dinâmicas definidas em AndroidManifest . Quer os PreferenceController s sejam definidos em código Java ou em XML, DashboardFragment gerencia a lógica de manipulação de cada configuração por meio de PreferenceController (discutido abaixo). Em seguida, eles são exibidos na UI como uma lista mista.

Controlador de preferência

Existem diferenças entre a implementação PreferenceController no Android 9 e no Android 8.x, conforme descrito nesta seção.

PreferenceController na versão Android 9

Um PreferenceController contém toda a lógica para interagir com a preferência, incluindo exibição, atualização, indexação de pesquisa, etc.

A interface de PreferenceController é definida como BasePreferenceController . Por exemplo, consulte o código em packages/apps/Settings/src/com/android/settings/core/ BasePreferenceController.java

Existem várias subclasses de BasePreferenceController , cada uma mapeada para um estilo de UI específico que o aplicativo Configurações suporta por padrão. Por exemplo, TogglePreferenceController possui uma API que mapeia diretamente como o usuário deve interagir com uma IU de preferência baseada em alternância.

BasePreferenceController possui APIs como getAvailabilityStatus() , displayPreference() , handlePreferenceTreeClicked(), etc. A documentação detalhada para cada API está na classe de interface.

Uma restrição na implementação BasePreferenceController (e suas subclasses, como TogglePreferenceController ) é que a assinatura do construtor deve corresponder a um dos seguintes:

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

Ao instalar uma preferência no fragmento, o painel fornece um método para anexar um PreferenceController antes do horário de exibição. No momento da instalação, o controlador é conectado ao fragmento para que todos os eventos futuros relevantes sejam enviados ao controlador.

DashboardFragment mantém uma lista de PreferenceController s na tela. No onCreate() do fragmento, todos os controladores são invocados para o método getAvailabilityStatus() e, se retornar verdadeiro, displayPreference() é invocado para processar a lógica de exibição. getAvailabilityStatus() também é importante para informar à estrutura de configurações quais itens estão disponíveis durante a pesquisa.

PreferenceController nas versões do Android 8.x

Um PreferenceController contém toda a lógica para interagir com a preferência, incluindo exibição, atualização e indexação de pesquisa. etc.

Correspondendo às interações de preferência, a interface de PreferenceController possui APIs isAvailable() , displayPreference() , handlePreferenceTreeClicked() etc. Documentação detalhada sobre cada API pode ser encontrada na classe de interface.

Ao instalar uma preferência no fragmento, o painel fornece um método para anexar um PreferenceController antes do horário de exibição. No momento da instalação, o controlador é conectado ao fragmento para que todos os eventos futuros relevantes sejam enviados ao controlador.

DashboardFragment mantém uma lista de PreferenceControllers na tela. No onCreate() do fragmento, todos os controladores são invocados para o método isAvailable() e, se retornar verdadeiro, displayPreference() é invocado para processar a lógica de exibição.

Usando DashboardFragment

Movendo uma preferência da página A para B

Se a preferência estiver listada estaticamente no arquivo XML de preferências da página original, siga o procedimento de movimentação estática para sua versão do Android abaixo. Caso contrário, siga o procedimento de movimentação dinâmica para a versão do Android.

Movimento estático no Android 9

  1. Encontre os arquivos XML de preferência para a página original e a página de destino. Você pode encontrar essas informações no método getPreferenceScreenResId() da página.
  2. Remova a preferência do XML da página original.
  3. Adicione a preferência ao XML da página de destino.
  4. Remova o PreferenceController dessa preferência da implementação Java da página original. Geralmente está em createPreferenceControllers() . O controlador pode ser declarado diretamente em XML.

    Nota : A preferência pode não ter um PreferenceController .

  5. Instancie o PreferenceController no createPreferenceControllers() da página de destino. Se o PreferenceController estiver definido em XML na página antiga, defina-o também em XML para a nova página.

Movimento dinâmico no Android 9

  1. Descubra qual categoria a página original e de destino hospeda. Você pode encontrar essas informações em DashboardFragmentRegistry .
  2. Abra o arquivo AndroidManifest.xml que contém a configuração que você precisa mover e encontre a entrada Activity que representa essa configuração.
  3. Defina o valor dos metadados da atividade para com.android.settings.category como a chave de categoria da nova página.

Movimento estático nas versões do Android 8.x

  1. Encontre os arquivos XML de preferência para a página original e a página de destino.
  2. Você pode encontrar essas informações no método getPreferenceScreenResId() da página.
  3. Remova a preferência no XML da página original.
  4. Adicione a preferência ao XML da página de destino.
  5. Remova o PreferenceController dessa preferência na implementação Java da página original. Geralmente está em getPreferenceControllers() .
  6. Nota : É possível que a preferência não possua um PreferenceController .

  7. Instancie o PreferenceController no getPreferenceControllers() da página de destino.

Movimento dinâmico nas versões do Android 8.x

  1. Descubra qual categoria a página original e de destino hospeda. Você pode encontrar essas informações em DashboardFragmentRegistry .
  2. Abra o arquivo AndroidManifest.xml que contém a configuração que você precisa mover e encontre a entrada Activity que representa essa configuração.
  3. Altere o valor dos metadados da atividade para com.android.settings.category e defina o ponto de valor para a chave de categoria da nova página.

Criando uma nova preferência em uma página

Se a preferência estiver listada estaticamente no arquivo XML de preferências da página original, siga o procedimento estático abaixo. Caso contrário, siga o procedimento dinâmico .

Criando uma preferência estática

  1. Encontre os arquivos XML de preferência para a página. Você pode encontrar essas informações no método getPreferenceScreenResId() da página.
  2. Adicione um novo item de preferência no XML. Certifique-se de que ele tenha um android:key exclusivo.
  3. Defina um PreferenceController para esta preferência no método getPreferenceControllers() da página.
    • No Android 8.xe opcionalmente no Android 9, instancie um PreferenceController para essa preferência no método createPreferenceControllers() da página.

      Se esta preferência já existia em outros locais, é possível que já exista um PreferenceController para ela. Você pode reutilizar o PreferenceController sem criar um novo.

    • A partir do Android 9, você pode optar por declarar o PreferenceController em XML ao lado da preferência. Por exemplo:
      <Preference
              android:key="reset_dashboard"
              android:title="@string/reset_dashboard_title"
              settings:controller="com.android.settings.system.ResetPreferenceController"/>
      

Criando uma preferência dinâmica

  1. Descubra qual categoria a página original e de destino hospeda. Você pode encontrar essas informações em DashboardFragmentRegistry .
  2. Crie uma nova atividade no AndroidManifest
  3. Adicione os metadados necessários à nova atividade para definir a configuração. Defina o valor dos metadados para com.android.settings.category com o mesmo valor definido na etapa 1.

Crie uma nova página

  1. Crie um novo fragmento, herdando de DashboardFragment .
  2. Defina sua categoria em DashboardFragmentRegistry .

    Nota: Esta etapa é opcional. Se não precisar de nenhuma preferência dinâmica nesta página, não será necessário fornecer uma chave de categoria.

  3. Siga as etapas para adicionar as configurações necessárias para esta página. Para obter mais informações, consulte a seção Implementação .

Validação

  • Execute os testes robolétricos em Configurações. Todos os testes existentes e novos devem passar.
  • Crie e instale as configurações e abra manualmente a página que está sendo modificada. A página deve ser atualizada imediatamente.