Suporte de exibição

As atualizações feitas nessas áreas específicas da exibição são fornecidas abaixo:

Redimensionar atividades e exibições

Para indicar que um aplicativo pode não suportar o modo de múltiplas janelas ou redimensionamento, as atividades usam o atributo resizeableActivity=false . Os problemas comuns encontrados pelos aplicativos quando as atividades são redimensionadas incluem:

  • Uma atividade pode ter uma configuração diferente da aplicação ou outro componente não visual. Um erro comum é ler as métricas de exibição do contexto do aplicativo. Os valores retornados não serão ajustados às métricas da área visível em que uma atividade é exibida.
  • Uma atividade pode não lidar com o redimensionamento e travar, exibir uma IU distorcida ou perder o estado devido à reinicialização sem salvar o estado da instância.
  • Um aplicativo pode tentar usar coordenadas de entrada absolutas (em vez daquelas relativas à posição da janela), o que pode quebrar a entrada em várias janelas.

No Android 7 (e superior), um aplicativo pode ser definido como resizeableActivity=false para ser sempre executado no modo de tela inteira. Nesse caso, a plataforma evita que atividades não redimensionáveis ​​entrem em tela dividida. Se o usuário tentar invocar uma atividade não redimensionável do inicializador enquanto já estiver no modo de tela dividida, a plataforma sairá do modo de tela dividida e iniciará a atividade não redimensionável no modo de tela inteira.

Os aplicativos que definem explicitamente esse atributo como false no manifesto não devem ser iniciados no modo de múltiplas janelas, a menos que o modo de compatibilidade seja aplicado:

  • A mesma configuração é aplicada ao processo, que contém todas as atividades e componentes não-atividades.
  • A configuração aplicada atende aos requisitos de CDD para monitores compatíveis com aplicativos.

No Android 10, a plataforma ainda impede que atividades não redimensionáveis ​​entrem no modo de tela dividida, mas elas podem ser dimensionadas temporariamente caso a atividade tenha declarado uma orientação ou proporção fixa. Caso contrário, a atividade será redimensionada para preencher a tela inteira, como no Android 9 e versões anteriores.

A implementação padrão aplica a seguinte política:

Quando uma atividade é declarada incompatível com múltiplas janelas através do uso do atributo android:resizeableActivity e quando essa atividade atende a uma das condições descritas abaixo, quando a configuração da tela aplicada deve ser alterada, a atividade e o processo são salvos com a configuração original e o usuário recebe a oportunidade de reiniciar o processo do aplicativo para usar a configuração de tela atualizada.

  • A orientação é fixa por meio do aplicativo android:screenOrientation
  • O aplicativo tem proporção máxima ou mínima padrão segmentando o nível da API ou declara a proporção explicitamente

Esta figura exibe uma atividade não redimensionável com uma proporção declarada. Ao dobrar o dispositivo, a janela é reduzida para caber na área, mantendo a proporção usando a caixa de correio apropriada. Além disso, uma opção de reinicialização da atividade é fornecida ao usuário sempre que a área de exibição da atividade é alterada.

Ao desdobrar o dispositivo, a configuração, o tamanho e a proporção da atividade não mudam, mas é exibida a opção de reiniciar a atividade.

Quando resizeableActivity não está definido (ou está definido como true ), o aplicativo oferece suporte total ao redimensionamento.

Implementação

Uma atividade não redimensionável com orientação ou proporção fixa é chamada de modo de compatibilidade de tamanho (SCM) no código. A condição é definida em ActivityRecord#shouldUseSizeCompatMode() . Quando uma atividade SCM é iniciada, a configuração relacionada à tela (como tamanho ou densidade) é fixada na configuração de substituição solicitada, de modo que a atividade não depende mais da configuração de exibição atual.

Se a atividade SCM não puder preencher a tela inteira, ela será alinhada na parte superior e centralizada horizontalmente. Os limites de atividade são calculados por AppWindowToken#calculateCompatBoundsTransformation() .

Quando uma atividade SCM usa uma configuração de tela diferente de seu contêiner (por exemplo, a exibição é redimensionada ou a atividade é movida para outra exibição), ActivityRecord#inSizeCompatMode() é verdadeira e SizeCompatModeActivityController (na UI do sistema) recebe o retorno de chamada para mostrar o processo botão reiniciar.

