Apps para dispositivos dobráveis e com várias telas
Geralmente, os apps não devem depender de identificadores estáticos ou de uma lógica que dependa de alguns IDs de tela. Na maioria dos casos, os apps precisam ser redimensionados e funcionar em telas diferentes, e o sistema precisa controlar onde os apps estão localizados. Por exemplo, para criar uma experiência nova e exclusiva para dispositivos dobráveis e iniciar um app especial na tela externa quando o dispositivo estiver dobrado.
Nesse caso, a SystemUI (ou outro componente do sistema) precisa detectar a dobra, determinar se é apropriado realizar uma ação e, em seguida, iniciar a atividade de destino e especificar um ID de tela externa como o destino de inicialização. Os apps não podem detectar essa ação nem realizar nenhuma ação em resposta e, em seguida, realizar a inicialização em uma tela específica. Em outras palavras, não suponha que o que funciona em um dispositivo vai funcionar em outros. Em resumo, o código específico do dispositivo aumenta a fragmentação.
Restringir o acesso às telas
Se a configuração do dispositivo exigir a restrição do acesso a uma ou mais telas, a recomendação é usar a flag Display#FLAG_PRIVATE para designar essas telas como privadas. Isso restringe a adição de conteúdo à tela, exceto pelo proprietário. Qualquer tentativa de iniciar uma atividade ou adicionar uma janela por alguém que não seja o proprietário resulta em uma SecurityException.
Se o sistema for o proprietário da tela, ele poderá adicionar janelas e iniciar atividades.
Além disso, as entidades colocadas em uma tela sempre podem acessar essa tela. Se o proprietário iniciar uma atividade em uma tela, ela poderá iniciar outras atividades nessa tela. Como resultado, o proprietário é responsável por restringir o acesso e permitir apenas apps confiáveis.
Além disso, mais restrições são adicionadas às telas virtuais, porque qualquer app pode criar uma sem torná-la visível ao usuário. Se a tela virtual não for de propriedade do sistema, somente atividades com allowEmbedded serão permitidas, e o autor da chamada precisará ter a permissão ACTIVITY_EMBEDDING.
Para mais informações, consulte:
ActivityStackSupervisor#isCallerAllowedToLaunchOnDisplay()ActivityDisplay#isUidPresent()DisplayManagerService#isUidPresentOnDisplay()
Para controlar condicionalmente as inicializações de atividades, use LaunchParamsController, que intercepta todas as inicializações de atividades e permite que um componente do sistema modifique os parâmetros usados para a inicialização. Isso está disponível em system_server.
Configurar as definições de janela de exibição e as decorações do sistema
As decorações do sistema podem
ser configuradas por tela em DisplayWindowSettings. Uma implementação de dispositivo pode fornecer uma configuração padrão em /data/system/display_settings.xml.
Esse valor determina se as decorações do sistema (launcher, papel de parede, barra de navegação e outras janelas de decoração) e o IME aparecem em uma tela.
Para mais detalhes, consulte DisplayWindowSettings#shouldShowSystemDecorsLocked() e DisplayWindowSettings#shouldShowImeLocked().
Para identificar a tela, use um ID exclusivo (esse padrão usa DisplayInfo#uniqueId) ou um ID de porta física para telas de hardware (consulte DisplayInfo#address).
Por exemplo, a configuração de exibição a seguir ativa as decorações do sistema e o IME em uma tela simulada:
<?xml version='1.0' encoding='utf-8' standalone='yes' ?> <display-settings> <config identifier="0" /> <display name="overlay:1" shouldShowSystemDecors="true" shouldShowIme="true" /> </display-settings>
No exemplo acima, uniqueId é usado para identificação de tela no atributo name, que para uma tela simulada é overlay:1.
Para uma tela integrada, um valor de amostra pode ser "local:45354385242535243453".
Outra opção é usar informações da porta de hardware e definir identifier="1"
para corresponder a DisplayWindowSettings#IDENTIFIER_PORT e, em seguida, atualizar o
nome para usar o formato "port:<port_id>":
<?xmlversion='1.0' encoding='utf-8' standalone='yes' ?> <display-settings> <config identifier="1" /> <display name="port:12345" shouldShowSystemDecors="true" shouldShowIme="true" /> </display-settings>
Para mais detalhes, consulte Identificadores de tela estáticos.
Para mais informações, consulte:
- Configurações da janela de exibição
- Identificadores de tela estáticos
- Suporte a decorações do sistema
Alternar telas entre tarefas de espelhamento e hospedagem
No Android 17 e versões mais recentes, o DisplayManager usa a
FLAG_ALLOWS_CONTENT_MODE_SWITCH flag para controlar se uma tela alterna
entre tarefas de espelhamento e hospedagem no momento da execução. Por padrão, essa flag está ativada para telas externas e desativada para todas as outras.
Quando FLAG_ALLOWS_CONTENT_MODE_SWITCH está presente, o DisplayManager monitora a configuração segura android.provider.Settings.Secure.MIRROR_BUILT_IN_DISPLAY para determinar se as tarefas serão espelhadas ou hospedadas. Embora essa seja a lógica padrão, os OEMs podem personalizar esse comportamento.
Topologia de exibição e movimento do ponteiro
No Android 17 e versões mais recentes, a topologia de exibição define as posições relativas das telas e restringe o movimento do ponteiro do mouse ao conjunto específico de telas na topologia.
O WindowManager decide incluir uma tela na topologia e chama DisplayManagerInternal.onDisplayBelongToTopologyChanged. O DisplayManager verifica DisplayTopologyCoordinator.isDisplayAllowedInTopology antes de adicionar a tela. Por padrão, se as telas locais puderem hospedar tarefas, o sistema as adicionará.
Se houver várias telas públicas que podem hospedar tarefas, a decisão de incluir a tela padrão será processada pelo provedor booleano shouldIncludeDefaultDisplayInTopology, que é transmitido para DisplayTopologyCoordinator. Se a tela padrão for a única tela pública que pode hospedar tarefas, ela sempre estará na topologia. No AOSP, o provedor booleano retorna true somente se a tela padrão for compatível com o Modo Janela para Computador ou se a configuração segura Settings.Secure.INCLUDE_DEFAULT_DISPLAY_IN_TOPOLOGY for true.
Os apps consultam a topologia atual usando DisplayManager.getDisplayTopology e reagem a mudanças na topologia registrando um listener com DisplayManager.registerTopologyListener.