Crea il tuo servizio di accessibilità

Un servizio di accessibilità è un'app che migliora l'interfaccia utente per fornire assistenza utenti con disabilità o che potrebbero temporaneamente non essere in grado di interagire completamente con un dispositivo. Ad esempio, gli utenti che guidano, si occupano di un bambino, o partecipare a una festa ad alto volume, potrebbero aver bisogno di un'interfaccia aggiuntiva o alternativa feedback.

Android offre servizi di accessibilità standard, tra cui: TalkBack e gli sviluppatori possono creare e distribuire i propri servizi. Questo documento spiega le nozioni di base della creazione di un servizio di accessibilità.

Un servizio di accessibilità può essere abbinato a una normale app o creato come progetto Android autonomo. I passaggi per creare il servizio sono gli stessi in in entrambe le situazioni.

Crea il tuo servizio di accessibilità

All'interno del progetto, crea un corso che si espanda AccessibilityService:

Kotlin

package com.example.android.apis.accessibility

import android.accessibilityservice.AccessibilityService
import android.view.accessibility.AccessibilityEvent

class MyAccessibilityService : AccessibilityService() {
...
    override fun onInterrupt() {}

    override fun onAccessibilityEvent(event: AccessibilityEvent?) {}
...
}

Java

package com.example.android.apis.accessibility;

import android.accessibilityservice.AccessibilityService;
import android.view.accessibility.AccessibilityEvent;

public class MyAccessibilityService extends AccessibilityService {
...
    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
    }

    @Override
    public void onInterrupt() {
    }

...
}

Se crei un nuovo progetto per questo Service e non prevedi di avere un'app associata, puoi rimuovere la classe Activity iniziale dal tuo sorgente.

Dichiarazioni e autorizzazioni del file manifest

Le app che forniscono servizi di accessibilità devono includere dichiarazioni specifiche in la loro app si manifesta come un servizio di accessibilità da parte di un sistema operativo completo. Questa sezione illustra le impostazioni obbligatorie e facoltative per i di accessibilità di Google.

Dichiarazione relativa al servizio di accessibilità

Affinché la tua app venga considerata come un servizio di accessibilità, includi un service invece dell'elemento activity, all'interno di application nel file manifest. Inoltre, all'interno dell'elemento service, includi una filtro per intent del servizio di accessibilità. Il file manifest deve inoltre proteggere il servizio aggiungendo il parametro BIND_ACCESSIBILITY_SERVICE per garantire che solo il sistema possa eseguire il binding. Ecco un esempio:

  <application>
    <service android:name=".MyAccessibilityService"
        android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
        android:label="@string/accessibility_service_label">
      <intent-filter>
        <action android:name="android.accessibilityservice.AccessibilityService" />
      </intent-filter>
    </service>
  </application>

Configurazione del servizio di accessibilità

I servizi di accessibilità devono fornire una configurazione che specifichi i tipi di eventi di accessibilità gestiti dal servizio e informazioni aggiuntive su il servizio. La configurazione di un servizio di accessibilità è contenuta nel AccessibilityServiceInfo . Il tuo servizio può creare e impostare una configurazione utilizzando un'istanza di questo classe e setServiceInfo() in fase di runtime. Tuttavia, non tutte le opzioni di configurazione sono disponibili con questo .

Puoi includere un elemento <meta-data> nel file manifest con un riferimento a un di configurazione del deployment, che consente di impostare l'intera gamma di opzioni di accessibilità al servizio di accessibilità, come illustrato nell'esempio seguente:

<service android:name=".MyAccessibilityService">
  ...
  <meta-data
    android:name="android.accessibilityservice"
    android:resource="@xml/accessibility_service_config" />
</service>

Questo elemento <meta-data> fa riferimento a un file XML che crei in: directory delle risorse dell'app: <project_dir>/res/xml/accessibility_service_config.xml>. Il seguente codice mostra un esempio dei contenuti del file di configurazione del servizio:

<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
    android:description="@string/accessibility_service_description"
    android:packageNames="com.example.android.apis"
    android:accessibilityEventTypes="typeAllMask"
    android:accessibilityFlags="flagDefault"
    android:accessibilityFeedbackType="feedbackSpoken"
    android:notificationTimeout="100"
    android:canRetrieveWindowContent="true"
    android:settingsActivity="com.example.android.accessibility.ServiceSettingsActivity"
/>

Per ulteriori informazioni sugli attributi XML che possono essere utilizzati nel di configurazione del servizio di accessibilità, consulta il riferimento seguente documentazione:

Per ulteriori informazioni su quali impostazioni di configurazione possono essere impostate dinamicamente in fase di esecuzione, consulta AccessibilityServiceInfo documentazione di riferimento.

Configurare il servizio di accessibilità

Quando imposti le variabili di configurazione per il tuo per comunicare al sistema come e quando eseguire:

  • A quali tipi di eventi vuoi che risponda?
  • Il servizio deve essere attivo per tutte le app o solo per un pacchetto specifico? nomi utente?
  • Quali tipi di feedback diversi utilizza?

Hai due opzioni per impostare queste variabili. L'opzione compatibile con le versioni precedenti è impostarle nel codice, utilizzando setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo) Per farlo, sostituisci onServiceConnected() e configurare il servizio lì, come illustrato nell'esempio seguente:

Kotlin

