Guia de integração para OEMs

Esta página descreve como processar entradas de seletor rotativo no VHAL, configurar seu build para incluir o serviço de seletor rotativo e como personalizar a experiência do seletor em todos os apps. Para apps OEM pré-instalados, como um iniciador fornecido pelo OEM, consulte Biblioteca da interface do carro (car-ui-library).

VHAL

Um controlador rotativo oferece suporte às seguintes ações:

  • Deslocar para cima, baixo, esquerda e direita.
  • Gire no sentido horário e anti-horário.
  • Pressione o botão central.
  • Pressione o botão Voltar .
  • Pressione o botão home.
  • Pressione outros botões, como "Telefone" e "Mídia".

Consulte hardware/interfaces/automotive/vehicle/2.0/types.hal para conferir a documentação sobre as propriedades do sistema e o int32Values correspondente.

O VHAL precisa processar estas ações:

Piano

Quando o usuário pressiona o controle rotativo para a direita, o VHAL precisa usar a propriedade HW_KEY_INPUT com o int32Values a seguir para enviar um evento ao Android:

  1. ACTION_DOWN
  2. KEYCODE_SYSTEM_NAVIGATION_RIGHT
  3. Selecione a tela de destino.

Quando o usuário libera o controlador rotativo, o VHAL precisa usar a mesma propriedade e o código de tecla com ACTION_UP. Os nudges em outras direções precisam usar os códigos de tecla correspondentes.

Não há códigos de tecla para diagonais, mas o VHAL pode combinar um evento horizontal e vertical para produzir uma diagonal se o hardware oferecer suporte a diagonais. Por exemplo, mover para cima e para a esquerda deve produzir:

  • HW_KEY_INPUT KEYCODE_SYSTEM_NAVIGATION_LEFT ACTION_DOWN
  • HW_KEY_INPUT KEYCODE_SYSTEM_NAVIGATION_UP ACTION_DOWN

Em qualquer ordem (e posteriormente), a liberação do controlador rotativo vai produzir:

  • HW_KEY_INPUT KEYCODE_SYSTEM_NAVIGATION_LEFT ACTION_UP
  • HW_KEY_INPUT KEYCODE_SYSTEM_NAVIGATION_UP ACTION_UP

O usuário pode pressionar o controlador rotativo em uma direção perpendicular antes de soltá-lo. Por exemplo, o seguinte cenário:

Direção perpendicular
Figura 1. Direção perpendicular

Isso vai gerar a seguinte sequência de eventos:

  1. HW_KEY_INPUT KEYCODE_SYSTEM_NAVIGATION_LEFT ACTION_DOWN
  2. HW_KEY_INPUT KEYCODE_SYSTEM_NAVIGATION_UP ACTION_DOWN
  3. HW_KEY_INPUT KEYCODE_SYSTEM_NAVIGATION_LEFT ACTION_UP
  4. HW_KEY_INPUT KEYCODE_SYSTEM_NAVIGATION_UP ACTION_UP

Nenhum evento de repetição será gerado enquanto o seletor giratório estiver em uma direção.

Girar

Quando o usuário gira o controlador rotativo no sentido horário em um dente (clique), o VHAL precisa usar a propriedade HW_ROTARY_INPUT com o seguinte int32Values para enviar um evento ao Android:

  1. ROTARY_INPUT_TYPE_SYSTEM_NAVIGATION
  2. Uma (1) retenção.
  3. Selecione a tela de destino.

O carimbo de data/hora do evento precisa ser definido como o tempo decorrido em nanossegundos.

Uma rotação no sentido anti-horário de uma (1) dente deve gerar o mesmo evento, mas com -1 para o número de dentes.

Se vários pontos de parada de rotação na mesma direção ocorrerem em rápida sucessão, o VHAL vai combinar os pontos de parada em um único evento para não sobrecarregar o sistema com eventos. Nesse caso, o carimbo de data/hora do evento deve ser quando a primeira detenção de rotação ocorreu. O int32Values precisa incluir o número de nanossegundos entre as posições de retenção consecutivas.

