Arquitetura de informação

O Android 8.0 introduziu uma nova arquitetura de informações para o aplicativo Configurações para simplificar a maneira como as configurações são organizadas e tornar mais fácil para os usuários encontrarem rapidamente as 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á atualmente implementada 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 as preferências da página herdada (implementada como SettingsPreferencePage ) para uma nova página (implementada usando DashboardFragment ) pode ser complicado. A preferência da página legada provavelmente não é implementada com um PreferenceController .

Portanto, ao mover as preferências de uma página herdada 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 que PreferenceController requer são descritas em seu nome e documentadas em Javadoc.

É altamente recomendável adicionar um teste de unidade para cada PreferenceController . Se a alteração for submetida 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ções no estilo de 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ções são modelados como controladores de estilo de plug-in. Portanto, uma página de configurações é construída por um único fragmento de host e vários controladores de configuração.

DashboardFragment

DashboardFragment é o host de controladores de preferência de estilo de plug-in. O fragmento herda de PreferenceFragment e tem 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 de 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. Os itens dinâmicos são definidos no 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 condiçõ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 Activity para a configuração e não deseja implementar a configuração estática extra.

Para definir 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.
  • Diga ao aplicativo Configurações a qual categoria ele pertence. A categoria é uma constante, definida em CategoryKey .
  • Opcional: adicione 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 . Independentemente de os PreferenceController serem definidos em código Java ou em XML, o DashboardFragment gerencia a lógica de manipulação de cada configuração por meio do PreferenceController (discutido abaixo). Em seguida, eles são exibidos na interface do usuário como uma lista mista.

Controlador de Preferências

Existem diferenças entre implementar PreferenceController no Android 9 e 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, veja o código em packages/apps/Settings/src/com/android/settings/core/ BasePreferenceController.java

Existem várias subclasses de BasePreferenceController , cada uma mapeando para um estilo de interface do usuário específico que o aplicativo Configurações oferece suporte por padrão. Por exemplo, TogglePreferenceController tem uma API que mapeia diretamente como o usuário deve interagir com uma IU 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 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 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.

O DashboardFragment mantém uma lista de PreferenceController na tela. No onCreate() do fragmento, todos os controladores são invocados para o método getAvailabilityStatus() e, se retornar true, 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 em 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.

Correspondendo às interações de preferência, a interface do PreferenceController possui as APIs isAvailable() , displayPreference() , handlePreferenceTreeClicked() etc. A 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 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 isAvailable() e, se retornar true, displayPreference() é invocado para processar a lógica de exibição.

Usando o DashboardFragment

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

Se a preferência estiver listada estaticamente no arquivo XML de preferência 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 sua 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 em XML diretamente.

    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. Encontre 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 localize a entrada Atividade que representa essa configuração.
  3. Defina o valor de metadados da atividade para com.android.settings.category para 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 para esta preferência na implementação Java da página original. Normalmente está em getPreferenceControllers() .
  6. Nota : É possível que a preferência não tenha um PreferenceController .

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

Mudança dinâmica nas versões do Android 8.x

  1. Encontre 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 localize a entrada Atividade que representa essa configuração.
  3. Altere o valor de metadados da atividade para com.android.settings.category , 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ência 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 essa preferência já existia em outros lugares, é 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. Encontre 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 de metadados para com.android.settings.category com o mesmo valor definido na etapa 1.

Criar uma nova página

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

    Nota: Esta etapa é opcional. Se você não precisar de preferências dinâmicas nesta página, não precisará 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 robolectric 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.