override fun onServiceConnected() {
    info.apply {
        // Set the type of events that this service wants to listen to. Others
        // aren't passed to this service.
        eventTypes = AccessibilityEvent.TYPE_VIEW_CLICKED or AccessibilityEvent.TYPE_VIEW_FOCUSED

        // If you only want this service to work with specific apps, set their
        // package names here. Otherwise, when the service is activated, it
        // listens to events from all apps.
        packageNames = arrayOf("com.example.android.myFirstApp", "com.example.android.mySecondApp")

        // Set the type of feedback your service provides.
        feedbackType = AccessibilityServiceInfo.FEEDBACK_SPOKEN

        // Default services are invoked only if no package-specific services are
        // present for the type of AccessibilityEvent generated. This service is
        // app-specific, so the flag isn't necessary. For a general-purpose
        // service, consider setting the DEFAULT flag.

        // flags = AccessibilityServiceInfo.DEFAULT;

        notificationTimeout = 100
    }

    this.serviceInfo = info

}

Java

@Override
public void onServiceConnected() {
    // Set the type of events that this service wants to listen to. Others
    // aren't passed to this service.
    info.eventTypes = AccessibilityEvent.TYPE_VIEW_CLICKED |
            AccessibilityEvent.TYPE_VIEW_FOCUSED;

    // If you only want this service to work with specific apps, set their
    // package names here. Otherwise, when the service is activated, it listens
    // to events from all apps.
    info.packageNames = new String[]
            {"com.example.android.myFirstApp", "com.example.android.mySecondApp"};

    // Set the type of feedback your service provides.
    info.feedbackType = AccessibilityServiceInfo.FEEDBACK_SPOKEN;

    // Default services are invoked only if no package-specific services are
    // present for the type of AccessibilityEvent generated. This service is
    // app-specific, so the flag isn't necessary. For a general-purpose service,
    // consider setting the DEFAULT flag.

    // info.flags = AccessibilityServiceInfo.DEFAULT;

    info.notificationTimeout = 100;

    this.setServiceInfo(info);

}

La seconda opzione consiste nel configurare il servizio utilizzando un file XML. Determinati le opzioni di configurazione, canRetrieveWindowContent, sono disponibili solo se configuri il servizio utilizzando XML. La configurazione le opzioni dell'esempio precedente hanno il seguente aspetto quando vengono definite utilizzando XML:

<accessibility-service
     android:accessibilityEventTypes="typeViewClicked|typeViewFocused"
     android:packageNames="com.example.android.myFirstApp, com.example.android.mySecondApp"
     android:accessibilityFeedbackType="feedbackSpoken"
     android:notificationTimeout="100"
     android:settingsActivity="com.example.android.apis.accessibility.TestBackActivity"
     android:canRetrieveWindowContent="true"
/>

Se utilizzi il linguaggio XML, fallo riferimento nel file manifest aggiungendo un'intestazione <meta-data> al tuo la dichiarazione di servizio che punta al file XML. Se memorizzi il file XML res/xml/serviceconfig.xml, il nuovo tag avrà il seguente aspetto:

<service android:name=".MyAccessibilityService">
     <intent-filter>
         <action android:name="android.accessibilityservice.AccessibilityService" />
     </intent-filter>
     <meta-data android:name="android.accessibilityservice"
     android:resource="@xml/serviceconfig" />
</service>

Metodi del servizio di accessibilità

Un servizio di accessibilità deve estendere la classe AccessibilityService e sostituisci i seguenti metodi di quella classe. Questi metodi sono presentati in l'ordine in cui vengono chiamati dal sistema Android: dall'avvio del servizio (onServiceConnected()) fino a quando è in esecuzione (onAccessibilityEvent(), onInterrupt()), a quando viene arrestato (onUnbind()).

  • onServiceConnected(): (facoltativo) il sistema chiama questo metodo quando viene si connette al tuo servizio di accessibilità. Usa questo metodo per eseguire la configurazione una tantum passaggi per il servizio, inclusa la connessione al sistema di feedback degli utenti quali la gestione audio o la vibrazione del dispositivo. Se vuoi impostare la configurazione del servizio in fase di runtime o apportare modifiche una tantum, questa è una posizione comoda per chiamare setServiceInfo().

  • onAccessibilityEvent(): (obbligatorio) il sistema richiama questo metodo quando rileva un AccessibilityEvent che corrisponda ai parametri di filtro degli eventi specificati dai tuoi strumenti di accessibilità servizio, ad esempio quando l'utente tocca un pulsante o si concentra su un'interfaccia utente. controllo in un'app per cui il tuo servizio di accessibilità fornisce feedback. Quando il sistema chiama questo metodo, trasmette l'elemento AccessibilityEvent associato, che il servizio può quindi interpretare e utilizzare per fornire feedback utente. Questo metodo può essere chiamato molte volte durante il ciclo di vita completamente gestito di Google Cloud.

  • onInterrupt(): (obbligatorio) il sistema chiama questo metodo quando il sistema vuole interrompere il feedback fornito dal tuo servizio, di solito risposta a un'azione dell'utente, ad esempio lo spostamento dello stato attivo su un controllo diverso. Questo può essere chiamato molte volte durante il ciclo di vita del tuo servizio.

  • onUnbind(): (facoltativo) il sistema chiama questo metodo quando viene per disattivare il servizio di accessibilità. Usa questo metodo per eseguire qualsiasi procedure di arresto una tantum, inclusa la rimozione dell'allocazione del sistema di feedback degli utenti quali la gestione audio o la vibrazione del dispositivo.

Questi metodi di callback forniscono la struttura di base per l'accessibilità completamente gestito di Google Cloud. Puoi decidere come trattare i dati forniti dal sistema Android in sotto forma di oggetti AccessibilityEvent e fornire feedback all'utente. Per Per ulteriori informazioni su come ottenere informazioni da un evento di accessibilità, consulta la sezione Ottenere informazioni su dettagli dell'evento.

Registrati agli eventi di accessibilità