Por exemplo, a seguinte sequência de rotações:

  • No momento t0, o usuário girou uma dente no sentido anti-horário.
  • No tempo t0 + 5 ns, o usuário girou uma dente no sentido anti-horário.
  • No tempo t0 + 8 ns, o usuário girou uma dente no sentido anti-horário.

deve gerar este evento:

  • Propriedade: HW_ROTARY_INPUT
  • Carimbo de data/hora: t0
  • int32Values:
    1. ROTARY_INPUT_TYPE_SYSTEM_NAVIGATION
    2. -3 (três engrenagens no sentido anti-horário).
    3. Selecione a tela de destino.
    4. 5 ns entre a primeira e a segunda retenção.
    5. 3 ns entre a segunda e a terceira retenção.

Botão central

Quando o usuário pressiona o botão central, o VHAL precisa usar a propriedade HW_KEY_INPUT com o seguinte int32Values para enviar um evento ao Android:

  1. ACTION_DOWN
  2. KEYCODE_DPAD_CENTER
  3. Selecione a tela de destino.

Quando o usuário libera o controlador rotativo, o VHAL precisa usar a mesma propriedade e o código de tecla com ACTION_UP.

Não gere eventos de repetição quando o botão central for pressionado.

Botão "Voltar"

Quando o usuário pressiona o botão "Voltar", o VHAL precisa usar a propriedade HW_KEY_INPUT com o seguinte int32Values para enviar um evento ao Android:

  1. ACTION_DOWN
  2. KEYCODE_BACK
  3. Selecione a tela de destino.

Quando o usuário libera o controlador rotativo, o VHAL precisa usar a mesma propriedade e o código de tecla com ACTION_UP.

Nenhum evento de repetição será gerado enquanto o botão central estiver pressionado.

Botão "Início"

Processe o botão "Home" como faria com o botão "Voltar", mas com KEYCODE_HOME em vez de KEYCODE_BACK.

Outros botões

Se o controle rotativo incluir outros botões, o VHAL poderá processá-los da maneira que o OEM preferir, já que eles não são considerados parte do seletor giratório do ponto de vista do Android. Eles geralmente são tratados como os botões "Voltar" e "Início", mas com códigos de tecla diferentes. Por exemplo, KEYCODE_CALL ou KEYCODE_MUSIC.

Configuração do build

A navegação por seletor giratório é fornecida por um serviço de acessibilidade chamado RotaryService. Para incluir esse serviço na imagem do sistema do dispositivo, adicione a seguinte linha ao makefile:

PRODUCT_PACKAGES += CarRotaryController

Também é recomendável incluir os seguintes pacotes em builds de depuração:

O serviço rotativo é ativado automaticamente quando o dispositivo é inicializado e quando ocorre uma troca de usuário. Isso garante que o usuário possa usar o controlador rotativo durante a configuração.

Se você usar o mesmo build para carros com e sem um controle giratório, adicione CarRotaryController, conforme mostrado acima, para que o código necessário seja incluído no build. Para impedir que o serviço de rotatória seja ativado em carros não rotativos, crie um RRO estático para sobrepor o recurso de string rotaryService em packages/services/Car/service com uma string vazia. Você vai usar o mesmo build, mas terá configurações de produto separadas para dispositivos rotativos e não rotativos. Somente o último inclui a sobreposição.

Personalização

Os OEMs podem personalizar a lógica de busca de foco, o destaque de foco e alguns itens adicionais usando sobreposições de recursos nos seguintes locais:

  • car-ui-library está localizado em packages/apps/Car/libs/car-ui-lib
  • RotaryService está localizado em packages/apps/Car/RotaryController
  • Core está localizado em frameworks/base/core

Histórico de sugestões

O OEM pode configurar se cada um dos dois tipos de histórico de sugestões está ativado ou não e, se sim, o tamanho do cache e a política de expiração. Isso é feito substituindo vários recursos da car-ui-library.

Cache do histórico do foco

