Sviluppare un servizio di accessibilità per Android

1. Introduzione

I servizi di accessibilità sono una funzionalità del framework di Android progettata per fornire feedback sulla 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 la sintesi vocale o fornendo un feedback aptico quando un utente passa il mouse sopra un'area importante dello schermo. Questo codelab ti mostra come creare un servizio di accessibilità molto semplice.

Cos'è un servizio di accessibilità?

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

Esempi di servizi di accessibilità comuni

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

Creazione di un servizio di accessibilità

Anche se Google fornisce servizi come Switch Access, Voice Access e TalkBack per gli utenti Android, non è possibile che questi servizi siano disponibili per 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 servizio semplice che esegue alcune operazioni utili utilizzando l'API Accessibility. Se sai scrivere un'app Android di base, puoi sviluppare un servizio simile.

L'API Accessibility è potente: il codice per il servizio che creerai è contenuto solo in 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 pulsanti laterali su un dispositivo.
  • L'utente ha difficoltà a scorrere o scorrere.

Dettagli del servizio

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

  1. Spegni il dispositivo senza raggiungere il tasto di accensione sul lato dello smartphone.
  2. Regola il volume senza toccare i pulsanti del volume sul lato dello smartphone.
  3. Esegui azioni di scorrimento senza scorrere effettivamente.
  4. Scorri senza dover scorrere.

Che cosa ti serve

Questo codelab presuppone l'utilizzo di 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) collegato al computer che utilizzerai per lo sviluppo.

Iniziamo.

2. Preparazione

Utilizzando il terminale, crea una directory in cui lavorerai. 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. In 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 root.

3. Comprendere il codice di partenza

Esplora il progetto che hai aperto.

Lo scheletro semplice per il servizio di accessibilità è già stato creato automaticamente. 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

Ecco una procedura dettagliata dei contenuti di ciascun file.

AndroidManifest.xml

Le informazioni sul servizio di accessibilità sono dichiarate nel file 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>

In AndroidManifest.xml sono stati dichiarati i tre elementi seguenti:

  1. Autorizzazione per l'associazione 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. Percorso 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 per il 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 &lt;accessibility-service&gt; sono stati definiti i seguenti metadati:

  1. Il tipo di feedback per questo servizio (questo codelab utilizza feedbackGeneric, che è un valore predefinito).
  2. I flag di accessibilità per il servizio (questo codelab utilizza flag predefiniti).
  3. Le funzionalità necessarie al servizio:
  4. Per eseguire lo scorrimento, l'opzione android:canPerformGestures è impostata su android:canPerformGestures.
  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 basso minimo assoluto 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 il codice a questo file durante il codelab.

action_bar.xml

Il servizio espone un'interfaccia utente 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 campo LinearLayout vuoto. Aggiungerai il markup per i pulsanti durante il codelab.

Avvio dell'applicazione

Assicurati che un dispositivo sia collegato al computer. Premi l'icona verde Riproduci Pulsante Riproduci di Android Studio utilizzato per avviare il servizio dalla barra dei menu verso la parte superiore dello schermo. In questo modo dovrebbe essere avviata l'app su cui stai lavorando.

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

Schermata Impostazioni di accessibilità

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

Finestra di dialogo dell&#39;autorizzazione per i servizi di accessibilità.

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

L'esecuzione del servizio non è molto utile, poiché non abbiamo ancora aggiunto alcuna funzionalità. Cominciamo.

4. Creazione dei pulsanti

Apri action_bar.xml in res/layout. Aggiungi il markup all'interno del campo 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 i pulsanti che l'utente deve premere per attivare azioni sul dispositivo.

Apri GlobalActionBarService.java e aggiungi una variabile per archiviare 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 aumenta il layout e aggiunge la barra delle azioni verso la 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 il funzionamento. L'autorizzazione chiave che utilizzerai qui è l'autorizzazione WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY. Questa autorizzazione ti consente di disegnare direttamente sullo schermo sopra contenuti esistenti senza dover seguire un complicato flusso di autorizzazioni.

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

  • Un servizio di accessibilità si avvia quando l'utente attiva esplicitamente il servizio 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à viene interrotto 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 di Run (usa Esegui nel menu in alto e vai a Modifica configurazioni. Quindi, utilizzando il menu a discesa, modifica l'opzione di lancio da "Attività predefinita" su "Niente".