Una delle funzioni più importanti della configurazione del servizio di accessibilità è consentirti di specificare quali tipi di eventi di accessibilità è in grado di gestire. Specificare queste informazioni consente ai servizi di accessibilità di cooperare e ti offre la flessibilità di gestire solo eventi specifici tipi da app specifiche. Il filtro eventi può includere: di classificazione:

  • Nomi dei pacchetti: specifica i nomi dei pacchetti delle app la cui funzionalità gli eventi che vuoi vengano gestiti dal servizio. Se questo parametro viene omesso, i valori servizio di accessibilità è considerato disponibile per servizi di accessibilità eventi per qualsiasi app. Puoi impostare questo parametro nel servizio di accessibilità di configurazione con l'attributo android:packageNames come separato da virgole o utilizza AccessibilityServiceInfo.packageNames .

  • Tipi di evento: specifica i tipi di eventi di accessibilità che vuoi e un servizio Kubernetes da gestire. Puoi impostare questo parametro nel servizio di accessibilità di configurazione con l'attributo android:accessibilityEventTypes come un elenco separato dal carattere |, ad esempio accessibilityEventTypes="typeViewClicked|typeViewFocused". Oppure puoi impostare utilizzando AccessibilityServiceInfo.eventTypes .

Quando imposti il servizio di accessibilità, considera attentamente quali eventi può gestire e registrarsi solo per questi eventi. Dato che gli utenti possono attivare più di un servizio di accessibilità alla volta, il servizio non deve utilizzare eventi che non è in grado di gestire. Ricorda che altri servizi potrebbero gestire per migliorare l'esperienza utente.

Volume accessibilità

I dispositivi con Android 8.0 (livello API 26) e versioni successive includono: STREAM_ACCESSIBILITY categoria del volume, che consente di regolare il volume dell'accessibilità all'output audio del servizio, indipendentemente da altri suoni sul dispositivo.

I servizi di accessibilità possono usare questo tipo di stream impostando FLAG_ENABLE_ACCESSIBILITY_VOLUME . Puoi quindi modificare il volume audio di accessibilità del dispositivo chiamando il adjustStreamVolume() sull'istanza del dispositivo AudioManager.

Il seguente snippet di codice mostra come un servizio di accessibilità può utilizzare STREAM_ACCESSIBILITY categoria di volume:

Kotlin

import android.media.AudioManager.*

class MyAccessibilityService : AccessibilityService() {

    private val audioManager = getSystemService(AUDIO_SERVICE) as AudioManager

    override fun onAccessibilityEvent(accessibilityEvent: AccessibilityEvent) {
        if (accessibilityEvent.source.text == "Increase volume") {
            audioManager.adjustStreamVolume(AudioManager.STREAM_ACCESSIBILITY, ADJUST_RAISE, 0)
        }
    }
}

Java

import static android.media.AudioManager.*;

public class MyAccessibilityService extends AccessibilityService {
    private AudioManager audioManager =
            (AudioManager) getSystemService(AUDIO_SERVICE);

    @Override
    public void onAccessibilityEvent(AccessibilityEvent accessibilityEvent) {
        AccessibilityNodeInfo interactedNodeInfo =
                accessibilityEvent.getSource();
        if (interactedNodeInfo.getText().equals("Increase volume")) {
            audioManager.adjustStreamVolume(AudioManager.STREAM_ACCESSIBILITY,
                ADJUST_RAISE, 0);
        }
    }
}

Per ulteriori informazioni, guarda il video della sessione Novità sull'accessibilità Android di Google I/O 2017, che inizia il 06:35.

Scorciatoia Accessibilità

Sui dispositivi con Android 8.0 (livello API 26) e versioni successive, gli utenti possono attivare e disattivare il servizio di accessibilità preferito da qualsiasi schermata premendo e tenendo premuti contemporaneamente entrambi i tasti del volume. Sebbene questa scorciatoia consenta disabilita TalkBack per impostazione predefinita, gli utenti possono configurare il pulsante per attivare e disattivare qualsiasi servizio installato sul suo dispositivo.

Per consentire agli utenti di accedere a un particolare servizio di accessibilità dal menu di il servizio deve richiedere la funzionalità in fase di runtime.

Per ulteriori informazioni, guarda il video della sessione Novità sull'accessibilità Android di Google I/O 2017, che inizia il 13:25.

Pulsante Accessibilità

Sui dispositivi che utilizzano un'area di navigazione sottoposta a rendering software e con Android 8.0 (livello API 26) o superiore, il lato destro della barra di navigazione include una pulsante Accessibilità. Quando gli utenti premeno questo pulsante, possono richiamare uno dei diversi servizi e funzioni di accessibilità abilitati, a seconda dei contenuti mostrato sullo schermo.

Consentire agli utenti di richiamare un determinato servizio di accessibilità utilizzando il servizio deve aggiungere FLAG_REQUEST_ACCESSIBILITY_BUTTON flag android:accessibilityFlags di un oggetto AccessibilityServiceInfo . Il servizio può quindi registrare i callback utilizzando registerAccessibilityButtonCallback()

Il seguente snippet di codice mostra come configurare un modello di servizio per rispondere all'utente premendo il pulsante Accessibilità:

Kotlin

private var mAccessibilityButtonController: AccessibilityButtonController? = null
private var accessibilityButtonCallback:
        AccessibilityButtonController.AccessibilityButtonCallback? = null
private var mIsAccessibilityButtonAvailable: Boolean = false

