Desenvolver um serviço de acessibilidade para Android

1. Introdução

Os serviços de acessibilidade são um recurso do framework do Android projetado para fornecer feedback de navegação alternativa ao usuário em nome de aplicativos instalados em dispositivos Android. Um serviço de acessibilidade pode se comunicar com o usuário em nome do aplicativo, por exemplo, convertendo texto em voz ou fornecendo retorno tátil quando o usuário passa o mouse sobre uma área importante da tela. Este codelab mostra como criar um serviço de acessibilidade muito simples.

O que é um serviço de acessibilidade?

Um serviço de acessibilidade ajuda usuários com deficiência no uso de dispositivos e apps Android. Ele é um serviço privilegiado de longa duração que ajuda os usuários a processar informações na tela e permite uma interação significativa com um dispositivo.

Exemplos de serviços comuns de acessibilidade

  • Acesso com interruptor: permite que os usuários do Android com limitações de mobilidade interajam com dispositivos usando um ou mais interruptores.
  • Voice Access (Beta): permite que usuários do Android com limitações de mobilidade controlem um dispositivo com comandos falados.
  • Talkback: um leitor de tela normalmente usado por usuários cegos ou com deficiência visual.

Como criar um serviço de acessibilidade

Embora o Google ofereça serviços como o acesso com interruptor, o Voice Access e o Talkback para usuários do Android, esses serviços não podem atender a todas as pessoas com deficiência. Como muitos usuários com deficiências têm necessidades específicas, as APIs do Android para a criação de serviços de acessibilidade são abertas, e os desenvolvedores podem criar esses serviços e distribuí-los pela Play Store.

O que você vai criar

Neste codelab, você desenvolverá um serviço simples que realiza algumas tarefas úteis usando a API de acessibilidade. Se você sabe programar um app Android básico, pode desenvolver um serviço semelhante.

A API de acessibilidade é poderosa: o código do serviço que você criará está contido em apenas quatro arquivos e usa aproximadamente 200 linhas de código.

O usuário final

Você criará um serviço para um usuário hipotético com as seguintes características:

  • O usuário tem dificuldade para alcançar os botões laterais em um dispositivo.
  • O usuário tem dificuldade em rolar ou deslizar.

Detalhes do serviço

O serviço vai sobrepor uma barra de ações global na tela. O usuário pode tocar em botões nessa barra para realizar as seguintes ações:

  1. Desligue o dispositivo sem encostar no botão liga/desliga na lateral do smartphone.
  2. Ajuste o volume sem tocar nos botões de volume na lateral do smartphone.
  3. Realize ações de rolagem sem precisar rolar a tela.
  4. Deslize sem precisar de um gesto.

O que é necessário

Este codelab presume que você vai usar o seguinte:

  1. Um computador com o Android Studio.
  2. Um terminal para executar comandos simples do shell.
  3. Um dispositivo com Android 7.0 (Nougat) conectado ao computador que você usará para o desenvolvimento.

Vamos começar!

2. Etapas da configuração

Usando o terminal, crie um diretório em que você vai trabalhar. Mude para este diretório.

Fazer o download do código

Você pode clonar o repositório que contém o código deste codelab:

git clone https://github.com/android/codelab-android-accessibility.git

O repositório contém vários projetos do Android Studio. No Android Studio, abra GlobalActionBarService.

Inicie o Android Studio clicando no ícone dele:

Logotipo usado para iniciar o Android Studio.

Selecione a opção Import project (Eclipse ADT, Gradle etc.):

Tela de boas-vindas do Android Studio.

Navegue até o local em que você clonou a origem e selecione GlobalActionBarService.

Em seguida, usando um terminal, mude para o diretório raiz.

3. Noções básicas sobre o código inicial

Explore o projeto que você abriu.

O esqueleto básico do serviço de acessibilidade já foi criado para você. Todo o código que você vai criar neste codelab está restrito aos quatro arquivos abaixo:

  1. app/src/main/AndroidManifest.xml
  2. app/src/main/res/layout/action_bar.xml
  3. app/src/main/res/xml/global_action_bar_service.xml
  4. app/src/main/java/com/example/android/globalactionbarservice/GlobalActionBarService.java

