Sviluppare un servizio di accessibilità per Android

1. Introduzione

I servizi di accessibilità sono una funzionalità del framework Android progettata per fornire feedback di navigazione alternativi all'utente per conto delle applicazioni installate sui dispositivi Android. Un servizio di accessibilità può comunicare con l'utente per conto dell'applicazione, ad esempio convertendo il testo in voce o fornendo un feedback aptico quando un utente passa il mouse sopra un'area importante dello schermo. Questo codelab mostra come creare un servizio di accessibilità molto semplice.

Che cos'è un servizio di accessibilità?

Un servizio di accessibilità aiuta gli utenti con disabilità a utilizzare dispositivi e app Android. Si tratta di un servizio privilegiato di lunga data che aiuta gli utenti a elaborare le informazioni sullo schermo e a interagire in modo significativo con un dispositivo.

Esempi di servizi di accessibilità comuni

  • Switch Access: consente agli utenti Android con limitazioni di mobilità di interagire con i dispositivi utilizzando uno o più sensori.
  • Voice Access (beta): consente agli utenti Android con mobilità ridotta di controllare un dispositivo con i comandi vocali.
  • TalkBack: uno screen reader comunemente utilizzato da utenti con disabilità visiva o ciechi.

Creazione di un servizio di accessibilità

Sebbene Google fornisca servizi come Switch Access, Voice Access e TalkBack per gli utenti Android, questi servizi non possono servire tutti gli utenti con disabilità. Poiché molti utenti con disabilità hanno esigenze uniche, le API di Android per la creazione di servizi di accessibilità sono aperte e gli sviluppatori sono liberi di creare servizi di accessibilità e distribuirli tramite il Play Store.

Cosa creerai

In questo codelab, svilupperai un semplice servizio che esegue alcune operazioni utili utilizzando l'API Accessibility. Se sai scrivere un'app per Android di base, puoi sviluppare un servizio simile.

L'API di accessibilità è potente: il codice per il servizio che creerai è contenuto in soli quattro file e utilizza circa 200 righe di codice.

L'utente finale

Creerai un servizio per un utente ipotetico con le seguenti caratteristiche:

  • L'utente ha difficoltà a raggiungere i tasti laterali di un dispositivo.
  • L'utente ha difficoltà a scorrere o scorrere con il dito.

Dettagli del servizio

Il servizio sovrappone una barra delle azioni globale sullo schermo. L'utente può toccare i pulsanti di questa barra per eseguire le seguenti azioni:

  1. Spegni il dispositivo senza raggiungere il tasto di accensione fisico sul lato dello smartphone.
  2. Regola il volume senza toccare i tasti del volume sul lato dello smartphone.
  3. Eseguire azioni di scorrimento senza scorrere effettivamente.
  4. Esegui uno scorrimento senza dover utilizzare un gesto di scorrimento.

Che cosa ti serve

Questo codelab presuppone che utilizzerai quanto segue:

  1. Un computer con Android Studio.
  2. Un terminale per l'esecuzione di semplici comandi shell.
  3. Un dispositivo con Android 7.0 (Nougat) connesso al computer che utilizzerai per lo sviluppo.

Iniziamo.

2. Preparazione

Utilizzando il terminale, crea una directory in cui lavorare. Passa a questa directory.

Scarica il codice

Puoi clonare il repository che contiene il codice per questo codelab:

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

Il repository contiene diversi progetti Android Studio. Utilizzando Android Studio, apri GlobalActionBarService.

Avvia Android Studio facendo clic sull'icona di Studio:

Logo utilizzato per avviare Android Studio.

Seleziona l'opzione Importa progetto (Eclipse ADT, Gradle e così via):

La schermata di benvenuto di Android Studio.

Vai alla posizione in cui hai clonato l'origine e seleziona GlobalActionBarService.

Quindi, utilizzando un terminale, passa alla directory principale.

3. Informazioni sul codice iniziale

Esplora il progetto che hai aperto.

Lo scheletro essenziale del servizio di accessibilità è già stato creato per te. Tutto il codice che scriverai in questo codelab è limitato ai seguenti quattro file:

  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

