O Android 8.0 apresentou uma nova arquitetura de informações para o app Configurações para simplificar a maneira como as configurações são organizadas e facilitar para os usuários a tarefa de encontrar rapidamente as configurações necessárias para personalizar os dispositivos Android. O Android 9 introduziu algumas melhorias para oferecer mais funcionalidades de configurações e uma implementação mais fácil.
Exemplos e origem
No momento, a maioria das páginas em "Configurações" é implementada usando o novo framework. 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
- 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
Os fabricantes de dispositivos são incentivados a adaptar a arquitetura de informações
de configurações existente e inserir outras páginas de configuração conforme necessário para acomodar
recursos específicos do parceiro. Mover preferências da página legada (implementada 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
PreferenceController
exige sã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 escrever testes baseados no Robolectric, consulte o
arquivo de 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ção 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
DashboardFragment
é o host dos controladores de preferências do tipo 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ática é 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 intent, levando a uma atividade externa ou
interna. Normalmente, a intent leva a uma página de configuração diferente. Por exemplo,
o item de configuração "Google" na página inicial "Configurações" é dinâmico. Os itens
dinâmicos são definidos em AndroidManifest
(discutido abaixo) e carregados
por um FeatureProvider
(definido como
DashboardFeatureProvider
).
As configurações dinâmicas são mais pesadas do que as configurações configuradas de forma estática. Por isso, normalmente os desenvolvedores precisam implementar a configuração como estática. No entanto, a configuração dinâmica pode ser útil quando uma das seguintes condições for verdadeira:
- A configuração não é implementada diretamente no app Configurações, como injetar uma configuração implementada por apps 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 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 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 vai solicitar uma lista de preferências do XML estático
e das configurações dinâmicas definidas em AndroidManifest
. Se as
PreferenceController
s 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
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
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 para como o usuário precisa interagir com uma interface de preferências baseadas 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 precisa corresponder a uma das seguintes opções:
public MyController(Context context, String key) {}
public MyController(Context context) {}
Ao instalar uma preferência no fragmento, o painel oferece 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
PreferenceController
s 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.
Correspondente às interações de preferência, a interface de
PreferenceController
tem as APIs isAvailable()
,
displayPreference()
, handlePreferenceTreeClicked()
etc.
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
os controladores são invocados para o método isAvailable()
e, se ele
retornar verdadeiro, o displayPreference()
será invocado para processar a lógica
de exibição.
Usar o DashboardFragment
Mover uma preferência da página A para a 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 movimentação dinâmica para sua versão do Android.
Movimento estático no Android 9
- Encontre os arquivos XML de preferências da página original e da página de destino. Você pode encontrar essas informações no método
getPreferenceScreenResId()
da página. - Remova a preferência do XML da página original.
- Adicione a preferência ao XML da página de destino.
- Remova o
PreferenceController
dessa preferência da implementação Java da página original. Normalmente, ele fica emcreatePreferenceControllers()
. O controlador pode ser declarado diretamente no XML.Observação: a preferência pode não ter um
PreferenceController
. - Instancie o
PreferenceController
nocreatePreferenceControllers()
da página de destino. Se oPreferenceController
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
- Encontre a categoria que hospeda a página original e a de destino. Essas informações
estão em
DashboardFragmentRegistry
. - Abra o arquivo
AndroidManifest.xml
que contém a configuração que precisa ser movida e encontre a entrada de atividade que representa essa configuração. - Defina o valor dos 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
- Encontre os arquivos XML de preferências da página original e da página de destino. Você pode encontrar essas informações no método
- Remova a preferência no XML da página original.
- Adicione a preferência ao XML da página de destino.
- Remova o
PreferenceController
para essa preferência na implementação Java da página original. Geralmente, ele fica emgetPreferenceControllers()
. - Instância o
PreferenceController
nogetPreferenceControllers()
da página de destino.
getPreferenceScreenResId()
da página.
Observação:é possível que a preferência não tenha um
PreferenceController
.
Movimento dinâmico nas versões do Android 8.x
- Descubra qual categoria hospeda a página original e a de destino. Você pode encontrar
essas informações em
DashboardFragmentRegistry
. - Abra o arquivo
AndroidManifest.xml
que contém a configuração que você precisa mover e encontre a entrada "Atividade" que representa essa configuração. - Mude o valor de metadados da atividade para
com.android.settings.category
e 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 listada de forma estática no arquivo XML de preferência da página original, siga o procedimento estático abaixo. Caso contrário, siga o procedimento dinâmico.
Criar uma preferência estática
- Encontre os arquivos XML de preferências da página. Você pode encontrar essas informações no método getPreferenceScreenResId() da página.
- Adicione um novo item de preferência no XML. Verifique se ele tem um
android:key
exclusivo. -
Defina um
PreferenceController
para essa preferência no métodogetPreferenceControllers()
da página.- No Android 8.x e, opcionalmente, no Android 9, instancie um
PreferenceController
para essa preferência no métodocreatePreferenceControllers()
da página.Se essa preferência já existia em outros lugares, é possível que já exista um
PreferenceController
para ela. É possível reutilizar oPreferenceController
sem criar um novo. -
A partir do Android 9, você pode declarar o
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"/>
- No Android 8.x e, opcionalmente, no Android 9, instancie um
Criar uma preferência dinâmica
- Descubra qual categoria hospeda a página original e a de destino. Confira
essas informações em
DashboardFragmentRegistry
. - Criar uma nova atividade em
AndroidManifest
- Adicione os metadados necessários à nova atividade para definir a configuração. Defina o
valor dos metadados de
com.android.settings.category
com o mesmo valor definido na etapa 1.
Criar nova página
- Crie um novo fragmento, herdando de
DashboardFragment
. - Defina a categoria em
DashboardFragmentRegistry
.Observação:esta etapa é opcional. Se você não precisar de preferências dinâmicas nesta página, não será necessário fornecer uma chave de categoria.
- Siga as etapas para adicionar as configurações necessárias a essa página. Para mais informações, consulte a seção Implementação.
Validação
- Execute os testes do Robolectric nas Configurações. Todos os testes novos e atuais precisam ser aprovados.
- Crie e instale as Configurações e abra manualmente a página que está sendo modificada. A página será atualizada imediatamente.