Veja a seguir um tutorial do conteúdo de cada arquivo.

AndroidManifest.xml

As informações sobre o serviço de acessibilidade são declaradas no manifesto:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
   package="com.example.android.globalactionbarservice">

   <application>
       <service
           android:name=".GlobalActionBarService"
           android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
           android:exported="true">
           <intent-filter>
               <action android:name="android.accessibilityservice.AccessibilityService" />
           </intent-filter>
           <meta-data
               android:name="android.accessibilityservice"
               android:resource="@xml/global_action_bar_service" />
       </service>
   </application>
</manifest>

Estes três itens obrigatórios são declarados em AndroidManifest.xml:

  1. Permissão para vincular a um serviço de acessibilidade:
<service
    ...
    android:permission = "android.permission.BIND_ACCESSIBILITY_SERVICE">
    ...             
</service>
  1. A intent AccessibilityService:
<intent-filter>
   <action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
  1. Local do arquivo que contém os metadados para o serviço que você está criando:
<meta-data
       ...
       android:resource="@xml/global_action_bar_service" />
</service>

global_action_bar_service.xml

Esse arquivo contém os metadados do serviço.

<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
   android:accessibilityFeedbackType="feedbackGeneric"
   android:accessibilityFlags="flagDefault"
   android:canPerformGestures="true"
   android:canRetrieveWindowContent="true" />

Usando um elemento &lt;accessibility-service&gt;, os seguintes metadados foram definidos:

  1. O tipo de feedback para este serviço. Este codelab usa feedbackGeneric, que é um bom padrão.
  2. As flags de acessibilidade do serviço (este codelab usa flags padrão).
  3. Os recursos necessários para o serviço:
  4. Para deslizar, android:canPerformGestures está definido como true.
  5. Para recuperar o conteúdo da janela, android:canRetrieveWindowContent é definido como true.

GlobalActionBarService.java

A maior parte do código do serviço de acessibilidade fica em GlobalActionBarService.java. Inicialmente, o arquivo contém o código absoluto mínimo (link em inglês) para um serviço de acessibilidade:

  1. Uma classe que estende AccessibilityService.
  2. Alguns métodos substituídos obrigatórios (deixados em branco neste codelab).
public class GlobalActionBarService extends AccessibilityService {

   @Override
   public void onAccessibilityEvent(AccessibilityEvent event) {

   }

   @Override
   public void onInterrupt() {

   }
}

Você vai adicionar código a esse arquivo durante o codelab.

action_bar.xml

O serviço expõe uma IU com quatro botões, e o arquivo de layout action_bar.xml contém a marcação para exibir esses botões:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:orientation="horizontal"
   android:layout_width="match_parent"
   android:layout_height="wrap_content">
</LinearLayout>

Por enquanto, este arquivo contém um LinearLayout vazio. Você vai adicionar marcações para os botões durante o codelab.

Como iniciar o aplicativo

É preciso ter um dispositivo está conectado ao computador. Pressione o ícone verde Play Botão &quot;Play&quot; do Android Studio usado para iniciar o serviço na barra de menus na parte de cima da tela. Isso deve abrir o app em que você está trabalhando.

Acesse Configurações > Acessibilidade. O Global Action Bar Service está instalado no dispositivo.

Tela &quot;Configurações de acessibilidade&quot;

Clique em Global Action Bar Service e ative-a. A seguinte caixa de diálogo de permissão vai aparecer:

Caixa de diálogo de permissão do serviço de acessibilidade.

O serviço de acessibilidade solicita permissão para observar as ações do usuário, recuperar o conteúdo da janela e realizar gestos em nome do usuário. Ao usar um serviço de acessibilidade de terceiros, confirme se você realmente confia na fonte.

A execução do serviço não traz muitas vantagens, já que ainda não adicionamos nenhuma funcionalidade. Vamos começar a fazer isso.