override fun onServiceConnected() {
    mAccessibilityButtonController = accessibilityButtonController
    mIsAccessibilityButtonAvailable =
            mAccessibilityButtonController?.isAccessibilityButtonAvailable ?: false

    if (!mIsAccessibilityButtonAvailable) return

    serviceInfo = serviceInfo.apply {
        flags = flags or AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON
    }

    accessibilityButtonCallback =
        object : AccessibilityButtonController.AccessibilityButtonCallback() {
            override fun onClicked(controller: AccessibilityButtonController) {
                Log.d("MY_APP_TAG", "Accessibility button pressed!")

                // Add custom logic for a service to react to the
                // accessibility button being pressed.
            }

            override fun onAvailabilityChanged(
                    controller: AccessibilityButtonController,
                    available: Boolean
            ) {
                if (controller == mAccessibilityButtonController) {
                    mIsAccessibilityButtonAvailable = available
                }
            }
    }

    accessibilityButtonCallback?.also {
        mAccessibilityButtonController?.registerAccessibilityButtonCallback(it, null)
    }
}

Java

private AccessibilityButtonController accessibilityButtonController;
private AccessibilityButtonController
        .AccessibilityButtonCallback accessibilityButtonCallback;
private boolean mIsAccessibilityButtonAvailable;

@Override
protected void onServiceConnected() {
    accessibilityButtonController = getAccessibilityButtonController();
    mIsAccessibilityButtonAvailable =
            accessibilityButtonController.isAccessibilityButtonAvailable();

    if (!mIsAccessibilityButtonAvailable) {
        return;
    }

    AccessibilityServiceInfo serviceInfo = getServiceInfo();
    serviceInfo.flags
            |= AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON;
    setServiceInfo(serviceInfo);

    accessibilityButtonCallback =
        new AccessibilityButtonController.AccessibilityButtonCallback() {
            @Override
            public void onClicked(AccessibilityButtonController controller) {
                Log.d("MY_APP_TAG", "Accessibility button pressed!");

                // Add custom logic for a service to react to the
                // accessibility button being pressed.
            }

            @Override
            public void onAvailabilityChanged(
              AccessibilityButtonController controller, boolean available) {
                if (controller.equals(accessibilityButtonController)) {
                    mIsAccessibilityButtonAvailable = available;
                }
            }
        };

    if (accessibilityButtonCallback != null) {
        accessibilityButtonController.registerAccessibilityButtonCallback(
                accessibilityButtonCallback, null);
    }
}

Per ulteriori informazioni, guarda il video della sessione Novità sull'accessibilità Android di Google I/O 2017, che inizia il 16:28.

Gesti con sensore di impronte

I servizi di accessibilità su dispositivi con Android 8.0 (livello API 26) o versioni successive può rispondere agli scorrimenti direzionali (su, giù, sinistra e destra) lungo la sensore di impronte digitali. Per configurare un servizio in modo che riceva callback su questi interazioni, completa la seguente sequenza:

  1. Dichiara il USE_BIOMETRIC autorizzazione e CAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES funzionalità.
  2. Imposta la FLAG_REQUEST_FINGERPRINT_GESTURES all'interno dell'attributo android:accessibilityFlags.
  3. Registrati per le richiamate utilizzando registerFingerprintGestureCallback().
di Gemini Advanced.

Tieni presente che non tutti i dispositivi includono sensori di impronte digitali. Per identificare se un dispositivo supporta il sensore, usa isHardwareDetected() . Anche su un dispositivo che include un sensore di impronte digitali, il servizio non può utilizzare il sensore quando è in uso per l'autenticazione. Per identificare quando se il sensore è disponibile, chiama isGestureDetectionAvailable() e implementare onGestureDetectionAvailabilityChanged() di Google.

Il seguente snippet di codice mostra un esempio di utilizzo dei gesti dell'impronta per navigare sul tabellone di gioco virtuale:

// AndroidManifest.xml
<manifest ... >
    <uses-permission android:name="android.permission.USE_FINGERPRINT" />
    ...
    <application>
        <service android:name="com.example.MyFingerprintGestureService" ... >
            <meta-data
                android:name="android.accessibilityservice"
                android:resource="@xml/myfingerprintgestureservice" />
        </service>
    </application>
</manifest>
// myfingerprintgestureservice.xml
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
    ...
    android:accessibilityFlags=" ... |flagRequestFingerprintGestures"
    android:canRequestFingerprintGestures="true"
    ... />

Kotlin

// MyFingerprintGestureService.kt
import android.accessibilityservice.FingerprintGestureController.*

class MyFingerprintGestureService : AccessibilityService() {

    private var gestureController: FingerprintGestureController? = null
    private var fingerprintGestureCallback:
            FingerprintGestureController.FingerprintGestureCallback? = null
    private var mIsGestureDetectionAvailable: Boolean = false

    override fun onCreate() {
        gestureController = fingerprintGestureController
        mIsGestureDetectionAvailable = gestureController?.isGestureDetectionAvailable ?: false
    }

    override fun onServiceConnected() {
        if (mFingerprintGestureCallback != null || !mIsGestureDetectionAvailable) return

        fingerprintGestureCallback =
                object : FingerprintGestureController.FingerprintGestureCallback() {
                    override fun onGestureDetected(gesture: Int) {
                        when (gesture) {
                            FINGERPRINT_GESTURE_SWIPE_DOWN -> moveGameCursorDown()
                            FINGERPRINT_GESTURE_SWIPE_LEFT -> moveGameCursorLeft()
                            FINGERPRINT_GESTURE_SWIPE_RIGHT -> moveGameCursorRight()
                            FINGERPRINT_GESTURE_SWIPE_UP -> moveGameCursorUp()
                            else -> Log.e(MY_APP_TAG, "Error: Unknown gesture type detected!")
                        }
                    }

                    override fun onGestureDetectionAvailabilityChanged(available: Boolean) {
                        mIsGestureDetectionAvailable = available
                    }
                }

        fingerprintGestureCallback?.also {
            gestureController?.registerFingerprintGestureCallback(it, null)
        }
    }
}

Java

