Tworzenie usługi ułatwień dostępu na Androida

1. Wprowadzenie

Usługi ułatwień dostępu to funkcja platformy Androida, która służy do dostarczania użytkownikowi opinii na temat alternatywnej nawigacji w imieniu aplikacji zainstalowanych na urządzeniach z Androidem. Usługa ułatwień dostępu może komunikować się z użytkownikiem w imieniu aplikacji, na przykład konwertując tekst na mowę lub sygnalizując reakcję haptyczną, gdy użytkownik najedzie kursorem na ważny obszar ekranu. Z tego ćwiczenia w Codelabs dowiesz się, jak stworzyć bardzo prostą usługę ułatwień dostępu.

Czym jest usługa ułatwień dostępu?

Usługa ułatwień dostępu pomaga użytkownikom z niepełnosprawnościami korzystać z urządzeń i aplikacji z Androidem. Jest to działająca od dawna usługa z podwyższonymi uprawnieniami, która pomaga użytkownikom w przetwarzaniu informacji na ekranie i umożliwia im rzeczową interakcję z urządzeniem.

Przykłady popularnych usług ułatwień dostępu

  • Switch Access: umożliwia użytkownikom Androida z ograniczeniami ruchowymi interakcję z urządzeniami za pomocą co najmniej jednego przełącznika.
  • Voice Access (beta): umożliwia użytkownikom Androida z ograniczeniami ruchowymi sterowanie urządzeniem przy użyciu poleceń głosowych.
  • TalkBack: czytnik ekranu często używany przez osoby z wadą wzroku lub niewidomym.

Tworzenie usługi ułatwień dostępu

Google świadczy na Androida takie usługi jak Switch Access, Voice Access czy TalkBack, ale nie jest w stanie obsłużyć wszystkich użytkowników z niepełnosprawnościami. Wielu użytkowników z niepełnosprawnościami ma wyjątkowe potrzeby, dlatego interfejsy API Androida do tworzenia usług ułatwień dostępu są otwarte, a deweloperzy mogą tworzyć usługi ułatwień dostępu i rozpowszechniać je w Sklepie Play.

Co będziesz tworzyć

W ramach tego ćwiczenia w Codelabs utworzysz prostą usługę, która zrobi kilka przydatnych rzeczy za pomocą interfejsu Accessibility API. Jeśli jesteś w stanie napisać podstawową aplikację na Androida, możesz opracować podobną usługę.

Interfejs Accessibility API jest wszechstronny – kod tworzonej usługi zawiera tylko 4 pliki i zawiera ok. 200 wierszy kodu.

Użytkownik

Utworzysz usługę dla hipotetycznego użytkownika o następujących cechach:

  • Użytkownik ma problem z dostępem do bocznych przycisków urządzenia.
  • Użytkownik ma problemy z przewijaniem lub przesuwaniem.

Szczegóły usługi

Usługa wyświetli na ekranie globalny pasek działań. Użytkownik może klikać przyciski na tym pasku, aby wykonywać te czynności:

  1. Wyłącz urządzenie bez naciskania przycisku zasilania z boku telefonu.
  2. Wyreguluj głośność bez dotykania przycisków głośności z boku telefonu.
  3. Wykonuj działania przewijania bez przewijania.
  4. Możesz przesuwać palcem bez konieczności wykonywania gestu przesuwania.

Czego potrzebujesz

W tym ćwiczeniu w Codelabs zakładamy, że będziesz używać:

  1. Komputer z Androidem Studio.
  2. Terminal do wykonywania prostych poleceń powłoki.
  3. Urządzenie z Androidem 7.0 (Nougat) podłączone do komputera, którego będziesz używać do programowania.

Zaczynajmy!

2. Przygotowanie

W terminalu utwórz katalog, w którym będziesz pracować. Przejdź do tego katalogu.

Pobieranie kodu

Możesz skopiować repozytorium zawierające kod tego ćwiczenia z programowania:

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

Repozytorium zawiera kilka projektów Android Studio. W Android Studio otwórz usługę GlobalActionBarService.

Uruchom Android Studio, klikając ikonę Studio:

Logo służące do uruchamiania Android Studio.

Wybierz opcję Importuj projekt (Eclipse ADT, Gradle itp.):

Ekran powitalny Android Studio.

Przejdź do lokalizacji, w której sklonowano źródło, i wybierz GlobalActionBarService.

Następnie za pomocą terminala przejdź do katalogu głównego.

3. Interpretacja kodu początkowego

Poznaj otwarty projekt.