4. Como criar os botões

Abra action_bar.xml em res/layout. Adicione a marcação dentro do LinearLayout vazio atualmente:

<LinearLayout ...>
    <Button
        android:id="@+id/power"
        android:text="@string/power"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    <Button
        android:id="@+id/volume_up"
        android:text="@string/volume"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    <Button
        android:id="@+id/scroll"
        android:text="@string/scroll"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    <Button
        android:id="@+id/swipe"
        android:text="@string/swipe"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
</LinearLayout>

Isso cria botões que o usuário pressiona para acionar ações no dispositivo.

Abra GlobalActionBarService.java e adicione uma variável para armazenar o layout da barra de ações:

public class GlobalActionBarService extends AccessibilityService {
    FrameLayout mLayout;
    ...
}

Agora, adicione um método onServiceStarted():

public class GlobalActionBarService extends AccessibilityService {
   FrameLayout mLayout;

   @Override
   protected void onServiceConnected() {
       // Create an overlay and display the action bar
       WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
       mLayout = new FrameLayout(this);
       WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
       lp.type = WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
       lp.format = PixelFormat.TRANSLUCENT;
       lp.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
       lp.width = WindowManager.LayoutParams.WRAP_CONTENT;
       lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
       lp.gravity = Gravity.TOP;
       LayoutInflater inflater = LayoutInflater.from(this);
       inflater.inflate(R.layout.action_bar, mLayout);
       wm.addView(mLayout, lp);
   }
}

O código infla o layout e adiciona a barra de ações à parte de cima da tela.

O método onServiceConnected() é executado quando o serviço está conectado. No momento, o serviço de acessibilidade tem todas as permissões necessárias para funcionar. A permissão fundamental que você vai usar aqui é a permissão WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY. Com essa permissão, é possível desenhar diretamente na tela acima do conteúdo existente sem precisar passar por um fluxo de permissões complicado.

Ciclo de vida do serviço de acessibilidade

O ciclo de vida de um serviço de acessibilidade é gerenciado exclusivamente pelo sistema e segue o ciclo de vida estabelecido.

  • Um serviço de acessibilidade é iniciado quando o usuário o ativa explicitamente nas configurações do dispositivo.
  • Depois que o sistema é vinculado a um serviço, ele chama onServiceConnected(). Esse método pode ser substituído por serviços que queiram realizar a configuração pós-vinculação.
  • Um serviço de acessibilidade é interrompido quando o usuário o desativa nas configurações do dispositivo ou quando chama disableSelf().

Como executar o serviço

Antes de iniciar o serviço usando o Android Studio, confira se as configurações de execução estão corretas.

