Arquitetura de informações

O Android 8.0 introduziu uma nova arquitetura da informação para o app Configurações para: simplificar a maneira como as configurações são organizadas e facilitar para os usuários encontrar rapidamente as configurações para personalizar os dispositivos Android. O Android 9 introduziu algumas melhorias para oferecer funcionalidade de configurações e implementação facilitada.

Exemplos e origem

No momento, a maioria das páginas em "Configurações" é implementada usando o novo framework. Uma boa 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
  • BackgroundPreferenceController: 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

Recomendamos que os fabricantes de dispositivos adaptem as informações atuais das configurações e inserir páginas de configuração adicionais conforme necessário para acomodar recursos específicos do parceiro. Como mover preferências da página legada (implementado como SettingsPreferencePage) para uma nova página (implementada usando DashboardFragment) pode ser complicado. A preferência da página legada provavelmente não está implementada com um PreferenceController.

Portanto, ao mover uma preferência de uma página legada para uma nova, é necessário criar um PreferenceController e mover o código para o controlador antes de instanciar no novo DashboardFragment. As APIs que As exigências de PreferenceController estão descritas no nome e documentadas no 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 mais informações sobre como criar testes baseados no Robolectric, consulte a arquivo readme packages/apps/Settings/tests/robotests/README.md.

Arquitetura de informações no estilo de plug-in

Cada item de configurações é implementado como uma Preferência. Uma preferência pode ser movidos 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 estilo plug-in que contém itens de configuração. Os itens de configurações são modelados como controladores no estilo de plug-in. Portanto, uma página de configurações é construída por um fragmento de host único e vários controladores de configuração.

DashboardFragment

O DashboardFragment é o host de controladores de preferência do estilo de plug-in. O fragmento herda de PreferenceFragment e tem hooks para expandir e atualizar listas de preferências estáticas e dinâmicas.

Preferências estáticas

Uma lista de preferências estáticas é definida em XML usando a tag <Preference>. Um A implementação de DashboardFragment usa as 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 intent, que leva a uma atividade externa ou interna. Normalmente, a intent leva a uma página de configuração diferente. Por exemplo: a página de configuração na página inicial "Configurações" é um item dinâmico. Dinâmico itens são definidos em AndroidManifest (discutidos abaixo) e carregados usando um FeatureProvider (definido como DashboardFeatureProvider).

As configurações dinâmicas são mais pesadas do que as configurações configuradas estaticamente. Por isso, normalmente os desenvolvedores precisam implementar a configuração como estática. No entanto, a configuração dinâmica pode ser útil quando alguma das seguintes condições é verdadeira:

  • A configuração não é implementada diretamente no app Configurações (como injetando uma configuração implementada por apps de OEM/operadora).
  • A configuração vai aparecer na página inicial de configurações.
  • Você já tem uma atividade para a configuração e não quer implementar a configuração extra estática.

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 intent à atividade.
  • Informe ao app Configurações a categoria a que 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 app Configurações de 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 solicita uma lista de preferências da API Configurações dinâmicas e XML definidas em AndroidManifest. Se as PreferenceControllers forem definidas em código Java ou em XML, DashboardFragment vai gerenciar a lógica de tratamento de cada configuração por PreferenceController (discutido abaixo). Em seguida, eles são exibidos na interface como uma lista mista.

PreferenceController

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

PreferenceController na versão do Android 9

Uma PreferenceController contém toda a lógica para interagir com o preferência, incluindo exibição, atualização, indexação de pesquisas, 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

Há várias subclasses de BasePreferenceController, cada uma mapeada para um estilo de interface específico compatível com o app Configurações por padrão. Por exemplo, TogglePreferenceController tem uma API que mapeia diretamente como o usuário deve interagir com uma interface de preferência baseada em alternância.

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

Uma restrição na implementação de BasePreferenceController (e das subclasses, como TogglePreferenceController) é que A assinatura do construtor precisa corresponder a um dos seguintes itens:

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

Ao instalar uma preferência para o fragmento, o painel fornece um método para anexar um PreferenceController antes do tempo de exibição. No momento da instalação, o controlador é conectado ao fragmento para que todos os eventos relevantes futuros 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 getAvailabilityStatus() e, se ele retornar "verdadeiro", displayPreference() será invocado para processar a lógica de exibição. O getAvailabilityStatus() também é importante para informar ao framework 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, indexação de pesquisa etc.

A interface de PreferenceController tem as APIs isAvailable(), displayPreference(), handlePreferenceTreeClicked() etc., de acordo com as interações de preferência. A documentação detalhada de cada API pode ser encontrada na classe de interface.

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

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

Usar o DashboardFragment

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

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

Movimento estático no Android 9

  1. Encontrar os arquivos XML de preferência para a página original e o destino página. Você pode encontrar essas informações na página getPreferenceScreenResId().
  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. Normalmente, é em createPreferenceControllers(): O controlador pode ser declarado em XML diretamente.

    Observação: a preferência pode não ter um PreferenceController.

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

Movimento dinâmico no Android 9

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

Movimentação estática nas versões do Android 8.x

  1. Encontre os arquivos XML de preferências da página original e da 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 para essa preferência na implementação Java da página original. Geralmente, ele fica em getPreferenceControllers().
  6. Observação: é possível que a preferência não tenha PreferenceController

  7. Instancie o PreferenceController no elemento getPreferenceControllers().

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

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

Criar uma nova preferência em uma página

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

Criar uma preferência estática

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

      Se essa preferência já existia em outros lugares, é possível que já exista um PreferenceController para ela. É possível reutilizar o PreferenceController sem criar um novo.

    • A partir do Android 9, é possível declarar os PreferenceController em XML ao lado da preferência. Exemplo:
      <Preference
              android:key="reset_dashboard"
              android:title="@string/reset_dashboard_title"
              settings:controller="com.android.settings.system.ResetPreferenceController"/>

Criar uma preferência dinâmica

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

Criar nova página

  1. Crie um novo fragmento, herdando de DashboardFragment.
  2. Defina a categoria no DashboardFragmentRegistry.

    Observação: esta etapa é opcional. Se você não precisar preferências dinâmicas nesta página, não será necessário fornecer uma chave de categoria.

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

Validação

  • Execute os testes do Robolectric nas Configurações. Todos os testes novos e existentes precisam passar.
  • Crie e instale as Configurações e abra manualmente a página que está sendo modificada. A página será atualizada imediatamente.