Développer un service d'accessibilité pour Android

1. Introduction

Les services d'accessibilité sont une fonctionnalité du framework Android conçue pour fournir à l'utilisateur des informations de navigation alternatives au nom des applications installées sur les appareils Android. Un service d'accessibilité peut communiquer avec l'utilisateur au nom de l'application, par exemple en convertissant du texte en parole ou en fournissant un retour haptique lorsqu'un utilisateur pointe sur une zone importante de l'écran. Cet atelier de programmation vous explique comment créer un service d'accessibilité très simple.

Qu'est-ce qu'un service d'accessibilité ?

Un service d'accessibilité aide les utilisateurs handicapés à utiliser les appareils et applications Android. Il s'agit d'un service privilégié de longue durée qui aide les utilisateurs à traiter les informations affichées à l'écran et leur permet d'interagir de manière significative avec un appareil.

Exemples de services d'accessibilité courants

  • Switch Access : permet aux utilisateurs Android à mobilité réduite d'interagir avec les appareils à l'aide d'un ou de plusieurs contacteurs.
  • Voice Access (bêta) : permet aux utilisateurs Android à mobilité réduite de contrôler un appareil à l'aide de commandes vocales.
  • TalkBack : lecteur d'écran couramment utilisé par les utilisateurs aveugles ou malvoyants.

Concevoir un service d'accessibilité

Bien que Google propose des services tels que Switch Access, Voice Access et TalkBack pour les utilisateurs Android, ces services ne peuvent pas répondre aux besoins de tous les utilisateurs en situation de handicap. Étant donné que de nombreux utilisateurs handicapés ont des besoins spécifiques, les API Android permettant de créer des services d'accessibilité sont ouvertes. Les développeurs sont libres de créer des services d'accessibilité et de les distribuer sur le Play Store.

Objectifs de l'atelier

Dans cet atelier de programmation, vous allez développer un service simple qui effectue quelques tâches utiles à l'aide de l'API d'accessibilité. Si vous savez écrire une application Android de base, vous pouvez développer un service similaire.

L'API d'accessibilité est puissante : le code du service que vous allez créer ne contient que quatre fichiers et environ 200 lignes de code.

L'utilisateur final

Vous allez créer un service pour un utilisateur hypothétique présentant les caractéristiques suivantes :

  • L'utilisateur a du mal à atteindre les boutons latéraux d'un appareil.
  • L'utilisateur a du mal à faire défiler ou à balayer l'écran.

Informations sur le service

Votre service superposera une barre d'action globale à l'écran. L'utilisateur peut appuyer sur les boutons de cette barre pour effectuer les actions suivantes :

  1. Éteignez l'appareil sans appuyer sur le bouton Marche/Arrêt sur le côté du téléphone.
  2. Réglez le volume sans appuyer sur les boutons de volume situés sur le côté du téléphone.
  3. Effectuez des actions de défilement sans faire défiler la page.
  4. Effectuez un balayage sans avoir à utiliser de geste de balayage.

Prérequis

Cet atelier de programmation suppose que vous utiliserez les éléments suivants :

  1. Un ordinateur exécutant Android Studio.
  2. Un terminal permettant d'exécuter des commandes shell simples.
  3. Un appareil fonctionnant sous Android 7.0 (Nougat) connecté à l'ordinateur que vous utiliserez pour le développement.

C'est parti !

2. Configuration

À l'aide du terminal, créez un répertoire dans lequel vous allez travailler. Accédez à ce répertoire.

Télécharger le code

Vous pouvez cloner le dépôt contenant le code de cet atelier de programmation :

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

Le dépôt contient plusieurs projets Android Studio. À l'aide d'Android Studio, ouvrez GlobalActionBarService.

Lancez Android Studio en cliquant sur l'icône Studio :

Logo utilisé pour lancer Android Studio.

Sélectionnez l'option Import project (Eclipse ADT, Gradle, etc.) (Importer un projet (Eclipse ADT, Gradle, etc.)) :

Écran d'accueil d'Android Studio.

Accédez à l'emplacement où vous avez cloné la source, puis sélectionnez GlobalActionBarService.

Ensuite, à l'aide d'un terminal, accédez au répertoire racine.

3. Comprendre le code de démarrage

Explorez le projet que vous avez ouvert.

Le squelette de base du service d'accessibilité a déjà été créé pour vous. Tout le code que vous allez écrire dans cet atelier de programmation est limité aux quatre fichiers suivants :

  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