(Android 11 QPR3, Android 11 para carros, Android 12)
Esse cache por FocusArea armazena a visualização mais recente focada no FocusArea para que ela possa ser focada ao voltar para o FocusArea. Esse cache pode ser configurado sobrepondo os seguintes recursos da car-ui-library:

  • car_ui_focus_history_cache_type:
    1. O cache está desativado.
    2. O cache expira após algum tempo (confira abaixo).
    3. O cache nunca expira.
  • car_ui_focus_history_expiration_period_ms: quantos milissegundos antes do cache expirar se o tipo de cache for definido como dois (2) (consulte acima).

Cache do histórico do FocusArea

(Android 11 QPR3, Android 11 Car, Android 12)
Esse cache armazena um histórico de sugestões para que a sugestão na direção oposta possa retornar o foco para o mesmo FocusArea. Esse cache pode ser configurado sobrepondo os seguintes recursos da car-ui-library:

  • car_ui_focus_area_history_cache_type:
    1. O cache está desativado.
    2. O cache expira após algum tempo (confira abaixo).
    3. O cache nunca expira.
  • car_ui_focus_area_history_expiration_period_ms: quantos milissegundos antes do cache expirar se o tipo de cache estiver definido como 2 (consulte acima).
  • car_ui_clear_focus_area_history_when_rotating: se o cache será anulado quando o usuário girar o controle.

Rotação

(Android 11 QPR3, Android 11 Car, Android 12)
O OEM pode substituir dois recursos de inteiros no RotaryService para especificar se há aceleração, como a aceleração do mouse, para a rotação:

  • rotation_acceleration_3x_ms: intervalo de tempo (em milissegundos) usado para decidir se o Google deve acelerar a rotação do controle para uma detenção de rotação. Se o intervalo entre esse dente e o dente de rotação anterior for menor que esse valor, ele será tratado como três dentes de rotação. Defina como 2147483647 para desativar a aceleração 3×.
  • rotation_acceleration_2x_ms: semelhante a rotation_acceleration_3x_ms. Usado para aceleração 2×. Defina como 2147483647 para desativar a aceleração 2x.

A aceleração funciona melhor quando há carimbos de data/hora individuais para cada dente de rotação, conforme requerido pelo VHAL. Se eles não estiverem disponíveis, o RotaryService vai presumir que os detents de rotação estão espaçados de maneira uniforme.

/**
     * Property to feed H/W rotary events to android
     *
     * int32Values[0] : RotaryInputType identifying which rotary knob rotated
     * int32Values[1] : number of detents (clicks), positive for clockwise,
     *                  negative for counterclockwise
     * int32Values[2] : target display defined in VehicleDisplay. Events not
     *                  tied to specific display must be sent to
     *                  VehicleDisplay#MAIN.
     * int32values[3 .. 3 + abs(number of detents) - 2]:
     *                  nanosecond deltas between pairs of consecutive detents,
     *                  if the number of detents is > 1 or < -1
     *
     * VehiclePropValue.timestamp: when the rotation occurred. If the number of
     *                             detents is > 1 or < -1, this is when the
     *                             first detent of rotation occurred.
     *
     * @change_mode VehiclePropertyChangeMode:ON_CHANGE
     * @data_enum RotaryInputType
     * @access VehiclePropertyAccess:READ
     */
    HW_ROTARY_INPUT = (
        0x0A20
        | VehiclePropertyGroup:SYSTEM
        | VehiclePropertyType:INT32_VEC
        | VehicleArea:GLOBAL),

Destaque de foco

O OEM pode substituir o destaque de foco padrão no framework do Android e vários recursos de destaque de foco na biblioteca car-ui-library.

Destaque de foco padrão

O framework do Android fornece um destaque de foco padrão pelo atributo selectableItemBackground. Em Theme.DeviceDefault, esse atributo se refere a item_background.xml em Core. O OEM pode sobrepor item_background.xml para mudar o drawable de destaque de foco padrão.