Tamanhos de exibição e proporções

O Android 10 oferece suporte para novas proporções, desde proporções altas de telas longas e finas até proporções de 1:1. Os aplicativos podem definir ApplicationInfo#maxAspectRatio e o ApplicationInfo#minAspectRatio da tela que eles são capazes de manipular.

proporções de aplicativos no Android 10

Figura 1. Exemplos de proporções de aplicativos compatíveis com Android 10

As implementações de dispositivos podem ter telas secundárias com tamanhos e resoluções menores que os exigidos pelo Android 9 e inferiores (mínimo de 2,5 polegadas de largura ou altura, mínimo de 320 DP para smallestScreenWidth ), mas apenas as atividades que optam pelo suporte a essas telas pequenas podem ser colocado lá.

Os aplicativos podem aceitar declarando um tamanho mínimo suportado que seja menor que oe igual ao tamanho de exibição de destino. Use os atributos de layout de atividade android:minHeight e android:minWidth no AndroidManifest para fazer isso.

Políticas de exibição

O Android 10 separa e move determinadas políticas de exibição da implementação padrão WindowManagerPolicy em PhoneWindowManager para classes por exibição, como:

  • Estado de exibição e rotação
  • Algumas teclas e rastreamento de eventos de movimento
  • UI do sistema e janelas de decoração

No Android 9 (e versões anteriores), a classe PhoneWindowManager cuidava de políticas de exibição, estado e configurações, rotação, rastreamento de quadros de janelas de decoração e muito mais. O Android 10 move a maior parte disso para a classe DisplayPolicy , exceto o rastreamento de rotação, que foi movido para DisplayRotation .

Configurações da janela de exibição

No Android 10, a configuração configurável de janelas por exibição foi expandida para incluir:

  • Modo de janela de exibição padrão
  • Valores de overscan
  • Rotação do usuário e modo de rotação
  • Tamanho forçado, densidade e modo de escala
  • Modo de remoção de conteúdo (quando a exibição é removida)
  • Suporte para decorações de sistema e IME

A classe DisplayWindowSettings contém configurações para essas opções. Eles persistem no disco na partição /data em display_settings.xml sempre que uma configuração é alterada. Para obter detalhes, consulte DisplayWindowSettings.AtomicFileStorage e DisplayWindowSettings#writeSettings() . Os fabricantes de dispositivos podem fornecer valores padrão em display_settings.xml para a configuração de seus dispositivos. No entanto, como o arquivo está armazenado em /data , pode ser necessária lógica adicional para restaurar o arquivo se for apagado por uma limpeza.

Por padrão, o Android 10 usa DisplayInfo#uniqueId como um identificador para uma exibição ao persistir as configurações. uniqueId deve ser preenchido para todas as exibições. Além disso, é estável para exibições físicas e de rede. Também é possível usar a porta de um display físico como identificador, que pode ser definido em DisplayWindowSettings#mIdentifier . Após cada gravação, todas as configurações são gravadas para que seja seguro atualizar a chave usada para uma entrada de exibição no armazenamento. Para obter detalhes, consulte Identificadores de exibição estáticos .

As configurações são mantidas no diretório /data por motivos históricos. Originalmente, eles eram usados ​​para persistir configurações definidas pelo usuário, como rotação da tela.

Identificadores de exibição estáticos

O Android 9 (e versões anteriores) não fornecia identificadores estáveis ​​para exibições na estrutura. Quando um display foi adicionado ao sistema, Display#mDisplayId ou DisplayInfo#displayId foi gerado para esse display incrementando um contador estático. Se o sistema adicionasse e removesse a mesma exibição, o resultado seria um ID diferente.

Se um dispositivo tivesse vários monitores disponíveis na inicialização, os monitores poderiam receber identificadores diferentes, dependendo do tempo. Embora o Android 9 (e versões anteriores) incluísse DisplayInfo#uniqueId , ele não continha informações suficientes para diferenciar os monitores porque os monitores físicos eram identificados como local:0 ou local:1 , para representar o monitor integrado e o externo.

O Android 10 altera DisplayInfo#uniqueId para adicionar um identificador estável e diferenciar entre exibições locais, de rede e virtuais.