Voici une présentation du contenu de chaque fichier.

AndroidManifest.xml

Les informations sur le service d'accessibilité sont déclarées dans le fichier manifeste :

<?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>

Les trois éléments obligatoires suivants sont déclarés dans AndroidManifest.xml :

  1. Autorisation d'association à un service d'accessibilité :
<service
    ...
    android:permission = "android.permission.BIND_ACCESSIBILITY_SERVICE">
    ...             
</service>
  1. L'intention AccessibilityService :
<intent-filter>
   <action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
  1. Emplacement du fichier contenant les métadonnées du service que vous créez :
<meta-data
       ...
       android:resource="@xml/global_action_bar_service" />
</service>

global_action_bar_service.xml

Ce fichier contient les métadonnées du service.

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

Les métadonnées suivantes ont été définies à l'aide d'un élément <accessibility-service> :

  1. Le type de commentaires pour ce service (cet atelier de programmation utilise feedbackGeneric, qui est une bonne valeur par défaut).
  2. Indicateurs d'accessibilité pour le service (cet atelier de programmation utilise les indicateurs par défaut).
  3. Les fonctionnalités nécessaires pour le service :
  4. Pour effectuer un balayage, android:canPerformGestures est défini sur true.
  5. Pour récupérer le contenu d'une fenêtre, android:canRetrieveWindowContent est défini sur true.

GlobalActionBarService.java

La majeure partie du code du service d'accessibilité se trouve dans GlobalActionBarService.java. Au départ, le fichier contient le code strictement minimal pour un service d'accessibilité :

  1. Une classe qui étend AccessibilityService.
  2. Quelques méthodes de remplacement requises (laissées vides dans cet atelier de programmation).
public class GlobalActionBarService extends AccessibilityService {

   @Override
   public void onAccessibilityEvent(AccessibilityEvent event) {

   }

   @Override
   public void onInterrupt() {

   }
}

Vous ajouterez du code à ce fichier au cours de l'atelier de programmation.

action_bar.xml

Le service expose une UI avec quatre boutons, et le fichier de mise en page action_bar.xml contient le balisage pour afficher ces boutons :

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

Ce fichier contient pour l'instant un LinearLayout vide. Vous ajouterez le balisage des boutons au cours de l'atelier de programmation.

Lancer l'application

Assurez-vous qu'un appareil est connecté à votre ordinateur. Appuyez sur l'icône verte Lire Bouton de lecture Android Studio utilisé pour lancer le service dans la barre de menu en haut de l'écran. L'application sur laquelle vous travaillez devrait se lancer.

Accédez à Paramètres > Accessibilité. Le service de barre d'actions globale est installé sur votre appareil.

Écran des paramètres d&#39;accessibilité

Cliquez sur Global Action Bar Service (Service de barre d'action globale) et activez-le. La boîte de dialogue d'autorisation suivante doit s'afficher :

Boîte de dialogue d&#39;autorisation du service d&#39;accessibilité.

Le service d'accessibilité demande l'autorisation d'observer les actions de l'utilisateur, de récupérer le contenu des fenêtres et d'effectuer des gestes pour le compte de l'utilisateur. Lorsque vous utilisez un service d'accessibilité tiers, assurez-vous de faire confiance à la source.

L'exécution du service n'a pas beaucoup d'effet, car nous n'avons pas encore ajouté de fonctionnalité. Commençons.

4. Créer les boutons

Ouvrez action_bar.xml dans res/layout. Ajoutez le balisage à l'intérieur de l'élément LinearLayout actuellement vide :

<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>

Cela crée des boutons sur lesquels l'utilisateur appuiera pour déclencher des actions sur l'appareil.

Ouvrez GlobalActionBarService.java et ajoutez une variable pour stocker la mise en page de la barre d'action :

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

Ajoutez maintenant une méthode 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);
   }
}

Le code augmente la taille de la mise en page et ajoute la barre d'action en haut de l'écran.

La méthode onServiceConnected() s'exécute lorsque le service est connecté. À ce stade, le service d'accessibilité dispose de toutes les autorisations nécessaires pour fonctionner. L'autorisation clé que vous utiliserez ici est WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY. Cette autorisation vous permet de dessiner directement sur l'écran au-dessus du contenu existant, sans avoir à suivre un flux d'autorisations complexe.

Cycle de vie du service d'accessibilité