Di seguito è riportata una descrizione dettagliata dei contenuti di ciascun file.

AndroidManifest.xml

Le informazioni sul servizio di accessibilità sono dichiarate nel manifest:

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

I seguenti tre elementi obbligatori sono dichiarati in AndroidManifest.xml:

  1. Autorizzazione per il collegamento a un servizio di 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. Posizione del file che contiene i metadati per il servizio che stai creando:
<meta-data
       ...
       android:resource="@xml/global_action_bar_service" />
</service>

global_action_bar_service.xml

Questo file contiene i metadati del servizio.

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

Utilizzando un elemento <accessibility-service>, sono stati definiti i seguenti metadati:

  1. Il tipo di feedback per questo servizio (questo codelab utilizza feedbackGeneric, che è un buon valore predefinito).
  2. I flag di accessibilità per il servizio (questo codelab utilizza i flag predefiniti).
  3. Le funzionalità necessarie per il servizio:
  4. Per eseguire lo scorrimento, android:canPerformGestures è impostato su true.
  5. Per recuperare i contenuti della finestra, android:canRetrieveWindowContent è impostato su true.

GlobalActionBarService.java

La maggior parte del codice per il servizio di accessibilità si trova in GlobalActionBarService.java. Inizialmente, il file contiene il codice minimo indispensabile per un servizio di accessibilità:

  1. Una classe che estende AccessibilityService.
  2. Un paio di metodi di override obbligatori (lasciati vuoti in questo codelab).
public class GlobalActionBarService extends AccessibilityService {

   @Override
   public void onAccessibilityEvent(AccessibilityEvent event) {

   }

   @Override
   public void onInterrupt() {

   }
}

Aggiungerai codice a questo file durante il codelab.

action_bar.xml

Il servizio espone una UI con quattro pulsanti e il file di layout action_bar.xml contiene il markup per la visualizzazione di questi pulsanti:

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

Per il momento, questo file contiene un LinearLayout vuoto. Durante il codelab aggiungerai il markup per i pulsanti.

Avvio dell'applicazione

Assicurati che un dispositivo sia connesso al computer. Premi l'icona verde Riproduci Pulsante Play di Android Studio utilizzato per avviare il servizio dalla barra del menu nella parte superiore dello schermo. Dovrebbe avviarsi l'app su cui stai lavorando.

Vai a Impostazioni > Accessibilità. Il servizio della barra delle azioni globale è installato sul tuo dispositivo.

Schermata delle impostazioni di accessibilità

Fai clic su Global Action Bar Service e attivalo. Dovresti visualizzare la seguente finestra di dialogo per le autorizzazioni:

Finestra di dialogo per l&#39;autorizzazione del servizio di accessibilità.

Il servizio di accessibilità richiede l'autorizzazione per osservare le azioni dell'utente, recuperare i contenuti delle finestre ed eseguire gesti per suo conto. Quando utilizzi un servizio di accessibilità di terze parti, assicurati di fidarti davvero della fonte.

L'esecuzione del servizio non fa molto, poiché non abbiamo ancora aggiunto alcuna funzionalità. Iniziamo.

4. Creazione dei pulsanti

Apri action_bar.xml in res/layout. Aggiungi il markup all'interno di LinearLayout attualmente vuoto:

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

In questo modo vengono creati pulsanti che l'utente premerà per attivare azioni sul dispositivo.

Apri GlobalActionBarService.java e aggiungi una variabile per memorizzare il layout della barra delle azioni:

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

Ora aggiungi un metodo 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);
   }
}

Il codice gonfia il layout e aggiunge la barra delle azioni nella parte superiore dello schermo.

Il metodo onServiceConnected() viene eseguito quando il servizio è connesso. Al momento, il servizio di accessibilità dispone di tutte le autorizzazioni necessarie per funzionare. L'autorizzazione chiave che utilizzerai qui è WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY. Questa autorizzazione ti consente di disegnare direttamente sullo schermo sopra i contenuti esistenti senza dover seguire un flusso di autorizzazioni complicato.

Ciclo di vita del servizio di accessibilità