Esse drawable normalmente precisa ser um StateListDrawable, que ajusta o plano de fundo com base em diferentes combinações de estados, incluindo android:state_focused e android:state_pressed. Quando o usuário usa o controle giratório para focar uma visualização, android:state_focused será true, mas android:state_pressed será false. Se o usuário pressionar o botão central no controle rotativo, android:state_focused e android:state_pressed serão true enquanto o usuário mantiver o botão pressionado. Quando o usuário libera o botão, apenas android:state_focused permanece true.

A car-ui-library usa um tema derivado de Theme.DeviceDefault. Como resultado, essa sobreposição afeta os apps que usam essa biblioteca e os que usam qualquer tema derivado de Theme.DeviceDefault. Ele não vai afetar apps que usam um tema não relacionado, como Theme.Material.

Foco nos recursos de destaque na car-ui-library

O OEM pode substituir vários recursos da biblioteca car-ui-library para controlar a aparência do destaque de foco em visualizações com um destaque de foco não retangular (como redondo ou em forma de pílula) e em apps que usam um tema que não é derivado de Theme.DeviceDefault. Esses recursos precisam ser sobrepostos para que o destaque de foco seja consistente com o drawable padrão.

(Android 11 QPR3, Android 11 para carros, Android 12)
Os recursos a seguir são usados para indicar quando uma visualização está em foco, mas não pressionada:

  • car_ui_rotary_focus_fill_color: cor de preenchimento.
  • car_ui_rotary_focus_stroke_color: cor do contorno.
  • car_ui_rotary_focus_stroke_width: espessura do contorno.

(Android 11 QPR3, Android 11 Car, Android 12)
Os recursos a seguir são usados para indicar quando uma visualização está com foco e pressionada:

  • car_ui_rotary_focus_pressed_fill_color: cor de preenchimento.
  • car_ui_rotary_focus_pressed_stroke_color: cor do contorno.
  • car_ui_rotary_focus_pressed_stroke_width: espessura do contorno.

Às vezes, um botão recebe uma cor de plano de fundo sólida para chamar a atenção do usuário, como no exemplo mostrado. Isso pode dificultar a visualização do destaque de foco.

Botão com plano de fundo sólido
Figura 2. Botão com plano de fundo sólido

Nessa situação, o desenvolvedor pode especificar um destaque de foco personalizado usando cores secundárias:
  • (Android 11 QPR3, Android 11 Car, Android 12)
    car_ui_rotary_focus_fill_secondary_color
    car_ui_rotary_focus_stroke_secondary_color
  • (Android 12)
    car_ui_rotary_focus_pressed_fill_secondary_color
    car_ui_rotary_focus_pressed_stroke_secondary_color

Qualquer uma das cores pode ser transparente, e qualquer dimensão pode ser zero se, por exemplo, você quiser apenas um preenchimento ou apenas um contorno.

Destaque de FocusArea

(Android 11 QPR3, Android 11 Car, Android 12)
FocusArea pode desenhar dois tipos de destaque quando um dos descendentes está em foco. Ambos podem ser usados em conjunto, se necessário. Esse recurso fica desativado por padrão no AOSP, mas pode ser ativado substituindo os recursos da car-ui-library:

  • car_ui_enable_focus_area_foreground_highlight: desenha um destaque sobre FocusArea e seus descendentes. No AOSP, esse drawable é um contorno ao redor do FocusArea. Os OEMs podem substituir o drawable car_ui_focus_area_foreground_highlight.
  • car_ui_enable_focus_area_background_highlight: desenha um destaque na parte de cima do FocusArea, mas atrás dos descendentes. No AOSP, esse drawable é um preenchimento sólido. Os OEMs podem substituir o car_ui_focus_area_background_highlight drawable.

Editores do método de entrada

Os editores de método de entrada (IME) são métodos de entrada. Por exemplo, um teclado na tela.

(Android 11 QPR3, Android 11 Car, Android 12)
O OEM precisa sobrepor o recurso de string default_touch_input_method no RotaryService para especificar o ComponentName do IME baseado em toque. Por exemplo, se o OEM usa o IME fornecido com o Android Automotive, ele precisa especificar com.google.android.apps.automotive.inputmethod/.InputMethodService.