Menu a discesa per configurare le impostazioni di esecuzione per avviare un servizio utilizzando Android Studio.

Ora dovresti riuscire ad avviare il servizio utilizzando Android Studio.

Premi l'icona verde Riproduci Pulsante Riproduci di Android Studio utilizzato per avviare il servizio dalla barra dei menu verso la parte superiore dello schermo. Quindi, vai a Impostazioni > Accessibilità e attiva Global Action Bar Service.

I quattro pulsanti che formano l'interfaccia utente del servizio dovrebbero essere sovrapposti ai contenuti visualizzati sullo schermo.

overlay.png

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

Per accedere al menu del tasto di accensione, configurePowerButton() utilizza il metodo performGlobalAction(), fornito da AccessibilityService. Il codice che hai appena aggiunto è semplice: facendo clic sul pulsante viene attivata una funzione onClickListener(). Viene chiamata performGlobalAction(GLOBAL_ACTION_POWER_DIALOG) e viene mostrata la finestra di dialogo dell'accensione all'utente.

Tieni presente che le azioni globali non sono legate a nessuna visualizzazione. Altri esempi di azioni globali sono i pulsanti Indietro, Home e Recenti.

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

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

Premi l'icona verde Riproduci Pulsante Riproduci di Android Studio utilizzato per avviare il servizio dalla barra dei menu verso la parte superiore dello schermo. Quindi, vai a Impostazioni > Accessibilità e avvia Global Action Bar Service.

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

6. Configurazione del tasto del volume

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

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

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

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

@Override
protected void onServiceConnected() {
   ...

   configureVolumeButton();
}

Premi l'icona verde Riproduci Pulsante Riproduci di Android Studio utilizzato per avviare il servizio dalla barra dei menu verso la parte superiore dello schermo. Poi vai a Impostazioni > Accessibilità e avviare Global Action Bar Service.

Premi il tasto del volume per regolare il volume.

L'utente ipotetico che non è in grado di raggiungere i controlli del volume sul lato del dispositivo ora può utilizzare Global Action Bar Service per modificare (aumentare) il volume.

7. Configurazione del pulsante di scorrimento

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

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

Questo metodo crea un'azione onClickListener() che si attiva quando l'utente fa clic sul pulsante di scorrimento. Cerca di trovare un nodo scorrevole e, in caso di esito positivo, esegue l'azione di scorrimento.

Ora aggiungi configureScrollButton() a configureScrollButton():

@Override
protected void onServiceConnected() {
   ...

   configureScrollButton();
}

Premi l'icona verde Riproduci Pulsante Riproduci di Android Studio utilizzato per avviare il servizio dalla barra dei menu verso la parte superiore dello schermo. Poi vai a Impostazioni > Accessibilità e avviare Global Action Bar Service.

Premi il pulsante indietro per andare a Impostazioni > Accessibilità. Gli elementi dell'attività relativi alle impostazioni di accessibilità sono scorrevoli e, toccando il pulsante di scorrimento, viene eseguita un'azione di scorrimento. Il nostro ipotetico utente che non è in grado di eseguire facilmente azioni di scorrimento ora può utilizzare il pulsante di scorrimento per scorrere un elenco di elementi.

8. Configurazione del pulsante di scorrimento

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

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

Ora aggiungi configureSwipeButton() per configureSwipeButton():

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

Premi l'icona verde Riproduci Pulsante Riproduci di Android Studio utilizzato per avviare il servizio dalla barra dei menu verso la parte superiore dello schermo. Poi vai a Impostazioni > Accessibilità e avviare Global Action Bar Service.

Il modo più semplice per testare la funzionalità di scorrimento è aprire l'applicazione Maps installata sul telefono. Una volta caricata la mappa, toccando il pulsante Scorrimento fai scorrere lo schermo verso destra.

9. Riepilogo

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

Puoi estendere questo servizio in diversi modi. Ad esempio:

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

Questo codelab riguarda solo un piccolo sottoinsieme delle funzionalità fornite dalle API di accessibilità. L'API copre inoltre i seguenti (elenco parziale):

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

Questo codelab ti spiega come scrivere un servizio di accessibilità. Se conosci un utente con problemi di accessibilità specifici che vorresti risolvere, ora puoi creare un servizio per aiutarlo.