Il ciclo di vita di un servizio di accessibilità è gestito esclusivamente dal sistema e segue il ciclo di vita del servizio stabilito.

  • Un servizio di accessibilità viene avviato quando l'utente lo attiva esplicitamente nelle impostazioni del dispositivo.
  • Dopo che il sistema si è associato a un servizio, chiama onServiceConnected(). Questo metodo può essere sostituito dai servizi che vogliono eseguire la configurazione post-associazione.
  • Un servizio di accessibilità si interrompe quando l'utente lo disattiva nelle impostazioni del dispositivo o quando chiama disableSelf().

Esecuzione del servizio

Prima di poter avviare il servizio utilizzando Android Studio, devi assicurarti che le impostazioni di esecuzione siano configurate correttamente.

Modifica la configurazione dell'esecuzione (utilizza Esegui dal menu in alto e vai a Modifica configurazioni). Poi, utilizzando il menu a discesa, modifica l'opzione di avvio da "Attività predefinita" a "Nessuna".

Seleziona il menu a discesa per configurare le impostazioni di esecuzione per avviare un servizio utilizzando Android Studio.

Ora dovresti essere in grado di avviare il servizio utilizzando Android Studio.

Premi l'icona verde Riproduci Pulsante Play di Android Studio utilizzato per avviare il servizio dalla barra del menu nella parte superiore dello schermo. Poi, vai su Impostazioni > Accessibilità e attiva Servizio della barra delle azioni globale.

Dovresti vedere i quattro pulsanti che formano l'interfaccia utente del servizio sovrapposti ai contenuti visualizzati sullo schermo.

overlay.png

Ora aggiungerai funzionalità ai quattro pulsanti, in modo che un utente possa toccarli per eseguire azioni utili.

5. Configurazione del tasto di accensione

Aggiungi il metodo configurePowerButton() a 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);
       }
   });
}

Per accedere al menu del tasto di accensione, configurePowerButton() utilizza il metodo performGlobalAction(), fornito da AccessibilityService. Il codice che hai appena aggiunto è semplice: fare clic sul pulsante attiva un onClickListener(). Questo chiama performGlobalAction(GLOBAL_ACTION_POWER_DIALOG) e mostra all'utente la finestra di dialogo di accensione.

Tieni presente che le azioni globali non sono associate ad alcuna visualizzazione. Premere il pulsante Indietro, il pulsante Home e il pulsante Recenti sono altri esempi di azioni globali.

Ora aggiungi configurePowerButton() alla fine del metodo onServiceConnected():

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

Premi l'icona verde Riproduci Pulsante Play di Android Studio utilizzato per avviare il servizio dalla barra del menu nella parte superiore dello schermo. Poi, vai su Impostazioni > Accessibilità e avvia il servizio della barra delle azioni globale.

Premi il tasto di accensione per visualizzare la finestra di dialogo di accensione.

6. Configurazione del tasto del volume

Aggiungi il metodo configureVolumeButton() a 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);
       }
   });
}

Il metodo configureVolumeButton() aggiunge un onClickListener() che viene attivato quando l'utente preme il tasto del volume. All'interno di questo listener, configureVolumeButton() utilizza un AudioManager per regolare il volume dello stream.

Tieni presente che chiunque può controllare il volume (non è necessario essere un servizio di accessibilità per farlo).

Ora aggiungi configureVolumeButton() alla fine del metodo onServiceConnected():

@Override
protected void onServiceConnected() {
   ...

   configureVolumeButton();
}

Premi l'icona verde Riproduci Pulsante Play di Android Studio utilizzato per avviare il servizio dalla barra del menu nella parte superiore dello schermo. Poi, vai su Impostazioni > Accessibilità e avvia il servizio della barra delle azioni globale.

Premi il tasto del volume per regolare il volume.

L'utente ipotetico che non riesce a raggiungere i controlli del volume sul lato del dispositivo ora può utilizzare il servizio della barra delle azioni globale per modificare (aumentare) il volume.

7. Configurazione del pulsante di scorrimento

Questa sezione prevede la codifica di due metodi. Il primo metodo trova un nodo scorrevole, mentre il secondo esegue l'azione di scorrimento per conto dell'utente.