(Android 11 QPR3, Android 11 Car, Android 12)
Se o OEM tiver criado um IME especificamente para o seletor giratório, ele precisará especificar o ComponentName no recurso rotary_input_method. Se esse recurso for sobreposto, o IME especificado será usado sempre que o usuário interagir com a unidade principal pelo botão de centralização, rotação e empurrão do controle giratório. Quando o usuário toca na tela, o IME anterior é usado. O botão "Voltar" (e outros botões no controle giratório) não afetam a seleção do IME. Se esse recurso não for sobreposto, nenhuma troca de IME ocorrerá. O Carboard não oferece suporte a seletor giratório. Portanto, o usuário não pode inserir texto pelo controle se o OEM não tiver fornecido um IME giratório.

RotaryIME é um IME rotativo de demonstração. Embora seja básico, ele é suficiente para testar a troca automática de IME descrita acima. O código-fonte de RotaryIME pode ser encontrado em packages/apps/Car/tests/RotaryIME/.

Alertas fora da tela

Por padrão, quando o usuário tenta sair da borda da tela, nada acontece. O OEM pode configurar o que deve ocorrer em cada uma das quatro direções especificando qualquer combinação de:

  1. Uma ação global definida por AccessibilityService. Por exemplo, GLOBAL_ACTION_BACK.
  2. Um código de chave, como KEYCODE_BACK.
  3. Uma intent para iniciar uma atividade representada como um URL.

(Android 11 QPR3, Android 11 Car, Android 12)
Eles são especificados sobrepondo os seguintes recursos de matriz no RotaryService:

  • off_screen_nudge_global_actions: matriz de ações globais a serem realizadas quando o usuário empurrar para cima, para baixo, para a esquerda ou para a direita fora da borda da tela. Nenhuma ação global será realizada se o elemento relevante dessa matriz for -1.
  • off_screen_nudge_key_codes: matriz de códigos de teclas de eventos de clique para injetar quando o usuário empurra para cima, para baixo, para a esquerda ou para a direita fora da borda da tela. Nenhum evento será injetado se o elemento relevante dessa matriz for 0 (KEYCODE_UNKNOWN).
  • off_screen_nudge_intents: matriz de intents para iniciar uma atividade quando o usuário empurra para cima, para baixo, para a esquerda ou para a direita fora da borda da tela. Nenhuma atividade será iniciada se o elemento relevante dessa matriz estiver vazio.

Outras configurações

É necessário sobrepor os seguintes recursos RotaryService:

  • (Android 11 QPR3, Android 11 Car, Android 12)
    config_showHeadsUpNotificationOnBottom: valor booleano para representar se as notificações de alerta precisam ser mostradas na parte de baixo, e não na parte de cima. Ele precisa ter o mesmo valor que o recurso booleano config_showHeadsUpNotificationOnBottom em frameworks/base/packages/CarSystemUI/res/values/config.xml.
  • (Android 11 QPR3, Android 11 Car, Android 12)
    notification_headsup_card_margin_horizontal: margem esquerda e direita para janela de notificação de aviso. Ele precisa ter o mesmo valor do recurso de dimen notification_headsup_card_margin_horizontal em packages/apps/Car/Notification/res/values/dimens.xml.
  • (Android 12)
    excluded_application_overlay_window_titles: uma matriz de títulos de janelas que não devem ser consideradas janelas de sobreposição. Isso precisa incluir títulos de janelas de apps que representam TaskViews ou TaskDisplayAreas. Por padrão, essa lista contém apenas "Maps".

É possível sobrepor o seguinte recurso RotaryService:

  • (Android 11 QPR3, Android 11 Car, Android 12)
    long_press_ms: valor inteiro para representar quantos milissegundos o botão central precisa ser pressionado para acionar um toque longo. Zero indica que o tempo limite padrão de pressionamento longo do sistema precisa ser usado. Esse é o valor padrão.