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 commentaires de navigation alternatifs 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 la synthèse vocale 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 ayant un handicap à se servir des appareils et des applications Android. Il s'agit d'un service privilégié de longue durée qui aide les utilisateurs à traiter les informations à 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 malvoyants et non-voyants.

Créer un service d'accessibilité

Bien que Google fournisse des services tels que Switch Access, Voice Access et TalkBack aux utilisateurs Android, ces services ne peuvent pas être utilisés par tous les utilisateurs en situation de handicap. Étant donné que de nombreux utilisateurs en situation de handicap ont des besoins uniques, les API Android permettant de créer des services d'accessibilité sont ouvertes, et les développeurs sont libres de créer des services d'accessibilité et de les distribuer via le Play Store.

Objectifs de l'atelier

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

L'API d'accessibilité est très efficace: le code du service que vous allez créer est contenu dans quatre fichiers seulement et utilise environ 200 lignes de code.

L'utilisateur final

Vous allez créer un service destiné à un utilisateur fictif et 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 l'écran 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 atteindre le bouton Marche/Arrêt sur le côté du téléphone.
  2. Réglez le volume sans toucher les boutons de volume sur le côté du téléphone.
  3. Effectuez des actions de défilement sans avoir à faire défiler la page.
  4. Effectuez un balayage sans avoir à balayer l'écran.

Prérequis

Dans cet atelier de programmation, nous partons du principe que vous allez utiliser:

  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) et 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épart

Explorez le projet que vous avez ouvert.

Le squelette de base du service d'accessibilité a déjà été créé pour vous. Le code que vous écrirez 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 un tutoriel sur le 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 le fichier AndroidManifest.xml:

  1. Autorisation de s'associer à un service d'accessibilité:
<service
    ...
    android:permission = "android.permission.BIND_ACCESSIBILITY_SERVICE">
    ...             
</service>
  1. L'intent 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 &lt;accessibility-service&gt;:

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

GlobalActionBarService.java

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

  1. Classe qui étend AccessibilityService.
  2. Quelques méthodes de remplacement requises (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 cet atelier de programmation.

action_bar.xml

Le service propose une interface utilisateur avec quatre boutons, et le fichier de mise en page action_bar.xml contient le balisage permettant d'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>

Pour le moment, ce fichier contient une mise en page LinearLayout vide. Vous ajouterez des balises pour les boutons au cours de cet 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 d&#39;Android Studio utilisé pour lancer le service dans la barre de menu en haut de l'écran. Cela devrait lancer l'application sur laquelle vous travaillez.

Accédez à Paramètres > Accessibilité. Le service de barre d'action 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 des utilisateurs, de récupérer le contenu des fenêtres et d'effectuer des gestes au nom de l'utilisateur. Lorsque vous utilisez un service d'accessibilité tiers, assurez-vous de vraiment faire confiance à la source.

L'exécution du service ne fait pas grand-chose, car nous n'avons encore ajouté aucune fonctionnalité. Commençons par le faire.

4. Création des boutons

Ouvrez action_bar.xml dans res/layout. Ajoutez le balisage dans le composant 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 devra appuyer 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 gonfle la mise en page et ajoute la barre d'action vers le haut de l'écran.

La méthode onServiceConnected() s'exécute lorsque le service est connecté. Pour le moment, le service d'accessibilité dispose de toutes les autorisations nécessaires à son fonctionnement. La principale autorisation que vous utiliserez ici est l'autorisation WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY. Cette autorisation vous permet de dessiner directement sur l'écran, au-dessus d'un contenu existant, sans avoir à suivre une procédure d'autorisation compliquée.

Cycle de vie des services 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 du service établi.

  • Un service d'accessibilité démarre lorsque l'utilisateur l'active explicitement dans les paramètres de l'appareil.
  • Une fois le système associé à un service, il appelle onServiceConnected(). Cette méthode peut être remplacée par les services qui souhaitent effectuer la 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 "Run" dans le menu du haut et accédez à "Edit Configurations" [Modifier les configurations]). Ensuite, dans le menu déroulant, remplacez l'option de lancement "Default Activity" (Activité par défaut) par par "Rien".

Liste déroulante permettant de 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 d&#39;Android Studio utilisé pour lancer le service dans la barre de menu en haut de l'écran. Accédez ensuite à Paramètres > Accessibilité et activez Global Action Bar Service (Service de barre d'action globale).