Edite sua configuração de execução (use "Run" no menu superior e acesse "Edit Configurations". Em seguida, usando o menu suspenso, altere a opção de inicialização de "Atividade padrão" para "Nothing".

Menu suspenso para definir as configurações de execução e iniciar um serviço usando o Android Studio.

Agora, você poderá iniciar o serviço usando o Android Studio.

Pressione o ícone verde Play Botão &quot;Play&quot; do Android Studio usado para iniciar o serviço na barra de menus na parte de cima da tela. Em seguida, acesse Configurações > Acessibilidade e ativar o Serviço Global Action Bar Service.

Os quatro botões que formam a interface do serviço vão aparecer sobrepostos ao conteúdo mostrado na tela.

overlay.png

Agora você adicionará funcionalidade aos quatro botões para que um usuário possa tocá-los para realizar ações úteis.

5. Como configurar o botão liga/desliga

Adicione o método configurePowerButton() a configurePowerButton():

private void configurePowerButton() {
   Button powerButton = (Button) mLayout.findViewById(R.id.power);
   powerButton.setOnClickListener(new View.OnClickListener() {
       @Override
       public void onClick(View view) {
           performGlobalAction(GLOBAL_ACTION_POWER_DIALOG);
       }
   });
}

Para acessar o menu do botão liga/desliga, configurePowerButton() usa o método performGlobalAction(), fornecido pelo AccessibilityService. O código que você acabou de adicionar é simples: clicar no botão aciona um onClickListener(). Isso chama performGlobalAction(GLOBAL_ACTION_POWER_DIALOG) e mostra a caixa de diálogo avançada para o usuário.

As ações globais não estão vinculadas a nenhuma visualização. Pressionar os botões "Voltar", "Página inicial" e "Recentes" são outros exemplos de ações globais.

Agora, adicione configurePowerButton() ao final do método onServiceConnected():

@Override
protected void onServiceConnected() {
   ...
   configurePowerButton();
}

Pressione o ícone verde Play Botão &quot;Play&quot; do Android Studio usado para iniciar o serviço na barra de menus na parte de cima da tela. Em seguida, acesse Configurações > Acessibilidade e iniciar o serviço de barra de ações global.

Pressione o botão liga/desliga para exibir a caixa de diálogo.

6. Como configurar o botão de volume

Adicione o método configureVolumeButton() a configureVolumeButton():

private void configureVolumeButton() {
   Button volumeUpButton = (Button) mLayout.findViewById(R.id.volume_up);
   volumeUpButton.setOnClickListener(new View.OnClickListener() {
       @Override
       public void onClick(View view) {
           AudioManager audioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
           audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC,
                   AudioManager.ADJUST_RAISE, AudioManager.FLAG_SHOW_UI);
       }
   });
}

O método configureVolumeButton() adiciona um onClickListener() que é acionado quando o usuário pressiona o botão de volume. Dentro desse listener, configureVolumeButton() usa um AudioManager para ajustar o volume do stream.

Observe que qualquer pessoa pode controlar o volume (você não precisa ser um serviço de acessibilidade para fazer isso).

Agora, adicione configureVolumeButton() ao final do método onServiceConnected():

@Override
protected void onServiceConnected() {
   ...

   configureVolumeButton();
}

Pressione o ícone verde Play Botão &quot;Play&quot; do Android Studio usado para iniciar o serviço na barra de menus na parte de cima da tela. Em seguida, acesse Configurações > Acessibilidade e iniciar o serviço Global Action Bar (link em inglês).

Pressione o botão de volume para mudar o volume.

O usuário hipotético que não conseguia alcançar os controles de volume na lateral do dispositivo agora pode usar o Serviço Global Action Bar para mudar (aumentar) o volume.

7. Como configurar o botão de rolagem

Esta seção envolve a codificação de dois métodos. O primeiro método encontra um nó rolável e o segundo executa a ação de rolagem em nome do usuário.

Adicione o método findScrollableNode a findScrollableNode:

private AccessibilityNodeInfo findScrollableNode(AccessibilityNodeInfo root) {
   Deque<AccessibilityNodeInfo> deque = new ArrayDeque<>();
   deque.add(root);
   while (!deque.isEmpty()) {
       AccessibilityNodeInfo node = deque.removeFirst();
       if (node.getActionList().contains(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD)) {
           return node;
       }
       for (int i = 0; i < node.getChildCount(); i++) {
           deque.addLast(node.getChild(i));
       }
   }
   return null;
}

Um serviço de acessibilidade não tem acesso às visualizações reais na tela. Em vez disso, ele vê um reflexo do que está na tela na forma de uma árvore composta por objetos AccessibilityNodeInfo. Esses objetos contêm informações sobre a visualização que representam (o local da visualização, qualquer texto associado a ela, metadados que foram adicionados para acessibilidade, ações compatíveis com a visualização etc.). O método findScrollableNode() executa uma travessia em nível de amplitude dessa árvore, começando no nó raiz. Se ele encontrar um nó rolável, ou seja, um nó que ofereça suporte à ação AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD), ele retornará esse nó.

Agora, adicione o método configureScrollButton() a configureScrollButton():