Tipo de exibição Formatar
Local
local:<stable-id>
Rede
network:<mac-address>
Virtual
virtual:<package-name-and-name>

Além das atualizações de uniqueId , DisplayInfo.address contém DisplayAddress , um identificador de exibição que é estável durante as reinicializações. No Android 10, DisplayAddress oferece suporte a exibições físicas e de rede. DisplayAddress.Physical contém um ID de exibição estável (o mesmo que em uniqueId ) e pode ser criado com DisplayAddress#fromPhysicalDisplayId() .

O Android 10 também fornece um método conveniente para obter informações de porta ( Physical#getPort() ). Este método pode ser usado na estrutura para identificar exibições estaticamente. Por exemplo, é usado em DisplayWindowSettings ). DisplayAddress.Network contém o endereço MAC e pode ser criado com DisplayAddress#fromMacAddress() .

Essas adições permitem que os fabricantes de dispositivos identifiquem monitores em configurações estáticas de vários monitores e definam diferentes configurações e recursos do sistema usando identificadores de monitores estáticos, como portas para monitores físicos. Esses métodos estão ocultos e devem ser usados ​​apenas em system_server .

Dado um ID de display HWC (que pode ser opaco e nem sempre estável), esse método retorna o número da porta de 8 bits (específico da plataforma) que identifica um conector físico para saída de display, bem como o blob EDID do display. SurfaceFlinger extrai informações do fabricante ou modelo do EDID para gerar IDs de exibição estáveis ​​de 64 bits expostos à estrutura. Se esse método não for compatível ou houver erros, o SurfaceFlinger retornará ao modo MD herdado, onde DisplayInfo#address é nulo e DisplayInfo#uniqueId é codificado, conforme descrito acima.

Para verificar se esse recurso é compatível, execute:

$ dumpsys SurfaceFlinger --display-id
# Example output.
Display 21691504607621632 (HWC display 0): port=0 pnpId=SHP displayName="LQ123P1JX32"
Display 9834494747159041 (HWC display 2): port=1 pnpId=HWP displayName="HP Z24i"
Display 1886279400700944 (HWC display 1): port=2 pnpId=AUS displayName="ASUS MB16AP"

Use mais de dois monitores

No Android 9 (e versões anteriores), SurfaceFlinger e DisplayManagerService presumiram a existência de no máximo dois monitores físicos com IDs 0 e 1 codificados.

A partir do Android 10, o SurfaceFlinger pode aproveitar uma API Hardware Composer (HWC) para gerar IDs de exibição estáveis, o que permite gerenciar um número arbitrário de exibições físicas. Para saber mais, consulte Identificadores de exibição estáticos .

A estrutura pode procurar o token IBinder para uma exibição física por meio de SurfaceControl#getPhysicalDisplayToken depois de obter o ID de exibição de 64 bits de SurfaceControl#getPhysicalDisplayIds ou de um evento hotplug DisplayEventReceiver .

No Android 10 (e versões anteriores), a tela interna principal é TYPE_INTERNAL e todas as telas secundárias são sinalizadas como TYPE_EXTERNAL , independentemente do tipo de conexão. Portanto, monitores internos adicionais são tratados como externos. Como solução alternativa, o código específico do dispositivo pode fazer suposições sobre DisplayAddress.Physical#getPort se o HWC for conhecido e a lógica de alocação de porta for previsível.

Esta limitação foi removida no Android 11 (e superior).

  • No Android 11, a primeira tela relatada durante a inicialização é a tela principal. O tipo de conexão (interna versus externa) é irrelevante. No entanto, continua a ser verdade que o display principal não pode ser desconectado e, na prática, deve ser um display interno. Observe que alguns telefones dobráveis ​​possuem vários monitores internos.
  • Os monitores secundários são categorizados corretamente como Display.TYPE_INTERNAL ou Display.TYPE_EXTERNAL (anteriormente conhecido como Display.TYPE_BUILT_IN e Display.TYPE_HDMI , respectivamente) dependendo do tipo de conexão.

Implementação

No Android 9 e versões anteriores, os monitores são identificados por IDs de 32 bits, onde 0 é o monitor interno, 1 é o monitor externo, [2, INT32_MAX] são monitores virtuais HWC e -1 representa um monitor inválido ou não HWC exibição virtual.

A partir do Android 10, os monitores recebem IDs estáveis ​​e persistentes, o que permite que SurfaceFlinger e DisplayManagerService rastreiem mais de dois monitores e reconheçam monitores vistos anteriormente. Se o HWC oferecer suporte a IComposerClient.getDisplayIdentificationData e fornecer dados de identificação de exibição, o SurfaceFlinger analisará a estrutura EDID e alocará IDs de exibição estáveis ​​de 64 bits para exibições físicas e virtuais de HWC. Os IDs são expressos usando um tipo de opção, onde o valor nulo representa uma exibição inválida ou uma exibição virtual não HWC. Sem suporte HWC, o SurfaceFlinger volta ao comportamento legado com no máximo dois monitores físicos.

Foco por exibição

Para oferecer suporte a diversas fontes de entrada direcionadas a monitores individuais ao mesmo tempo, o Android 10 pode ser configurado para oferecer suporte a diversas janelas focadas, no máximo uma por monitor. Isso se destina apenas a tipos especiais de dispositivos quando vários usuários interagem com o mesmo dispositivo ao mesmo tempo e usam diferentes métodos de entrada ou dispositivos, como o Android Automotive.

É altamente recomendável que esse recurso não seja habilitado para dispositivos comuns, incluindo dispositivos com várias telas ou aqueles usados ​​para experiências semelhantes às de desktop. Isto se deve principalmente a uma preocupação de segurança que pode fazer com que os usuários se perguntem qual janela tem o foco de entrada.

Imagine o usuário que insere informações seguras em um campo de entrada de texto, talvez fazendo login em um aplicativo bancário ou inserindo um texto que contém informações confidenciais. Um aplicativo malicioso pode criar uma exibição virtual fora da tela para executar uma atividade, também com um campo de entrada de texto. Atividades legítimas e maliciosas têm foco e ambas exibem um indicador de entrada ativa (cursor piscando).

No entanto, como a entrada de um teclado (hardware ou software) é inserida apenas na atividade superior (o aplicativo lançado mais recentemente), ao criar uma exibição virtual oculta, um aplicativo malicioso pode capturar a entrada do usuário, mesmo ao usar um teclado de software. na tela do dispositivo principal.

Use com.android.internal.R.bool.config_perDisplayFocusEnabled para definir o foco por exibição.

Compatibilidade

Problema: no Android 9 e versões anteriores, no máximo uma janela do sistema fica em foco por vez.

Solução: No caso raro em que duas janelas do mesmo processo seriam focadas, o sistema fornece foco apenas para a janela que está mais acima na ordem Z. Essa restrição foi removida para aplicativos direcionados ao Android 10, momento em que se espera que eles possam oferecer suporte a várias janelas focadas simultaneamente.

Implementação

WindowManagerService#mPerDisplayFocusEnabled controla a disponibilidade desse recurso. No ActivityManager , ActivityDisplay#getFocusedStack() agora é usado em vez do rastreamento global em uma variável. ActivityDisplay#getFocusedStack() determina o foco com base na ordem Z em vez de armazenar o valor em cache. Isso ocorre para que apenas uma fonte, WindowManager, precise rastrear a ordem Z das atividades.

ActivityStackSupervisor#getTopDisplayFocusedStack() adota uma abordagem semelhante para aqueles casos em que a pilha focada mais alta do sistema deve ser identificada. As pilhas são percorridas de cima para baixo, procurando a primeira pilha elegível.

InputDispatcher agora pode ter múltiplas janelas focadas (uma por exibição). Se um evento de entrada for específico do display, ele será despachado para a janela em foco no display correspondente. Caso contrário, ele será despachado para a janela em foco na exibição em foco, que é a exibição com a qual o usuário interagiu mais recentemente.

Consulte InputDispatcher::mFocusedWindowHandlesByDisplay e InputDispatcher::setFocusedDisplay() . Os aplicativos focados também são atualizados separadamente em InputManagerService por meio de NativeInputManager::setFocusedApplication() .

No WindowManager , as janelas em foco também são rastreadas separadamente. Consulte DisplayContent#mCurrentFocus e DisplayContent#mFocusedApp e os respectivos usos. Os métodos de rastreamento e atualização de foco relacionados foram movidos de WindowManagerService para DisplayContent .