Prosty szkielet usługi ułatwień dostępu został już dla Ciebie utworzony. Cały kod, który będziesz tworzyć w ramach tego ćwiczenia, będzie ograniczony do tych 4 plików:

  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

Oto przewodnik po zawartości każdego pliku.

AndroidManifest.xml

Informacje o usłudze ułatwień dostępu są zadeklarowane w pliku manifestu:

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

W pliku AndroidManifest.xml zadeklarowano te 3 wymagane elementy:

  1. Uprawnienie do powiązania z usługą ułatwień dostępu:
<service
    ...
    android:permission = "android.permission.BIND_ACCESSIBILITY_SERVICE">
    ...             
</service>
  1. Intencja AccessibilityService:
<intent-filter>
   <action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
  1. Lokalizacja pliku zawierającego metadane tworzonej usługi:
<meta-data
       ...
       android:resource="@xml/global_action_bar_service" />
</service>

global_action_bar_service.xml

Ten plik zawiera metadane dotyczące usługi.

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

Przy użyciu elementu &lt;accessibility-service&gt; zdefiniowano te metadane:

  1. Typ informacji zwrotnych dla tej usługi (w tym ćwiczeniu w Codelabs używa wartości feedbackOgólny, co jest dobrym ustawieniem domyślnym).
  2. Flagi ułatwień dostępu usługi (to ćwiczenie z programowania korzysta z flag domyślnych).
  3. Funkcje wymagane przez usługę:
  4. Aby można było obsługiwać gesty przesuwania, parametr android:canPerformGestures ma wartość android:canPerformGestures.
  5. Aby można było pobrać zawartość okna, atrybut android:canRetrieveWindowContent ma wartość android:canRetrieveWindowContent.

GlobalActionBarService.java

Większość kodu usługi ułatwień dostępu znajduje się w pliku GlobalActionBarService.java. Początkowo plik zawiera absolutny minimalny kod usługi ułatwień dostępu:

  1. Klasa, która stanowi rozszerzenie usługi AccessibilityService.
  2. Kilka wymaganych, zastąpionych metod (pozostałe puste w tym ćwiczeniu z programowania).
public class GlobalActionBarService extends AccessibilityService {

   @Override
   public void onAccessibilityEvent(AccessibilityEvent event) {

   }

   @Override
   public void onInterrupt() {

   }
}

Kod dodasz do tego pliku w ramach tych ćwiczeń.

action_bar.xml

Usługa udostępnia interfejs z 4 przyciskami, a plik układu action_bar.xml zawiera znaczniki do ich wyświetlania:

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

Na razie ten plik zawiera pusty układ LinearLayout. W ramach tych ćwiczeń dodasz znaczniki do przycisków.

Uruchamianie aplikacji

Sprawdź, czy urządzenie jest podłączone do komputera. Na pasku menu w górnej części ekranu naciśnij zieloną ikonę Odtwórz Przycisk Play w Android Studio użyty do uruchomienia usługi. Powinna uruchomić się aplikacja, nad którą pracujesz.

Otwórz Ustawienia > Ułatwienia dostępu. Na urządzeniu jest zainstalowana globalna usługa paska działań.

Ekran ustawień ułatwień dostępu

Kliknij Global Action Bar Service (Usługa globalnego paska działań) i ją włącz. Powinno się wyświetlić następujące okno uprawnień:

Okno uprawnień usługi ułatwień dostępu.

Usługa ułatwień dostępu prosi o pozwolenie na obserwowanie działań użytkownika, pobieranie zawartości okna i wykonywanie gestów w imieniu użytkownika. Jeśli korzystasz z zewnętrznej usługi ułatwień dostępu, upewnij się, że ufasz jej źródłom.

Uruchomienie usługi niewiele daje, ponieważ nie dodaliśmy jeszcze żadnej funkcji. Zaczynajmy.

4. Tworzenie przycisków

Otwórz plik action_bar.xml w res/layout. Dodaj znaczniki do obecnie pustego elementu LinearLayout:

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

Spowoduje to utworzenie przycisków, które użytkownik będzie musiał nacisnąć, aby aktywować działania na urządzeniu.

Otwórz plik GlobalActionBarService.java i dodaj zmienną do przechowywania układu paska działań:

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

Teraz dodaj metodę 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);
   }
}

Kod rozszerza układ i umieszcza pasek działań w górnej części ekranu.

Metoda onServiceConnected() jest uruchamiana po nawiązaniu połączenia z usługą. Obecnie usługa ułatwień dostępu ma wszystkie uprawnienia potrzebne do działania. Uprawnienie klucza, którego będziesz tu używać, to uprawnienie WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY. To uprawnienie umożliwia rysowanie bezpośrednio na ekranie nad istniejącymi treściami bez konieczności przechodzenia skomplikowanej procedury przyznawania uprawnień.