// MyFingerprintGestureService.java
import static android.accessibilityservice.FingerprintGestureController.*;

public class MyFingerprintGestureService extends AccessibilityService {
    private FingerprintGestureController gestureController;
    private FingerprintGestureController
            .FingerprintGestureCallback fingerprintGestureCallback;
    private boolean mIsGestureDetectionAvailable;

    @Override
    public void onCreate() {
        gestureController = getFingerprintGestureController();
        mIsGestureDetectionAvailable =
                gestureController.isGestureDetectionAvailable();
    }

    @Override
    protected void onServiceConnected() {
        if (fingerprintGestureCallback != null
                || !mIsGestureDetectionAvailable) {
            return;
        }

        fingerprintGestureCallback =
               new FingerprintGestureController.FingerprintGestureCallback() {
            @Override
            public void onGestureDetected(int gesture) {
                switch (gesture) {
                    case FINGERPRINT_GESTURE_SWIPE_DOWN:
                        moveGameCursorDown();
                        break;
                    case FINGERPRINT_GESTURE_SWIPE_LEFT:
                        moveGameCursorLeft();
                        break;
                    case FINGERPRINT_GESTURE_SWIPE_RIGHT:
                        moveGameCursorRight();
                        break;
                    case FINGERPRINT_GESTURE_SWIPE_UP:
                        moveGameCursorUp();
                        break;
                    default:
                        Log.e(MY_APP_TAG,
                                  "Error: Unknown gesture type detected!");
                        break;
                }
            }

            @Override
            public void onGestureDetectionAvailabilityChanged(boolean available) {
                mIsGestureDetectionAvailable = available;
            }
        };

        if (fingerprintGestureCallback != null) {
            gestureController.registerFingerprintGestureCallback(
                    fingerprintGestureCallback, null);
        }
    }
}

Per ulteriori informazioni, guarda il video della sessione Novità sull'accessibilità Android di Google I/O 2017, che inizia il 09:03.

Sintesi vocale multilingue

A partire da Android 8.0 (livello API 26), il servizio di sintesi vocale di Android possono identificare e pronunciare frasi in più lingue in un unico blocco di testo. Per attivare questa funzionalità di cambio automatico della lingua in un modello di racchiudi tutte le stringhe LocaleSpan oggetti, come mostrato nel seguente snippet di codice:

Kotlin

val localeWrappedTextView = findViewById<TextView>(R.id.my_french_greeting_text).apply {
    text = wrapTextInLocaleSpan("Bonjour!", Locale.FRANCE)
}

private fun wrapTextInLocaleSpan(originalText: CharSequence, loc: Locale): SpannableStringBuilder {
    return SpannableStringBuilder(originalText).apply {
        setSpan(LocaleSpan(loc), 0, originalText.length - 1, 0)
    }
}

Java

TextView localeWrappedTextView = findViewById(R.id.my_french_greeting_text);
localeWrappedTextView.setText(wrapTextInLocaleSpan("Bonjour!", Locale.FRANCE));

private SpannableStringBuilder wrapTextInLocaleSpan(
        CharSequence originalText, Locale loc) {
    SpannableStringBuilder myLocaleBuilder =
            new SpannableStringBuilder(originalText);
    myLocaleBuilder.setSpan(new LocaleSpan(loc), 0,
            originalText.length() - 1, 0);
    return myLocaleBuilder;
}

Per ulteriori informazioni, guarda il video della sessione Novità sull'accessibilità Android di Google I/O 2017, che inizia il 22:59.

Agire per conto degli utenti

A partire dal 2011, i servizi di accessibilità possono agire per conto degli utenti, ad esempio: la modifica dell'elemento attivo dell'input e la selezione (attivazione) degli elementi dell'interfaccia utente. Nella 2012, la gamma di azioni è stata ampliata per includere elenchi di scorrimento e interazioni con campi di testo. I servizi di accessibilità possono inoltre eseguire azioni a livello globale, ad esempio per andare alla schermata Home, premere il pulsante Indietro e aprire schermata delle notifiche ed elenco delle app recenti. Dal 2012, Android include accessibilità, che rende tutti gli elementi visibili selezionabili da un di accessibilità di Google.

Queste funzionalità consentono agli sviluppatori di servizi di accessibilità di creare modalità di navigazione, come la navigazione tramite gesti, e offri agli utenti con disabilità un migliore controllo dei propri dispositivi Android.

Ascolta i gesti

I servizi di accessibilità possono ascoltare gesti specifici e rispondere agendo su per conto di un utente. Questa funzione richiede che la tua richiesta del servizio di accessibilità l'attivazione della funzione Esplora al tocco. Il tuo servizio può richiederlo l'attivazione impostando il parametro flags membro dell'istanza AccessibilityServiceInfo del servizio per FLAG_REQUEST_TOUCH_EXPLORATION_MODE, come mostrato nell'esempio seguente.

Kotlin

class MyAccessibilityService : AccessibilityService() {

    override fun onCreate() {
        serviceInfo.flags = AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE
    }
    ...
}

Java

public class MyAccessibilityService extends AccessibilityService {
    @Override
    public void onCreate() {
        getServiceInfo().flags = AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE;
    }
    ...
}

Quando il servizio richiede l'attivazione di Esplora al tocco, l'utente deve consentire attivare la funzionalità, se non è già attiva. Quando questa funzionalità viene attiva, il servizio riceve notifica dei gesti di accessibilità tramite il tuo servizio onGesture() e può rispondere agendo per conto dell'utente.

Gesti continui

I dispositivi con Android 8.0 (livello API 26) supportano i gesti continui oppure gesti programmatici che contengono più di un Path.

