Compatibilidade de entrada para Chromebooks

Em dispositivos Chrome OS, muitos usuários interagem com apps usando um teclado, mouse, trackpad, stylus ou gamepad. Embora esses dispositivos de entrada também sejam usados em smartphones Android, eles não são tão comuns e geralmente são ignorados por desenvolvedores.

Os desenvolvedores que quiserem que o app funcione bem com entradas no Chrome OS e outros dispositivos Android compatíveis com telas grandes precisam analisar as seguintes otimizações:

  • Adicione e teste a compatibilidade básica do teclado, como a navegação pelo teclado por meio das teclas de seta e de Tab, Enter para confirmar a entrada de texto e Espaço para reproduzir/pausar em apps de mídia.

  • Adicione atalhos de teclado padrão, quando for o caso, por exemplo, Ctrl + Z para desfazer, Ctrl + S para salvar.

  • Teste interações básicas do mouse com o botão direito para acessar o menu de contexto, alterações de ícone ao passar o cursor e eventos de rolagem do mouse/trackpad em visualizações personalizadas.

  • Teste dispositivos de entrada específicos do app, como a stylus para apps de desenho, controles de jogos e controles de MIDI para apps de música.

  • Considere a compatibilidade avançada de entrada que pode destacar o app em ambientes de computador: touchpad como um modificador para apps de DJ, captura de mouse para jogos e atalhos de teclado adicionais para usuários avançados.

Teclado

A maneira como seu app responde à entrada do teclado contribui para uma boa experiência em computadores. Existem três tipos de entrada de teclado: navegação, pressionamentos de tecla e atalhos.

A navegação pelo teclado raramente é implementada em apps voltados para o toque, mas os usuários esperam isso quando estão usando um app e estão com as mãos no teclado. Também pode ser essencial para usuários com necessidades de acessibilidade em smartphones e computadores.

Para muitos apps, a navegação simples pelas teclas de seta e Tab é o suficiente e é gerenciada automaticamente pelo framework do Android. Por exemplo, uma visualização de um Button é focalizável por padrão e a navegação pelo teclado geralmente funciona sem nenhum código adicional. Para ativar a navegação pelo teclado para visualizações que não são focalizáveis por padrão, os desenvolvedores precisam marcá-las como focalizáveis. Isso pode ser feito de maneira programática ou em XML, conforme mostrado abaixo. Consulte a documentação de Processamento de foco para mais informações.

Kotlin

yourView.isFocusable = true

Java

yourView.setFocusable(true);

Como alternativa, você pode definir o atributo focusable no arquivo de layout:

android:focusable="true"

Depois que o foco é ativado, o framework do Android cria um mapeamento de navegação para todas as visualizações focalizáveis com base na posição delas. Isso geralmente funciona conforme o esperado, e não é necessário realizar qualquer outra ação. Quando o mapeamento padrão não está correto para as necessidades de um app, ele pode ser substituído da seguinte maneira:

Kotlin

// Arrow keys
yourView.nextFocusLeftId = R.id.view_to_left
yourView.nextFocusRightId = R.id.view_to_right
yourView.nextFocusTopId = R.id.view_above
yourView.nextFocusBottomId = R.id.view_below

// Tab key
yourView.nextFocusForwardId = R.id.next_view

Java

// Arrow keys
yourView.setNextFocusLeftId(R.id.view_to_left);
yourView.setNextFocusRightId(R.id.view_to_left);
yourView.setNextFocusTopId(R.id.view_to_left);
yourView.setNextFocusBottomId(R.id.view_to_left);

// Tab key
yourView.setNextFocusForwardId(R.id.next_view);

É recomendável tentar acessar todas as funcionalidades do app antes de cada lançamento, usando apenas o teclado. Facilite o acesso às ações mais comuns sem o mouse ou a entrada de toque.

Lembre-se de que a compatibilidade com o teclado pode ser essencial para usuários com necessidades de acessibilidade.

Pressionamentos de tecla

Para entrada de texto que seria processada por um teclado virtual na tela (IME) como um EditText, os apps devem se comportar como esperado no Chrome OS sem trabalho adicional do desenvolvedor. Para pressionamentos de tecla que não podem ser previstos pelo framework, os apps precisarão lidar com o comportamento. Isso ocorre especialmente em apps com visualizações personalizadas.

Alguns exemplos são apps de chat que usam a tecla Enter para enviar uma mensagem, apps de mídia que iniciam/interrompem a reprodução com a tecla de espaço e jogos que controlam o movimento com as teclas w, a, s e d.

A maioria dos apps modifica o evento onKeyUp e adiciona o comportamento esperado para cada código de tecla recebido, conforme mostrado abaixo.