Le cycle de vie d'un service d'accessibilité est géré exclusivement par le système et suit le cycle de vie de service établi.

  • Un service d'accessibilité démarre lorsque l'utilisateur l'active explicitement dans les paramètres de l'appareil.
  • Une fois que le système est lié à un service, il appelle onServiceConnected(). Cette méthode peut être remplacée par les services qui souhaitent effectuer une configuration post-liaison.
  • Un service d'accessibilité s'arrête lorsque l'utilisateur le désactive dans les paramètres de l'appareil ou lorsqu'il appelle disableSelf().

Exécuter le service

Avant de pouvoir lancer le service à l'aide d'Android Studio, vous devez vous assurer que vos paramètres d'exécution sont correctement configurés.

Modifiez votre configuration d'exécution (utilisez "Exécuter" dans le menu du haut, puis accédez à "Modifier les configurations"). Ensuite, dans le menu déroulant, remplacez l'option de lancement "Default Activity" (Activité par défaut) par "Nothing" (Rien).

Déroulez le menu pour configurer les paramètres d&#39;exécution afin de lancer un service à l&#39;aide d&#39;Android Studio.

Vous devriez maintenant pouvoir lancer le service à l'aide d'Android Studio.

Appuyez sur l'icône verte Lire Bouton de lecture Android Studio utilisé pour lancer le service dans la barre de menu en haut de l'écran. Ensuite, accédez à Paramètres > Accessibilité et activez Service de barre d'actions globale.

Les quatre boutons qui composent l'UI du service devraient s'afficher en superposition sur le contenu affiché à l'écran.

overlay.png

Vous allez maintenant ajouter des fonctionnalités aux quatre boutons, afin qu'un utilisateur puisse les toucher pour effectuer des actions utiles.

5. Configurer le bouton Marche/Arrêt

Ajoutez la méthode configurePowerButton() à GlobalActionBarService.java :

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);
       }
   });
}

Pour accéder au menu du bouton Marche/Arrêt, configurePowerButton() utilise la méthode performGlobalAction() fournie par AccessibilityService. Le code que vous venez d'ajouter est simple : lorsque l'utilisateur clique sur le bouton, un onClickListener() est déclenché. Cela appelle performGlobalAction(GLOBAL_ACTION_POWER_DIALOG) et affiche la boîte de dialogue d'alimentation à l'utilisateur.

Notez que les actions globales ne sont associées à aucune vue. Appuyer sur le bouton Retour, le bouton Accueil ou le bouton Applications récentes sont d'autres exemples d'actions globales.

Ajoutez maintenant configurePowerButton() à la fin de la méthode onServiceConnected() :

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

Appuyez sur l'icône verte Lire Bouton de lecture Android Studio utilisé pour lancer le service dans la barre de menu en haut de l'écran. Ensuite, accédez à Paramètres > Accessibilité et démarrez le service de barre d'actions globale.

Appuyez sur le bouton Marche/Arrêt pour afficher la boîte de dialogue d'alimentation.

6. Configurer le bouton de volume

Ajoutez la méthode configureVolumeButton() à GlobalActionBarService.java :

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);
       }
   });
}

La méthode configureVolumeButton() ajoute un onClickListener() qui se déclenche lorsque l'utilisateur appuie sur le bouton de volume. Dans ce listener, configureVolumeButton() utilise un AudioManager pour ajuster le volume du flux.

Notez que tout le monde peut contrôler le volume (vous n'avez pas besoin d'être un service d'accessibilité pour le faire).

Ajoutez maintenant configureVolumeButton() à la fin de la méthode onServiceConnected() :

@Override
protected void onServiceConnected() {
   ...

   configureVolumeButton();
}

Appuyez sur l'icône verte Lire Bouton de lecture Android Studio utilisé pour lancer le service dans la barre de menu en haut de l'écran. Ensuite, accédez à Paramètres > Accessibilité et lancez le service de barre d'actions globale.

Appuyez sur le bouton Volume pour modifier le volume.

L'utilisateur hypothétique qui ne parvient pas à accéder aux commandes de volume sur le côté de l'appareil peut désormais utiliser Global Action Bar Service pour modifier (augmenter) le volume.

7. Configurer le bouton de défilement

Cette section implique de coder deux méthodes. La première méthode permet de trouver un nœud défilable, tandis que la seconde exécute l'action de défilement pour le compte de l'utilisateur.

Ajoutez la méthode findScrollableNode à GlobalActionBarService.java :

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;
}