private void configureScrollButton() {
   Button scrollButton = (Button) mLayout.findViewById(R.id.scroll);
   scrollButton.setOnClickListener(new View.OnClickListener() {
       @Override
       public void onClick(View view) {
           AccessibilityNodeInfo scrollable = findScrollableNode(getRootInActiveWindow());
           if (scrollable != null) {
               scrollable.performAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD.getId());
           }
       }
   });
}

Esse método cria um onClickListener() que é acionado quando o botão de rolagem é clicado. Ele tenta encontrar um nó rolável e, se bem-sucedido, executa a ação de rolagem.

Agora adicione configureScrollButton() a configureScrollButton():

@Override
protected void onServiceConnected() {
   ...

   configureScrollButton();
}

Pressione o ícone verde Play Botão &quot;Play&quot; do Android Studio usado para iniciar o serviço na barra de menus na parte de cima da tela. Em seguida, acesse Configurações > Acessibilidade e iniciar o serviço Global Action Bar (link em inglês).

Pressione o botão "Voltar" para acessar Configurações > Acessibilidade. É possível rolar os itens da atividade de configurações de acessibilidade, e tocar no botão "Rolar" executa uma ação de rolagem. Nosso usuário hipotético, que não conseguia realizar ações de rolagem com facilidade, agora pode usar o botão de rolagem para percorrer uma lista de itens.

8. Como configurar o botão de deslizar

Adicione o método configureSwipeButton() a configureSwipeButton():

private void configureSwipeButton() {
   Button swipeButton = (Button) mLayout.findViewById(R.id.swipe);
   swipeButton.setOnClickListener(new View.OnClickListener() {
       @Override
       public void onClick(View view) {
           Path swipePath = new Path();
           swipePath.moveTo(1000, 1000);
           swipePath.lineTo(100, 1000);
           GestureDescription.Builder gestureBuilder = new GestureDescription.Builder();
           gestureBuilder.addStroke(new GestureDescription.StrokeDescription(swipePath, 0, 500));
           dispatchGesture(gestureBuilder.build(), null, null);
       }
   });
}

O método configureSwipeButton() usa uma nova API adicionada ao N que realiza gestos em nome do usuário. O código usa um objeto GestureDescription para especificar o caminho do gesto que vai ser executado (valores fixados no código são usados neste codelab) e, em seguida, envia o gesto de deslizar em nome do usuário usando o método AccessibilityService dispatchGesture().

Agora, adicione configureSwipeButton() a onServiceConnected():

@Override
protected void onServiceConnected() {
   ...
   configureSwipeButton();
}

Pressione o ícone verde Play Botão &quot;Play&quot; do Android Studio usado para iniciar o serviço na barra de menus na parte de cima da tela. Em seguida, acesse Configurações > Acessibilidade e iniciar o serviço Global Action Bar (link em inglês).

A maneira mais fácil de testar a funcionalidade de deslizar é abrir o aplicativo Maps instalado no smartphone. Após o carregamento do mapa, tocar no botão Deslizar desliza a tela para a direita.

9. Resumo

Parabéns! Você criou um serviço de acessibilidade simples e funcional.

É possível estender esse serviço de várias maneiras. Exemplo:

  1. Torne a barra de ações móvel (ela só fica na parte superior da tela por enquanto).
  2. Permita que o usuário aumente e diminua o volume.
  3. Permitir que o usuário deslize para a esquerda e para a direita.
  4. Adicionar suporte a outros gestos aos quais a barra de ações pode responder.

Este codelab abrange apenas um pequeno subconjunto da funcionalidade fornecida pelas APIs de acessibilidade. A API também abrange o seguinte (lista parcial):

  • Suporte a várias janelas.
  • Compatibilidade com AccessibilityEvents. Quando a interface muda, os serviços de acessibilidade são notificados sobre essas mudanças usando objetos AccessibilityEvent. O serviço poderá responder, conforme apropriado, às alterações de IU.
  • Capacidade de controlar a ampliação.

Este codelab ensina os primeiros passos na criação de um serviço de acessibilidade. Se você conhece um usuário com problemas específicos de acessibilidade que gostaria de resolver, agora pode criar um serviço para ajudá-lo.