Vous devriez voir les quatre boutons de l'interface utilisateur du service 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 appuyer dessus pour effectuer des actions utiles.

5. Configurer le bouton Marche/Arrêt

Ajoutez la méthode configurePowerButton() au fichier 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);
       }
   });
}

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: un clic sur le bouton déclenche une onClickListener(). Cette opération appelle performGlobalAction(GLOBAL_ACTION_POWER_DIALOG) et présente la boîte de dialogue d'alimentation à l'utilisateur.

Notez que les actions globales ne sont associées à aucune vue. Les boutons "Retour", "Accueil" et "Récents" 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 d&#39;Android Studio utilisé pour lancer le service dans la barre de menu en haut de l'écran. Accédez ensuite à Paramètres > Accessibilité et lancez le service Global Action Bar.

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

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

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

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 d&#39;Android Studio utilisé pour lancer le service dans la barre de menu en haut de l'écran. Accédez ensuite à Paramètres > Accessibilité et démarrez le service de barre d'action globale.

Appuyez sur le bouton de volume pour régler le volume.

L'utilisateur fictif qui n'est pas en mesure d'accéder aux commandes de volume sur le côté de l'appareil peut désormais utiliser Global Action Bar Service (Service de barre d'action globale) pour modifier (augmenter) le volume.

7. Configurer le bouton de défilement

Cette section implique le codage de deux méthodes. La première méthode recherche un nœud à faire défiler et les deuxièmes méthodes effectuent l'action de défilement pour le compte de l'utilisateur.

Ajoutez la méthode findScrollableNode à 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;
}

Un service d'accessibilité n'a pas accès aux vues réelles à l'écran. Au lieu de cela, le contenu à l'écran est reflété 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 qui ont été ajoutées pour l'accessibilité, les actions compatibles avec la vue, etc.). La méthode findScrollableNode() effectue un balayage de cette arborescence en avant-première, en commençant par le nœud racine. S'il trouve un nœud déroulant (c'est-à-dire un nœud compatible avec l'action AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD)), il le renvoie. Sinon, il renvoie la valeur "null".

Ajoutez maintenant la méthode configureScrollButton() au fichier 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());
           }
       }
   });
}

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 à faire défiler et, si l'opération réussit, 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 d&#39;Android Studio utilisé pour lancer le service dans la barre de menu en haut de l'écran. Accédez ensuite à Paramètres > Accessibilité et démarrez le service de barre d'action globale.

Appuyez sur le bouton Retour pour accéder à Paramètres > Accessibilité. Il est possible de faire défiler les éléments de l'activité des paramètres d'accessibilité, et le bouton "Défilement" permet de faire défiler l'écran. Notre utilisateur fictif qui n'est pas en mesure d'effectuer facilement des actions de défilement peut désormais utiliser le bouton Faire défiler pour faire défiler une liste d'éléments.

8. Configurer le bouton "Balayer"

Ajoutez la méthode configureSwipeButton() à 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);
       }
   });
}

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 pour le compte de l'utilisateur à l'aide de la méthode AccessibilityService dispatchGesture().

Ajoutez maintenant configureSwipeButton() à onServiceConnected():

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

Appuyez sur l'icône verte Lire Bouton de lecture d&#39;Android Studio utilisé pour lancer le service dans la barre de menu en haut de l'écran. Accédez ensuite à Paramètres > Accessibilité et démarrez le service de barre d'action 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, appuyez sur le bouton Balayer pour faire 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. Faites en sorte que la barre d'action puisse être déplacée (elle reste en haut de l'écran pour le moment).
  2. Autorisez l'utilisateur à monter et à baisser le volume.
  3. Autorisez l'utilisateur à balayer l'écran vers la gauche et vers la droite.
  4. Ajout de la prise en charge de gestes supplémentaires 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.
  • Prise en charge des AccessibilityEvent. Lorsque l'UI change, les services d'accessibilité en sont informés à l'aide d'objets AccessibilityEvent. Le service peut ensuite répondre aux modifications de l'interface utilisateur, le cas échéant.
  • Possibilité de contrôler l'agrandissement.

Cet atelier de programmation vous aide à créer un service d'accessibilité. Si vous connaissez un utilisateur ayant des problèmes d'accessibilité spécifiques que vous souhaitez résoudre, vous pouvez désormais créer un service pour l'aider.