Quando specifichi una sequenza di tratti, puoi specificare che appartengano alla lo stesso gesto programmatico utilizzando l'argomento finale willContinue nella GestureDescription.StrokeDescription come mostrato nello snippet di codice riportato di seguito:

Kotlin

// Simulates an L-shaped drag path: 200 pixels right, then 200 pixels down.
private fun doRightThenDownDrag() {
    val dragRightPath = Path().apply {
        moveTo(200f, 200f)
        lineTo(400f, 200f)
    }
    val dragRightDuration = 500L // 0.5 second

    // The starting point of the second path must match
    // the ending point of the first path.
    val dragDownPath = Path().apply {
        moveTo(400f, 200f)
        lineTo(400f, 400f)
    }
    val dragDownDuration = 500L
    val rightThenDownDrag = GestureDescription.StrokeDescription(
            dragRightPath,
            0L,
            dragRightDuration,
            true
    ).apply {
        continueStroke(dragDownPath, dragRightDuration, dragDownDuration, false)
    }
}

Java

// Simulates an L-shaped drag path: 200 pixels right, then 200 pixels down.
private void doRightThenDownDrag() {
    Path dragRightPath = new Path();
    dragRightPath.moveTo(200, 200);
    dragRightPath.lineTo(400, 200);
    long dragRightDuration = 500L; // 0.5 second

    // The starting point of the second path must match
    // the ending point of the first path.
    Path dragDownPath = new Path();
    dragDownPath.moveTo(400, 200);
    dragDownPath.lineTo(400, 400);
    long dragDownDuration = 500L;
    GestureDescription.StrokeDescription rightThenDownDrag =
            new GestureDescription.StrokeDescription(dragRightPath, 0L,
            dragRightDuration, true);
    rightThenDownDrag.continueStroke(dragDownPath, dragRightDuration,
            dragDownDuration, false);
}

Per ulteriori informazioni, guarda il video della sessione Novità sull'accessibilità Android di Google I/O 2017, che inizia il 15:47.

Usa le azioni di accessibilità

I servizi di accessibilità possono agire per conto degli utenti al fine di semplificare le interazioni con le tue app e aumentare la produttività. La capacità dei servizi di accessibilità di eseguire azioni è stata aggiunta nel 2011 e notevolmente ampliata nel 2012.

Per agire per conto degli utenti, il tuo servizio di accessibilità deve registrarsi di ricevere eventi dalle app e di richiedere l'autorizzazione per la visualizzazione dei contenuti di app impostando android:canRetrieveWindowContent su true nel file di configurazione del servizio. Quando gli eventi vengono ricevuti da può recuperare AccessibilityNodeInfo dall'evento utilizzando getSource(). Con l'oggetto AccessibilityNodeInfo, il servizio può quindi esplorare la vista della gerarchia per determinare quale azione intraprendere e poi agire per l'utente utilizzando performAction()

Kotlin

class MyAccessibilityService : AccessibilityService() {

    override fun onAccessibilityEvent(event: AccessibilityEvent) {
        // Get the source node of the event.
        event.source?.apply {

            // Use the event and node information to determine what action to
            // take.

            // Act on behalf of the user.
            performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD)

            // Recycle the nodeInfo object.
            recycle()
        }
    }
    ...
}

Java

public class MyAccessibilityService extends AccessibilityService {

    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
        // Get the source node of the event.
        AccessibilityNodeInfo nodeInfo = event.getSource();

        // Use the event and node information to determine what action to take.

        // Act on behalf of the user.
        nodeInfo.performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);

        // Recycle the nodeInfo object.
        nodeInfo.recycle();
    }
    ...
}

Il metodo performAction() consente al tuo servizio di intervenire all'interno di un dell'app. Se il servizio deve eseguire un'azione globale, ad esempio andare alla schermata Home, toccare il pulsante Indietro o aprire schermata delle notifiche o dell'elenco di app recenti, quindi usa performGlobalAction() .

Utilizza i tipi di impostazione dello stato attivo

Nel 2012, Android ha introdotto un obiettivo dell'interfaccia utente chiamato focus per l'accessibilità. I servizi di accessibilità possono utilizzare questa impostazione per selezionare qualsiasi interfaccia utente visibile dell'elemento e agire su di esso. Questo tipo di elemento attivo è diverso da input dell'input, che determina quale elemento dell'interfaccia utente sullo schermo riceve input quando un utente digita caratteri, premi Invio su una tastiera o sposta il centro del dispositivo tasto di un D-pad.

È possibile che un elemento di un'interfaccia utente abbia lo stato attivo per l'input mentre un altro elemento è incentrato sull'accessibilità. Lo scopo dell'accessibilità è per fornire servizi di accessibilità con un metodo di interazione con elementi su uno schermo, a prescindere dal fatto che l'elemento sia attivabile dall'input da da una prospettiva di sistema. Per garantire l'interazione del servizio di accessibilità correttamente con le app di input, segui le linee guida per testare il accessibilità per testare il servizio durante l'utilizzo di un'app tipica.

Un servizio di accessibilità può determinare quale elemento dell'interfaccia utente ha input la messa a fuoco o l'accessibilità utilizzando AccessibilityNodeInfo.findFocus() . Puoi anche cercare elementi che possono essere selezionati con lo stato attivo dell'input utilizzando focusSearch() . Infine, il servizio di accessibilità può impostare il focus dell'accessibilità utilizzando il performAction(AccessibilityNodeInfo.ACTION_SET_ACCESSIBILITY_FOCUS) .

Raccogliere informazioni

I servizi di accessibilità utilizzano metodi standard per la raccolta e la rappresentazione delle chiavi Unità di informazioni fornite dall'utente, ad esempio dettagli degli eventi, testo e numeri.

Ottieni dettagli sulle modifiche alle finestre