Cykl życia usługi ułatwień dostępu

Cykl życia usługi ułatwień dostępu jest zarządzany wyłącznie przez system i odbywa się zgodnie z ustalonym cyklem życia usługi.

  • Usługa ułatwień dostępu uruchamia się, gdy użytkownik włączy ją w ustawieniach urządzenia.
  • Po powiązaniu systemu z usługą wywołuje metodę onServiceConnected(). Tę metodę mogą zastąpić usługi, które chcą skonfigurować po powiązaniu usługi.
  • Usługa ułatwień dostępu zatrzymuje się, gdy użytkownik ją wyłączy w ustawieniach urządzenia lub po wywołaniu funkcji disableSelf().

Uruchamianie usługi

Zanim będzie można uruchomić tę usługę za pomocą Android Studio, musisz upewnić się, że ustawienia uruchamiania są prawidłowo skonfigurowane.

Zmień konfigurację uruchamiania (w menu u góry kliknij Uruchom i kliknij Edytuj konfiguracje). Następnie w menu zmień opcję uruchamiania z „Domyślna aktywność”. wybierz „Nic”.

Menu rozwijane, aby skonfigurować ustawienia uruchamiania umożliwiające uruchamianie usługi przy użyciu Android Studio.

Uruchomienie usługi powinno być teraz możliwe przy użyciu Android Studio.

Na pasku menu w górnej części ekranu naciśnij zieloną ikonę Odtwórz Przycisk Play w Android Studio użyty do uruchomienia usługi. Następnie otwórz Ustawienia > Ułatwienia dostępu i włącz Global Action Bar Service.

Cztery przyciski tworzące interfejs usługi powinny być nałożone na zawartość wyświetlaną na ekranie.

overlay.png

Teraz do 4 przycisków dodamy nowe funkcje, dzięki którym użytkownik będzie mógł je dotknąć, aby wykonać przydatne czynności.

5. Konfigurowanie przycisku zasilania

Dodaj metodę configurePowerButton() do pliku 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);
       }
   });
}

Aby uzyskać dostęp do menu przycisku zasilania, funkcja configurePowerButton() używa metody performGlobalAction() udostępnianej przez AccessibilityService. Dodany przed chwilą kod jest prosty: kliknięcie przycisku aktywuje funkcję onClickListener(). Wywołuje to działanie funkcji performGlobalAction(GLOBAL_ACTION_POWER_DIALOG) i wyświetla użytkownikowi okno zasilania.

Pamiętaj, że działania globalne nie są powiązane z żadnymi wyświetleniami. Inne przykłady działań globalnych to kliknięcia przycisków Wstecz, Ekran główny i Ostatnie.

Teraz dodaj configurePowerButton() na końcu metody onServiceConnected():

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

Na pasku menu w górnej części ekranu naciśnij zieloną ikonę Odtwórz Przycisk Play w Android Studio użyty do uruchomienia usługi. Następnie otwórz Ustawienia > Ułatwienia dostępu i uruchom globalną usługę paska działań.

Naciśnij przycisk zasilania, by wyświetlić okno zasilania.

6. Konfigurowanie przycisku głośności

Dodaj metodę configureVolumeButton() do pliku 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);
       }
   });
}

Metoda configureVolumeButton() dodaje funkcję onClickListener(), która jest aktywowana, gdy użytkownik naciśnie przycisk głośności. W tym detektorze funkcja configureVolumeButton() używa interfejsu AudioManager, aby dostosować głośność strumienia.

Pamiętaj, że każdy może kontrolować głośność (nie musisz być użytkownikiem usług ułatwień dostępu, aby to zrobić).

Teraz dodaj configureVolumeButton() na końcu metody onServiceConnected():

@Override
protected void onServiceConnected() {
   ...

   configureVolumeButton();
}

Na pasku menu w górnej części ekranu naciśnij zieloną ikonę Odtwórz Przycisk Play w Android Studio użyty do uruchomienia usługi. Następnie otwórz Ustawienia > Ułatwienia dostępu i uruchom globalną usługę paska działań.

Naciśnij przycisk głośności, by zmienić głośność.

Hipotetyczny użytkownik, który nie może sięgnąć po elementy sterujące głośnością z boku urządzenia, może teraz zmienić (zwiększyć) głośność za pomocą globalnej usługi paska działań.

7. Konfigurowanie przycisku przewijania

W tej części omawiamy 2 metody kodowania. Pierwsza metoda znajduje węzeł z możliwością przewijania, a druga wykonuje działanie przewijania w imieniu użytkownika.