Kotlin

override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean {
    return when (keyCode) {
        KeyEvent.KEYCODE_ENTER -> {
            sendChatMessage()
            true
        }
        KeyEvent.KEYCODE_SPACE -> {
            playOrPauseMedia()
            true
        }
        else -> super.onKeyUp(keyCode, event)
    }
}

Java

@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_ENTER) {
        sendMessage();
        return true;
    } else if (KeyEvent.KEYCODE_SPACE){
        playOrPauseMedia();
        return true;
    } else {
        return super.onKeyUp(keyCode, event);
    }
}

O uso de onKeyUp impede que os apps recebam vários eventos se uma chave for mantida pressionada ou liberada lentamente. Jogos e apps que precisam da funcionalidade de manter as teclas de teclado pressionadas podem procurar o evento onKeyDown.

Ao adicionar compatibilidade com teclado, siga a documentação de processamento do teclado do Android.

Atalhos

Os atalhos comuns Ctrl, Alt e Shift são esperados em ambientes de computador. Se um app não os implementar, a experiência dos usuários poderá ser frustrante e interrompida. Além disso, atalhos para tarefas específicas usadas com frequência agradam usuários avançados. Os atalhos facilitam o uso e a diferenciação de apps que não têm atalhos.

Alguns atalhos comuns incluem Ctrl + S (salvar), Ctrl + Z (desfazer) e Ctrl + Shift + Z (refazer). Para ver um exemplo de alguns atalhos mais avançados, consulte a lista de teclas de atalho do player de mídia VLC (link em inglês).

Os atalhos podem ser implementados usando dispatchKeyShortcutEvent. Isso intercepta todas as combinações de tecla meta (Alt, Ctrl e Shift) para um determinado código de tecla. Para verificar se há uma tecla meta específica, use KeyEvent.isCtrlPressed(), KeyEvent.isShiftPressed(), KeyEvent.isAltPressed() ou KeyEvent.hasModifiers().

A separação do código de atalho de outro processo de pressionamento de tecla (como onKeyUp ou onKeyDown) pode facilitar a manutenção do código e mantém a aceitação padrão das teclas meta sem ter que implementar manualmente as verificações de tecla meta em todos os casos. Permitir todas as combinações de tecla meta também pode ser mais conveniente para usuários acostumados com diferentes layouts de teclado e sistemas operacionais.

Kotlin

override fun dispatchKeyShortcutEvent(event: KeyEvent): Boolean {
  return when (event.keyCode) {
    KeyEvent.KEYCODE_O -> {
      openFile() // Ctrl+O, Shift+O, Alt+O
      true
    }
    KeyEvent.KEYCODE_Z-> {
      if (event.isCtrlPressed) {
        if (event.isShiftPressed) {
          redoLastAction() // Ctrl+Shift+Z pressed
          true
        } else {
          undoLastAction() // Ctrl+Z pressed
          true
        }
      }
    }
    else -> {
      return super.dispatchKeyShortcutEvent(event)
    }
  }
}

Java

@Override
public boolean dispatchKeyShortcutEvent(KeyEvent event) {
  if (event.getKeyCode() == KeyEvent.KEYCODE_O) {
      openFile(); // Ctrl+O, Shift+O, Alt+O
      return true;
  } else if(event.getKeyCode() == KeyEvent.KEYCODE_Z) {
      if (event.isCtrlPressed()) {
          if (event.isShiftPressed()) {
              redoLastAction();
              return true;
          }
          else {
              undoLastAction();
              return true;
          }
      }
  }
  return super.dispatchKeyShortcutEvent(event);
}

Você também pode implementar atalhos em onKeyUp pela verificação de KeyEvent.isCtrlPressed(), KeyEvent.isShiftPressed() ou KeyEvent.isAltPressed() como explicado acima. Isso pode ser mais fácil de manter se o comportamento meta for mais uma modificação em um comportamento do app do que um atalho. Por exemplo, quando W significa "avançar" e Shift + W significa "avançar correndo".

Kotlin

override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean {
  return when(keyCode) {
    KeyEvent.KEYCODE_W-> {
      if (event.isShiftPressed) {
        if (event.isCtrlPressed) {
          flyForward() // Ctrl+Shift+W pressed
          true
        } else {
          runForward() // Shift+W pressed
          true
        }
      } else {
        walkForward() // W pressed
        true
      }
    }
    else -> super.onKeyUp(keyCode, event)
  }
}

Java

@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_W) {
        if (event.isShiftPressed()) {
            if (event.isCtrlPressed()) {
                flyForward(); // Ctrl+Shift+W pressed
                return true;
            } else {
                runForward(); // Shift+W pressed
                return true;
            }
        } else {
            walkForward();
            return true;
        }
    }
    return super.onKeyUp(keyCode, event);
}