Android 9 (livello API 28) e versioni successive consente alle app di tenere traccia degli aggiornamenti delle finestre quando un'app ridisegna più finestre contemporaneamente. Quando TYPE_WINDOWS_CHANGED utilizza getWindowChanges() API per determinare come cambiano le finestre. Durante un aggiornamento multi-finestra, produce il proprio insieme di eventi. Il metodo getSource() restituisce la radice della finestra associata a ogni evento.

Se un'app definisce il riquadro Accessibilità per la sua View, il tuo servizio può riconoscere quando la UI dell'app viene aggiornata. Quando TYPE_WINDOW_STATE_CHANGED si verifica un evento, utilizza i tipi restituiti getContentChangeTypes() per determinare come cambia la finestra. Ad esempio, il framework può rilevare un riquadro ha un nuovo titolo o quando un riquadro scompare.

Visualizza i dettagli dell'evento

Android fornisce informazioni sull'interfaccia utente ai servizi di accessibilità interazione attraverso AccessibilityEvent oggetti. Nelle versioni precedenti di Android, le informazioni disponibili in un evento di accessibilità, fornendo al contempo dettagli sul controllo dell'interfaccia utente selezionati dagli utenti, offerti con limitazioni informazioni contestuali. In molti casi, queste informazioni di contesto mancanti potrebbero fondamentale per comprendere il significato del controllo selezionato.

Un esempio di interfaccia in cui il contesto è fondamentale è un calendario o un giorno planner. Se l'utente seleziona una fascia oraria delle 16:00 in un elenco di giorni da lunedì a venerdì e il servizio di accessibilità annuncia "16:00", ma non annuncia il giorno della settimana nome, il giorno del mese o il nome del mese, il feedback risultante poco chiara. In questo caso, il contesto di un controllo dell'interfaccia utente è fondamentale un utente che desidera pianificare una riunione.

Dal 2011, Android aumenta significativamente la quantità di informazioni di accessibilità è in grado di ottenere informazioni su un'interazione con l'interfaccia utente componendo eventi di accessibilità basati sulla gerarchia delle visualizzazioni. Una gerarchia di visualizzazione è l'insieme componenti dell'interfaccia utente che contengono il componente (i relativi elementi principali) e l'utente elementi dell'interfaccia che potrebbero essere contenuti da quel componente (i relativi elementi secondari). Nella in questo modo Android può fornire maggiori dettagli sugli eventi di accessibilità, i servizi di accessibilità forniscono feedback più utili agli utenti.

Un servizio di accessibilità ottiene informazioni su un evento dell'interfaccia utente tramite un valore AccessibilityEvent trasmesso dal sistema Metodo di callback onAccessibilityEvent(). Questo oggetto fornisce dettagli evento, tra cui il tipo di oggetto su cui si agisce, il suo testo descrittivo e altri dettagli.

  • AccessibilityEvent.getRecordCount() e getRecord(int): questi metodi consentono di recuperare l'insieme AccessibilityRecord che contribuiscono a AccessibilityEvent trasmessi a te dall'oggetto di un sistema operativo completo. Questo livello di dettaglio fornisce maggiore contesto per l'evento che attiva il servizio di accessibilità.

  • AccessibilityRecord.getSource(): questo metodo restituisce un oggetto AccessibilityNodeInfo. Questo oggetto ti consente richiedere la gerarchia del layout di visualizzazione (principali e secondari) del componente che ha origine l'evento di accessibilità. Questa funzione consente a un modello di indaga sull'intero contesto di un evento, inclusi i contenuti e lo stato di eventuali viste contenenti o secondarie.

di Gemini Advanced.

La piattaforma Android offre a un AccessibilityService la possibilità di eseguire query la gerarchia delle viste, raccogliendo informazioni sul componente dell'interfaccia utente che genera sia di un evento sia dei relativi eventi principali e secondari. Per farlo, imposta la riga seguente nella tua configurazione XML:

android:canRetrieveWindowContent="true"

Dopodiché, recupera un oggetto AccessibilityNodeInfo utilizzando getSource(). Questa chiamata restituisce un oggetto solo se la finestra da cui ha origine l'evento è sarà sempre la finestra attiva. In caso contrario, viene restituito un valore nullo, quindi comportati di conseguenza.

Nell'esempio seguente, il codice esegue le seguenti operazioni quando viene ricevuto un evento:

  1. Cattura immediatamente l'elemento principale della vista da cui ha avuto origine l'evento.
  2. In questa visualizzazione cerca un'etichetta e una casella di controllo come viste secondarie.
  3. Se le individua, crea una stringa da segnalare all'utente, indicando la e se è stata selezionata o meno.

Se in qualsiasi momento viene restituito un valore nullo mentre attraversa la gerarchia di visualizzazione, il metodo si arrende silenziosamente.

Kotlin

// Alternative onAccessibilityEvent that uses AccessibilityNodeInfo.

override fun onAccessibilityEvent(event: AccessibilityEvent) {

    val source: AccessibilityNodeInfo = event.source ?: return

    // Grab the parent of the view that fires the event.
    val rowNode: AccessibilityNodeInfo = getListItemNodeInfo(source) ?: return

    // Using this parent, get references to both child nodes, the label, and the
    // checkbox.
    val taskLabel: CharSequence = rowNode.getChild(0)?.text ?: run {
        rowNode.recycle()
        return
    }

    val isComplete: Boolean = rowNode.getChild(1)?.isChecked ?: run {
        rowNode.recycle()
        return
    }

    // Determine what the task is and whether it's complete based on the text
    // inside the label, and the state of the checkbox.
    if (rowNode.childCount < 2 || !rowNode.getChild(1).isCheckable) {
        rowNode.recycle()
        return
    }

    val completeStr: String = if (isComplete) {
        getString(R.string.checked)
    } else {
        getString(R.string.not_checked)
    }
    val reportStr = "$taskLabel$completeStr"
    speakToUser(reportStr)
}