Un service d'accessibilité n'a pas accès aux vues réelles à l'écran. Au lieu de cela, il voit un reflet de ce qui se trouve à l'écran sous la forme d'une arborescence composée d'objets AccessibilityNodeInfo. Ces objets contiennent des informations sur la vue qu'ils représentent (l'emplacement de la vue, tout texte associé à la vue, les métadonnées ajoutées pour l'accessibilité, les actions prises en charge par la vue, etc.). La méthode findScrollableNode() effectue une traversée en largeur de cet arbre, en commençant par le nœud racine. S'il trouve un nœud défilable (c'est-à-dire un nœud qui prend en charge l'action AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD)), il le renvoie. Sinon, il renvoie la valeur null.

Ajoutez maintenant la méthode configureScrollButton() à GlobalActionBarService.java :

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());
           }
       }
   });
}

Cette méthode crée un onClickListener() qui se déclenche lorsque l'utilisateur clique sur le bouton de défilement. Il tente de trouver un nœud défilable et, si c'est le cas, effectue l'action de défilement.

Ajoutez maintenant configureScrollButton() à onServiceConnected() :

@Override
protected void onServiceConnected() {
   ...

   configureScrollButton();
}

Appuyez sur l'icône verte Lire Bouton de lecture Android Studio utilisé pour lancer le service dans la barre de menu en haut de l'écran. Ensuite, accédez à Paramètres > Accessibilité et lancez le service de barre d'actions globale.

Appuyez sur le bouton Retour pour accéder à Paramètres > Accessibilité. Les éléments de l'activité des paramètres d'accessibilité sont défilables. Appuyer sur le bouton "Défiler" permet de faire défiler l'écran. Notre utilisateur hypothétique qui ne peut pas faire défiler facilement une liste d'éléments peut désormais utiliser le bouton "Faire défiler".

8. Configurer le bouton de balayage

Ajoutez la méthode configureSwipeButton() à GlobalActionBarService.java :

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);
       }
   });
}

La méthode configureSwipeButton() utilise une nouvelle API ajoutée dans N qui effectue des gestes pour le compte de l'utilisateur. Le code utilise un objet GestureDescription pour spécifier le chemin d'accès du geste à effectuer (des valeurs codées en dur sont utilisées dans cet atelier de programmation), puis envoie le geste de balayage au nom de l'utilisateur à l'aide de la méthode dispatchGesture() de AccessibilityService.

Ajoutez maintenant configureSwipeButton() à onServiceConnected() :

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

Appuyez sur l'icône verte Lire Bouton de lecture Android Studio utilisé pour lancer le service dans la barre de menu en haut de l'écran. Ensuite, accédez à Paramètres > Accessibilité et lancez le service de barre d'actions globale.

Le moyen le plus simple de tester la fonctionnalité de balayage consiste à ouvrir l'application Maps installée sur votre téléphone. Une fois la carte chargée, appuyer sur le bouton "Balayer" fait glisser l'écran vers la droite.

9. Résumé

Félicitations ! Vous avez créé un service d'accessibilité simple et fonctionnel.

Vous pouvez étendre ce service de différentes manières. Exemple :

  1. Rendez la barre d'action mobile (pour l'instant, elle se trouve en haut de l'écran).
  2. Autoriser l'utilisateur à augmenter et à diminuer le volume.
  3. Autorisez l'utilisateur à balayer l'écran vers la gauche et vers la droite.
  4. Ajoutez la prise en charge d'autres gestes auxquels la barre d'action peut répondre.

Cet atelier de programmation ne couvre qu'un petit sous-ensemble des fonctionnalités fournies par les API d'accessibilité. L'API couvre également les éléments suivants (liste partielle) :

  • Prise en charge de plusieurs fenêtres.
  • Compatibilité avec les AccessibilityEvent. Lorsque l'UI change, les services d'accessibilité sont informés de ces changements à l'aide d'objets AccessibilityEvent. Le service peut alors répondre de manière appropriée aux modifications de l'UI.
  • Vous pouvez contrôler le niveau d'agrandissement.

Cet atelier de programmation vous permet de vous familiariser avec l'écriture d'un service d'accessibilité. Si vous connaissez un utilisateur qui présente des problèmes d'accessibilité spécifiques que vous souhaitez résoudre, vous pouvez désormais créer un service pour l'aider.