Aggiungi il metodo findScrollableNode a 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 servizio di accessibilità non ha accesso alle visualizzazioni effettive sullo schermo. Vede invece un riflesso di ciò che è sullo schermo sotto forma di albero composto da oggetti AccessibilityNodeInfo. Questi oggetti contengono informazioni sulla vista che rappresentano (la posizione della vista, qualsiasi testo associato alla vista, i metadati aggiunti per l'accessibilità, le azioni supportate dalla vista e così via). Il metodo findScrollableNode() esegue l'attraversamento in ampiezza di questo albero, a partire dal nodo radice. Se trova un nodo scorrevole (ovvero un nodo che supporta l'azione AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD)), lo restituisce, altrimenti restituisce null.

Ora aggiungi il metodo configureScrollButton() a 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());
           }
       }
   });
}

Questo metodo crea un onClickListener() che si attiva quando viene fatto clic sul pulsante di scorrimento. Tenta di trovare un nodo scorrevole e, se riesce, esegue l'azione di scorrimento.

Ora aggiungi configureScrollButton() a onServiceConnected():

@Override
protected void onServiceConnected() {
   ...

   configureScrollButton();
}

Premi l'icona verde Riproduci Pulsante Play di Android Studio utilizzato per avviare il servizio dalla barra del menu nella parte superiore dello schermo. Poi, vai su Impostazioni > Accessibilità e avvia il servizio della barra delle azioni globale.

Premi il pulsante Indietro per andare a Impostazioni > Accessibilità. Gli elementi nell'attività delle impostazioni di accessibilità sono scorrevoli e toccando il pulsante Scorrimento viene eseguita un'azione di scorrimento. Il nostro utente ipotetico che non riesce a eseguire facilmente le azioni di scorrimento ora può utilizzare il pulsante Scorrimento per scorrere un elenco di elementi.

8. Configurazione del pulsante di scorrimento

Aggiungi il metodo configureSwipeButton() a 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);
       }
   });
}

Il metodo configureSwipeButton() utilizza una nuova API aggiunta in N che esegue i gesti per conto dell'utente. Il codice utilizza un oggetto GestureDescription per specificare il percorso del gesto da eseguire (in questo codelab vengono utilizzati valori hardcoded) e poi invia il gesto di scorrimento per conto dell'utente utilizzando il metodo dispatchGesture() di AccessibilityService.

Ora aggiungi configureSwipeButton() a onServiceConnected():

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

Premi l'icona verde Riproduci Pulsante Play di Android Studio utilizzato per avviare il servizio dalla barra del menu nella parte superiore dello schermo. Poi, vai su Impostazioni > Accessibilità e avvia il servizio della barra delle azioni globale.

Il modo più semplice per testare la funzionalità di scorrimento è aprire l'applicazione Maps installata sullo smartphone. Una volta caricata la mappa, toccando il pulsante Scorri, lo schermo scorre verso destra.

9. Riepilogo

Complimenti! Hai creato un servizio di accessibilità semplice e funzionale.

Puoi estendere questo servizio in vari modi. Ad esempio:

  1. Rendi spostabile la barra delle azioni (per ora si trova nella parte superiore dello schermo).
  2. Consenti all'utente di aumentare e diminuire il volume.
  3. Consente all'utente di scorrere sia a sinistra che a destra.
  4. Aggiungi il supporto per ulteriori gesti a cui la barra delle azioni può rispondere.

Questo codelab copre solo un piccolo sottoinsieme delle funzionalità fornite dalle API di accessibilità. L'API copre anche quanto segue (elenco parziale):

  • Supporto per più finestre.
  • Supporto per gli AccessibilityEvent. Quando l'interfaccia utente cambia, i servizi di accessibilità vengono informati di queste modifiche tramite gli oggetti AccessibilityEvent. Il servizio può quindi rispondere in modo appropriato alle modifiche dell'interfaccia utente.
  • Possibilità di controllare l'ingrandimento.

Questo codelab ti aiuta a iniziare a scrivere un servizio di accessibilità. Se conosci un utente con problemi di accessibilità specifici che vuoi risolvere, ora puoi creare un servizio per aiutarlo.