Java

// Alternative onAccessibilityEvent that uses AccessibilityNodeInfo.

@Override
public void onAccessibilityEvent(AccessibilityEvent event) {

    AccessibilityNodeInfo source = event.getSource();
    if (source == null) {
        return;
    }

    // Grab the parent of the view that fires the event.
    AccessibilityNodeInfo rowNode = getListItemNodeInfo(source);
    if (rowNode == null) {
        return;
    }

    // Using this parent, get references to both child nodes, the label, and the
    // checkbox.
    AccessibilityNodeInfo labelNode = rowNode.getChild(0);
    if (labelNode == null) {
        rowNode.recycle();
        return;
    }

    AccessibilityNodeInfo completeNode = rowNode.getChild(1);
    if (completeNode == null) {
        rowNode.recycle();
        return;
    }

    // Determine what the task is and whether it's complete based on the text
    // inside the label, and the state of the checkbox.
    if (rowNode.getChildCount() < 2 || !rowNode.getChild(1).isCheckable()) {
        rowNode.recycle();
        return;
    }

    CharSequence taskLabel = labelNode.getText();
    final boolean isComplete = completeNode.isChecked();
    String completeStr = null;

    if (isComplete) {
        completeStr = getString(R.string.checked);
    } else {
        completeStr = getString(R.string.not_checked);
    }
    String reportStr = taskLabel + completeStr;
    speakToUser(reportStr);
}

Ora hai a disposizione un servizio di accessibilità completo e funzionante. Prova a configurare come interagisce con l'utente aggiungendo la sintesi vocale di Android motore di ricerca o l'utilizzo di una Vibrator per fornire la tecnologia aptica feedback.

Elabora testo

I dispositivi con Android 8.0 (livello API 26) e versioni successive includono diversi di elaborazione del testo che semplificano i servizi di accessibilità identificare e operare su specifiche unità di testo che appaiono sullo schermo.

Descrizioni comandi

Android 9 (livello API 28) introduce diverse funzionalità che consentono di accedere a: tooltip nell'interfaccia utente di un'app. Utilizza le funzionalità di getTooltipText() per leggere il testo di una descrizione comando e utilizzare ACTION_SHOW_TOOLTIP e ACTION_HIDE_TOOLTIP per indicare alle istanze di View di mostrare o nascondere le relative descrizioni comando.

Testo suggerimento

A partire dal 2017, Android include diversi metodi per interagire con testo del suggerimento dell'oggetto basato su testo:

  • La isShowingHintText() e setShowingHintText() indicano e impostano, rispettivamente, se il testo attuale del nodo rappresenta il testo del suggerimento del nodo.
  • getHintText() consente di accedere al testo del suggerimento stesso. Anche se un oggetto non viene visualizzato testo del suggerimento, chiamata a getHintText() riuscita.

Posizioni dei caratteri di testo sullo schermo

Sui dispositivi con Android 8.0 (livello API 26) e versioni successive, i servizi di accessibilità Possibilità di determinare le coordinate dello schermo per il riquadro di delimitazione di ciascun carattere all'interno di un widget TextView. Servizi trova queste coordinate chiamando refreshWithExtraData(), passaggio EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY come primo argomento e un oggetto Bundle come secondo argomento. Quando il metodo viene eseguito, il sistema compila Argomento Bundle con un array assegnabile di Rect oggetti. Ogni oggetto Rect rappresenta il riquadro di delimitazione di un determinato carattere.

Valori di intervallo unilaterali standardizzati

Alcuni oggetti AccessibilityNodeInfo utilizzano un'istanza AccessibilityNodeInfo.RangeInfo per indicare che un elemento dell'interfaccia utente può assumere un intervallo di valori. Quando crei un intervallo con RangeInfo.obtain(), o quando si recuperano i valori estremi dell'intervallo utilizzando getMin() e getMax(), tieni presente che i dispositivi con Android 8.0 (livello API 26) e versioni successive rappresentano intervalli unilaterali in modo standardizzato:

Rispondere agli eventi di accessibilità

Ora che il servizio è configurato per eseguire e rimanere in ascolto degli eventi, scrivi il codice in modo che sa cosa fare quando arriva un AccessibilityEvent. Per iniziare, esegui l'override onAccessibilityEvent(AccessibilityEvent) . In questo metodo, utilizza getEventType() per determinare il tipo di evento getContentDescription() per estrarre eventuale testo dell'etichetta associato alla vista che attiva l'evento:

Kotlin

override fun onAccessibilityEvent(event: AccessibilityEvent) {
    var eventText: String = when (event.eventType) {
        AccessibilityEvent.TYPE_VIEW_CLICKED -> "Clicked: "
        AccessibilityEvent.TYPE_VIEW_FOCUSED -> "Focused: "
        else -> ""
    }

    eventText += event.contentDescription

    // Do something nifty with this text, like speak the composed string back to
    // the user.
    speakToUser(eventText)
    ...
}

Java

@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
    final int eventType = event.getEventType();
    String eventText = null;
    switch(eventType) {
        case AccessibilityEvent.TYPE_VIEW_CLICKED:
            eventText = "Clicked: ";
            break;
        case AccessibilityEvent.TYPE_VIEW_FOCUSED:
            eventText = "Focused: ";
            break;
    }

    eventText = eventText + event.getContentDescription();

    // Do something nifty with this text, like speak the composed string back to
    // the user.
    speakToUser(eventText);
    ...
}

Risorse aggiuntive

Per saperne di più, consulta le seguenti risorse:

Guide

Codelab