Dodaj metodę findScrollableNode do pliku 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;
}

Usługa ułatwień dostępu nie ma dostępu do rzeczywistych widoków na ekranie. Zamiast tego widzi odbicie tego, co widać na ekranie, w postaci drzewa złożonego z obiektów AccessibilityNodeInfo. Obiekty te zawierają informacje o reprezentowanym przez nie widoku (lokalizacja widoku, wszelki powiązany z nim tekst, dodane metadane na potrzeby ułatwień dostępu, działania obsługiwane przez widok itp.). Metoda findScrollableNode() wykonuje przemierzanie od początku tego drzewa, zaczynając od węzła głównego. Jeśli znajdzie węzeł, który można przewijać (tj. węzeł obsługujący działanie AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD)), zwraca go. W przeciwnym razie zwraca wartość null.

Teraz dodaj metodę configureScrollButton() do pliku 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());
           }
       }
   });
}

Ta metoda tworzy funkcję onClickListener(), która uruchamia się po kliknięciu przycisku przewijania. Próbuje znaleźć węzeł z możliwością przewijania i w przypadku powodzenia wykonuje działanie przewijania.

Teraz dodaj parametr configureScrollButton() do usługi configureScrollButton():

@Override
protected void onServiceConnected() {
   ...

   configureScrollButton();
}

Na pasku menu w górnej części ekranu naciśnij zieloną ikonę Odtwórz Przycisk Play w Android Studio użyty do uruchomienia usługi. Następnie otwórz Ustawienia > Ułatwienia dostępu i uruchom globalną usługę paska działań.

Naciśnij przycisk Wstecz, aby otworzyć Ustawienia > Ułatwienia dostępu. Elementy aktywności ustawień ułatwień dostępu można przewijać, a kliknięcie przycisku przewijania powoduje przewijanie. Nasz hipotetyczny użytkownik, który nie potrafi łatwo przewijać, może teraz przewijać listę elementów, używając przycisku przewijania.

8. Konfigurowanie przycisku przesuwania

Dodaj metodę configureSwipeButton() do obiektu 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);
       }
   });
}

Metoda configureSwipeButton() korzysta z nowego interfejsu API, który został dodany do języka N, który wykonuje gesty w imieniu użytkownika. Kod używa obiektu GestureDescription do określenia ścieżki do wykonania gestu (w tym ćwiczeniu z programowania używane są wartości zakodowane na stałe), a następnie wysyła gest przesuwania w imieniu użytkownika za pomocą metody dispatchGesture() AccessibilityService.

Teraz dodaj parametr configureSwipeButton() do onServiceConnected():

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

Na pasku menu w górnej części ekranu naciśnij zieloną ikonę Odtwórz Przycisk Play w Android Studio użyty do uruchomienia usługi. Następnie otwórz Ustawienia > Ułatwienia dostępu i uruchom globalną usługę paska działań.

Najprostszym sposobem na przetestowanie funkcji przesuwania jest otwarcie aplikacji Mapy zainstalowanej na telefonie. Po załadowaniu mapy dotknięcie przycisku Przesuń palcem w prawo powoduje przesunięcie ekranu w prawo.

9. Podsumowanie

Gratulacje! Masz prostą, funkcjonalną usługę ułatwień dostępu.

Usługę tę możesz rozszerzyć na różne sposoby. Na przykład:

  1. Ustaw pasek działań w sposób ruchomy (obecnie znajduje się tylko na górze ekranu).
  2. Pozwól użytkownikowi jednocześnie zwiększać i zmniejszać głośność.
  3. Zezwalaj użytkownikowi na przesuwanie palcem w lewo i w prawo.
  4. Dodaliśmy obsługę dodatkowych gestów, na które może reagować pasek działań.

To ćwiczenie w Codelabs obejmuje tylko niewielki podzbiór funkcji udostępnianych przez interfejsy API ułatwień dostępu. Interfejs API obejmuje również następujące (częściową listę):

  • Obsługa wielu okien.
  • Obsługa zdarzeń AccessibilityEvent. Gdy interfejs się zmienia, usługi ułatwień dostępu są powiadamiane o tych zmianach za pomocą obiektów AccessibilityEvent. Usługa może wtedy reagować odpowiednio na zmiany w interfejsie użytkownika.
  • Możliwość kontrolowania powiększenia.

Dzięki tym ćwiczeniom w Codelabs dowiesz się, jak pisać usługę ułatwień dostępu. Jeśli znasz użytkownika mającego konkretne problemy z ułatwieniami dostępu, którym chcesz pomóc, możesz utworzyć dla niego usługę.