Compatibilidade com mouse e touchpad

O Chrome OS processa automaticamente a maioria dos eventos do mouse e do trackpad para que eles funcionem como eventos de toque em um smartphone Android. Isso inclui rolagem com dois dedos no touchpad/roda do mouse. Geralmente, a maioria dos apps geralmente precisa processar apenas três eventos voltados para computadores: clicar com o botão direito do mouse, passar o cursor e arrastar e soltar.

Clicar com o botão direito

Todas as ações que fazem com que um app mostre um menu de contexto, como manter pressionado um item de lista, também precisam reagir a eventos de clique com o botão direito do mouse. Para gerenciar eventos de clique com o botão direito do mouse, os apps precisam registrar um View.OnContextClickListener. Para detalhes sobre como criar um menu de contexto, consulte a documentação do menu de contexto do Android

Kotlin

yourView.setOnContextClickListener {
  showContextMenu()
  true
}

Java

yourView.setOnContextClickListener(v -> {
    showContextMenu();
    return true;
});

Passar cursor

Os desenvolvedores podem fazer com que seus layouts de apps pareçam bem-acabados e fáceis de usar pelo processamento de eventos de passar o cursor. Isso é especialmente verdadeiro para visualizações personalizadas. Os dois exemplos mais comuns são:

  • indicar aos usuários se um elemento tem comportamento interativo, clicável ou editável, mudando o ícone do ponteiro do mouse;
  • adicionar feedback visual a itens em uma grade ou lista grande quando o ponteiro do mouse estiver sobre eles.

Kotlin

// Change the icon to a "hand" pointer on hover,
// Highlight the view by changing the background.
yourView.setOnHoverListener { view, _ ->
  addVisualHighlighting(true)
  view.pointerIcon =
    PointerIcon.getSystemIcon(view.context,
    PointerIcon.TYPE_HAND)
  false // listener did not consume the event.
}

Java

yourView.setOnHoverListener((view, event) -> {
    addVisualHighlighting(true);
    view.setPointerIcon(PointerIcon
            .getSystemIcon(view.getContext(), PointerIcon.TYPE_HAND));
    return true;
});

Arrastar e soltar

Em um ambiente de várias janelas, os usuários esperam poder arrastar e soltar itens entre apps. Isso é válido para dispositivos Chrome OS, bem como tablets, smartphones e dispositivos dobráveis no modo de tela dividida.

Os desenvolvedores precisam considerar se os usuários arrastarão itens para o app. Alguns exemplos comuns incluem: editores de fotos provavelmente receberão fotos, players de áudio receberão arquivos de áudio e programas de desenho receberão imagens.

Para adicionar compatibilidade ao recurso de arrastar e soltar, siga a documentação do recurso de arrastar e soltar do Android e confira esta postagem do blog do Chrome OS (em inglês).

Considerações especiais para o Chrome OS

  • Para processar arquivos no app Arquivos do Chrome OS, procure o tipo MIME application/x-arc-uri-list.
  • Lembre-se de solicitar permissão usando requestDragAndDropPermissions para acessar itens arrastados de fora do app.
  • Um item precisa ter a sinalização View.DRAG_FLAG_GLOBAL para ser arrastado para outros apps.

Compatibilidade avançada com ponteiro

Os apps que fazem o processamento avançado da entrada do mouse e do touchpad precisam seguir a documentação do Android para View.onGenericMotionEvent() e usar MotionEvent.getSource() para distinguir entre SOURCE_MOUSE e SOURCE_TOUCHSCREEN.

Examine o MotionEvent para implementar o comportamento necessário:

  • O movimento gera eventos de ACTION_HOVER_MOVE.
  • Os botões geram eventos de ACTION_BUTTON_PRESS e ACTION_BUTTON_RELEASE. Também é possível verificar o estado atual de todos os botões do mouse/trackpad usando getButtonState().
  • A rolagem da roda do mouse gera eventos ACTION_SCROLL.

Stylus

Muitos Chromebooks vêm com uma stylus, e os apps Android gerenciam isso como entrada touchscreen. Alguns dispositivos também podem ter uma prancheta de desenho USB ou Bluetooth, como o Wacom Intuos (em inglês). Os apps para Android podem receber entradas por Bluetooth, mas não funcionam com a entrada USB.

Um evento da stylus é relatado como um evento de touchscreen via View.onTouchEvent() ou View.onGenericMotionEvent() e contém um MotionEvent.getSource() do tipo SOURCE_STYLUS. O MotionEvent também conterá dados adicionais:

Pontos históricos

O Android agrupa eventos de entrada e os envia uma vez por frame. Uma stylus pode relatar eventos em frequências muito maiores do que a tela. Ao criar apps de desenho, é importante verificar eventos que podem estar no passado recente usando as APIs getHistorical:

  • MotionEvent.getHistoricalX()
  • MotionEvent.getHistoricalY()
  • MotionEvent.getHistoricalPressure()
  • MotionEvent.getHistoricalAxisValue()

Rejeição da palma da mão

O Chrome OS tenta reconhecer quando a palma da mão de um usuário está apoiada na tela touchscreen. No entanto, isso nem sempre é possível. Às vezes, um evento de toque pode ser relatado ao app antes que o SO o reconheça como uma palma. Nesse caso, os toques serão cancelados relatando um evento de ACTION_CANCEL.

Esse evento informa ao app que determinados toques são inválidos e que é necessário desfazer todas as interações causadas por eles. Por exemplo, um app de desenho pode desenhar temporariamente novas linhas assim que elas forem recebidas para fornecer a menor latência, mas só as confirmará permanentemente na tela quando a série de toques estiver claramente concluída. Se os eventos de toque forem cancelados, as linhas temporárias poderão ser facilmente apagadas.

Apps de anotações

O Chrome OS tem uma intent especial que exibe apps de anotações registrados para os usuários. Para registrar um app como de anotações, adicione o seguinte ao manifesto do Android:

  <intent-filter>
    <action android:name="org.chromium.arc.intent.action.CREATE_NOTE" />
    <category android:name="android.intent.category.DEFAULT" />
  </intent-filter>

Quando um app é registrado, o usuário pode selecioná-lo como o app de anotações padrão. Quando uma nova anotação for solicitada, o app precisará criar uma nota vazia pronta para entrada da stylus. Quando o usuário quiser anotar uma imagem (como uma captura de tela ou imagem salva), o app será iniciado com ClipData contendo um ou mais itens com URIs content://. O app precisa criar uma nota que use a primeira imagem anexada como uma imagem de plano de fundo e entrar em um modo em que o usuário pode desenhar com uma stylus.

Testar intents de anotações sem stylus

Para testar se um app responde corretamente a intents de anotação sem uma stylus ativa, use o seguinte método para exibir as opções de anotação:

  1. Alterne para o modo de desenvolvedor e torne o dispositivo gravável.
  2. Pressione Ctrl + Alt + F2 para abrir um terminal.
  3. Execute o comando sudo vi /etc/chrome_dev.conf.
  4. Pressione i para editar e adicionar --ash-enable-palette a uma nova linha no final do arquivo.
  5. Salve pressionando Esc e digitando :, w, q e pressionando Enter.
  6. Pressione Ctrl + Alt + F1 para retornar à IU normal do Chrome OS.
  7. Saia e faça login novamente.

Agora você verá um menu da stylus na estante:

  • Toque no botão da stylus na estante e escolha Nova nota. Isso deve abrir uma nota de desenho em branco.
  • Faça uma captura de tela. Na estante, selecione botão de stylus > Capturar tela ou faça o download de uma imagem. Deve haver a opção "Anotar imagem" na notificação. Isso deve iniciar o app com a imagem pronta para ser anotada.

Controles de jogos

Os Chromebooks são compatíveis com até quatro controles de jogos. Os desenvolvedores precisam usar as APIs de controle de jogos padrão do Android para processar esses controles.

Os botões são mapeados para valores comuns após um mapeamento comum. Infelizmente, nem todos os fabricantes de controles de jogos seguem as mesmas convenções de mapeamento. Você pode fornecer uma experiência muito melhor se permitir que os usuários selecionem mapeamentos de controles mais usados.

Modo de tradução de entrada

O Chrome OS ativa um modo de tradução de entrada por padrão. Para a maioria dos apps Android, esse modo os ajuda a funcionar como esperado em um ambiente de computador. Alguns exemplos incluem ativar automaticamente a rolagem com dois dedos no touchpad, a rolagem da roda do mouse e o mapeamento de coordenadas de exibição brutas para coordenadas de janela. Geralmente, os desenvolvedores de apps não precisam implementar nenhum desses comportamentos.

Se um app implementar um comportamento de entrada personalizado, por exemplo, definir uma ação personalizada de gesto de pinça com dois dedos ou se essas traduções de entrada não fornecerem os eventos de entrada esperados pelo app, você poderá desativar o modo de tradução de entrada adicionando a seguinte tag ao manifesto do Android:

<uses-feature
  android:name="android.hardware.type.pc"
  android:required="false" />