Android 用のユーザー補助サービスを開発する

1. はじめに

ユーザー補助サービスは、Android デバイスにインストールされたアプリに代わって、ユーザーに代替ナビゲーション フィードバックを提供するように設計された Android フレームワークの機能です。ユーザー補助サービスは、アプリに代わってユーザーに情報を伝達できます。たとえば、ユーザーが画面の重要な領域にカーソルを合わせたときに、テキストを音声に変換したり、触覚フィードバックを提供したりできます。この Codelab では、非常にシンプルなユーザー補助サービスを作成する方法について説明します。

ユーザー補助サービスとは

ユーザー補助サービスは、障がいのあるユーザーが Android デバイスやアプリを使用するのを支援します。画面上の情報を処理し、デバイスを意味のある方法で操作できるようにする、特権を持つ長時間実行サービスです。

一般的なユーザー補助サービスの例

  • スイッチ アクセス: 肢体不自由のある Android ユーザーが 1 つ以上のスイッチを使ってデバイスを操作できるようにします。
  • Voice Access(ベータ版): 身体の不自由な Android ユーザーが音声コマンドでデバイスを操作できるようにします。
  • Talkback: 視覚障がいのあるユーザーや目の不自由なユーザーがよく使用するスクリーン リーダー。

ユーザー補助サービスの構築

Google は Android ユーザー向けにスイッチ アクセス、音声アクセス、TalkBack などのサービスを提供していますが、これらのサービスはすべての障がいのあるユーザーに対応できるわけではありません。障がいのあるユーザーのニーズは多種多様であるため、Android のユーザー補助サービス作成用 API はオープンです。デベロッパーは自由にユーザー補助サービスを作成し、Google Play ストアを通じて配布できます。

作成するアプリの概要

この Codelab では、ユーザー補助 API を使用していくつかの便利な処理を行うシンプルなサービスを開発します。基本的な Android アプリを作成できるのであれば、同様のサービスを開発できます。

ユーザー補助 API は強力です。作成するサービスのコードは 4 つのファイルにのみ含まれており、約 200 行のコードを使用します。

エンドユーザー

次の特性を持つ架空のユーザー向けのサービスを構築します。

  • デバイスのサイドボタンに手が届きにくい。
  • スクロールやスワイプが難しい。

サービスの詳細

サービスは、画面にグローバル アクション バーをオーバーレイします。このバーのボタンをタップすると、次の操作を行えます。

  1. スマートフォンの側面にある電源ボタンに触れずにデバイスの電源をオフにします。
  2. スマートフォンの側面にある音量ボタンに触れずに音量を調整できます。
  3. 実際にスクロールせずにスクロール アクションを実行します。
  4. スワイプ ジェスチャーを使用せずにスワイプを実行します。

必要なもの

この Codelab では、以下を使用することを前提としています。

  1. Android Studio を実行しているパソコン。
  2. 簡単なシェルコマンドを実行するためのターミナル。
  3. Android 7.0(Nougat)を搭載したデバイス。開発に使用するパソコンに接続されているもの。

では始めましょう。

2. 設定方法

ターミナルを使用して、作業するディレクトリを作成します。このディレクトリに移動します。

コードをダウンロードする

この Codelab のコードを含むリポジトリのクローンを作成できます。

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

このリポジトリには、複数の Android Studio プロジェクトが含まれています。Android Studio を使用して、GlobalActionBarService を開きます。

Studio アイコンをクリックして Android Studio を起動します。

Android Studio の起動に使用されるロゴ。

[Import project (Eclipse ADT, Gradle, etc.)] オプションを選択します。

Android Studio のウェルカム画面。

ソースのクローンを作成した場所に移動し、GlobalActionBarService を選択します。

次に、ターミナルを使用してルート ディレクトリに移動します。

3. 開始コードについて

開いたプロジェクトを確認します。

ユーザー補助サービスの基本的なスケルトンはすでに作成されています。この Codelab で記述するコードはすべて、次の 4 つのファイルに限定されます。

  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

各ファイルの内容について説明します。

AndroidManifest.xml

ユーザー補助サービスに関する情報は、マニフェストで宣言されます。

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

次の 3 つの必須項目は AndroidManifest.xml で宣言されています。

  1. ユーザー補助サービスにバインドする権限:
<service
    ...
    android:permission = "android.permission.BIND_ACCESSIBILITY_SERVICE">
    ...             
</service>
  1. AccessibilityService インテント:
<intent-filter>
   <action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
  1. 作成するサービスのメタデータを含むファイルの場所:
<meta-data
       ...
       android:resource="@xml/global_action_bar_service" />
</service>

global_action_bar_service.xml

このファイルには、サービスのメタデータが含まれています。

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

<accessibility-service> 要素を使用して、次のメタデータが定義されています。

  1. このサービスのフィードバック タイプ(この Codelab では、デフォルトとして適切な feedbackGeneric を使用します)。
  2. サービスのユーザー補助フラグ(この Codelab ではデフォルトのフラグを使用します)。
  3. サービスに必要な機能:
  4. スワイプを行うために、android:canPerformGesturestrue に設定されています。
  5. ウィンドウ コンテンツを取得するために、android:canRetrieveWindowContenttrue に設定されています。

GlobalActionBarService.java

ユーザー補助サービスのコードのほとんどは GlobalActionBarService.java にあります。当初、このファイルにはユーザー補助機能サービスの最低限のコードが含まれています。

  1. AccessibilityService を拡張するクラス。
  2. オーバーライドが必要なメソッド(この Codelab では空のまま)。
public class GlobalActionBarService extends AccessibilityService {

   @Override
   public void onAccessibilityEvent(AccessibilityEvent event) {

   }

   @Override
   public void onInterrupt() {

   }
}

この Codelab では、このファイルにコードを追加します。

action_bar.xml

このサービスは 4 つのボタンを含む UI を公開します。action_bar.xml レイアウト ファイルには、これらのボタンを表示するためのマークアップが含まれています。

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

このファイルには、現時点では空の LinearLayout が含まれています。この Codelab では、ボタンのマークアップを追加します。

アプリケーションを起動する

デバイスをパソコンに接続しておきます。画面上部のメニューバーから緑色の [Play] アイコン サービスを起動するために使用される Android Studio の再生ボタン をクリックします。これにより、作業中のアプリが起動します。

[設定] > [ユーザー補助機能] に移動します。デバイスに Global Action Bar Service がインストールされている。

ユーザー補助設定画面

[Global Action Bar Service] をクリックしてオンにします。次の権限ダイアログが表示されます。

ユーザー補助サービス権限のダイアログ。

ユーザー補助サービスは、ユーザーの操作の監視、ウィンドウ コンテンツの取得、ユーザーに代わってジェスチャーを実行する権限をリクエストします。サードパーティのユーザー補助サービスを使用する場合は、ソースを十分に信頼できることを確認してください

まだ機能を追加していないため、サービスを実行しても何も起こりません。それでは、始めましょう。

4. ボタンの作成

res/layoutaction_bar.xml を開きます。現在空の 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>

これにより、ユーザーがデバイスでアクションをトリガーするために押すボタンが作成されます。

GlobalActionBarService.java を開き、アクション バーのレイアウトを保存する変数を追加します。

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

次に、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);
   }
}

このコードはレイアウトを拡張し、画面の上部付近にアクションバーを追加します。

サービスが接続されると、onServiceConnected() メソッドが実行されます。この時点で、ユーザー補助サービスは機能するために必要なすべての権限を持っています。ここで使用するキー権限は、WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY 権限です。この権限を使用すると、複雑な権限フローを経由することなく、既存のコンテンツの上に直接描画できます。

ユーザー補助サービスのライフサイクル

ユーザー補助サービスのライフサイクルはシステムによってのみ管理され、確立されたサービス ライフサイクルに従います。

  • ユーザーがデバイス設定でサービスを明示的にオンにすると、ユーザー補助サービスが開始されます。
  • システムがサービスにバインドされると、onServiceConnected() が呼び出されます。このメソッドは、バインド後の設定を行いたいサービスによってオーバーライドできます。
  • ユーザーがデバイスの設定で無効にするか、disableSelf() を呼び出すと、ユーザー補助サービスは停止します。

サービスを実行する

Android Studio を使用してサービスを起動する前に、実行設定が正しく構成されていることを確認する必要があります。

実行構成を編集します(上部のメニューから [実行] を選択し、[構成を編集] に移動します)。次に、プルダウンを使用して、[Launch Option] を [Default Activity] から [Nothing] に変更します。

プルダウンして実行設定を構成し、Android Studio を使用してサービスを起動します。

これで、Android Studio を使用してサービスを起動できるようになりました。

画面上部のメニューバーから緑色の [Play] アイコン サービスを起動するために使用される Android Studio の再生ボタン をクリックします。次に、[設定] > [ユーザー補助] に移動し、[Global Action Bar Service] をオンにします。

画面に表示されたコンテンツの上に、サービス UI を構成する 4 つのボタンがオーバーレイ表示されます。

overlay.png

次に、ユーザーがボタンをタップして便利なアクションを実行できるように、4 つのボタンに機能を追加します。

5. 電源ボタンの設定

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

電源ボタン メニューへのアクセスの場合、configurePowerButton()AccessibilityService によって提供される performGlobalAction() メソッドを使用します。追加したコードはシンプルです。ボタンをクリックすると onClickListener() がトリガーされます。これにより performGlobalAction(GLOBAL_ACTION_POWER_DIALOG) が呼び出され、電源ダイアログがユーザーに表示されます。

グローバル アクションはビューに関連付けられていません。戻るボタン、ホームボタン、最近使ったアプリボタンを押すことも、グローバル アクションの例です。

次に、onServiceConnected() メソッドの末尾に configurePowerButton() を追加します。

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

画面上部のメニューバーから緑色の [Play] アイコン サービスを起動するために使用される Android Studio の再生ボタン をクリックします。次に、[設定] > [ユーザー補助] に移動し、Global Action Bar Service を開始します。

電源ボタンを押して電源ダイアログを表示します。

6. 音量ボタンの設定

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

configureVolumeButton() メソッドは、ユーザーが音量ボタンを押したときにトリガーされる onClickListener() を追加します。このリスナー内で、configureVolumeButton()AudioManager を使用してストリームの音量を調整します。

音量は誰でも調整できます(ユーザー補助サービスである必要はありません)。

次に、onServiceConnected() メソッドの末尾に configureVolumeButton() を追加します。

@Override
protected void onServiceConnected() {
   ...

   configureVolumeButton();
}

画面上部のメニューバーから緑色の [Play] アイコン サービスを起動するために使用される Android Studio の再生ボタン をクリックします。次に、[設定] > [ユーザー補助] に移動し、グローバル アクション バー サービスを開始します。

音量ボタンを押して音量を変更します。

デバイスの側面にある音量調節に手が届かないユーザーは、グローバル アクション バー サービスを使用して音量を変更(上げる)できるようになりました。

7. スクロール ボタンの設定

このセクションでは、2 つのメソッドをコーディングします。最初のメソッドはスクロール可能なノードを見つけ、2 番目のメソッドはユーザーの代わりにスクロール アクションを実行します。

findScrollableNodefindScrollableNode メソッドを追加します。

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

ユーザー補助サービスは、画面上の実際のビューにアクセスできません。代わりに、画面上の内容が AccessibilityNodeInfo オブジェクトで構成されたツリーの形で反映されます。これらのオブジェクトには、ビューに関する情報(ビューの位置、ビューに関連付けられたテキスト、ユーザー補助機能のために追加されたメタデータ、ビューでサポートされているアクションなど)が含まれます。findScrollableNode() メソッドは、ルートノードから始まるこのツリーの幅優先探索を実行します。スクロール可能なノード(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD アクション)をサポートするノード)が見つかった場合はそれを返し、それ以外の場合は null を返します。

次に、configureScrollButton()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());
           }
       }
   });
}

このメソッドは、スクロール ボタンがクリックされたときに起動する onClickListener() を作成します。スクロール可能なノードを見つけようとし、成功した場合はスクロール アクションを実行します。

次に、onServiceConnected()configureScrollButton() を追加します。

@Override
protected void onServiceConnected() {
   ...

   configureScrollButton();
}

画面上部のメニューバーから緑色の [Play] アイコン サービスを起動するために使用される Android Studio の再生ボタン をクリックします。次に、[設定] > [ユーザー補助] に移動し、グローバル アクション バー サービスを開始します。

[戻る] ボタンを押して、[設定] > [ユーザー補助機能] に移動します。ユーザー補助設定アクティビティの項目はスクロール可能で、スクロール ボタンをタップするとスクロール アクションが実行されます。スクロール操作を簡単に行うことができない架空のユーザーは、スクロール ボタンを使用してアイテムのリストをスクロールできるようになりました。

8. スワイプボタンの設定

configureSwipeButton()configureSwipeButton() メソッドを追加します。

private void configureSwipeButton() {
   Button swipeButton = (Button) mLayout.findViewById(R.id.swipe);
   swipeButton.setOnClickListener(new View.OnClickListener() {
       @Override
       public void onClick(View view) {
           Path swipePath = new Path();
           swipePath.moveTo(1000, 1000);
           swipePath.lineTo(100, 1000);
           GestureDescription.Builder gestureBuilder = new GestureDescription.Builder();
           gestureBuilder.addStroke(new GestureDescription.StrokeDescription(swipePath, 0, 500));
           dispatchGesture(gestureBuilder.build(), null, null);
       }
   });
}

configureSwipeButton() メソッドは、ユーザーの代わりにジェスチャーを実行する N で追加された新しい API を使用します。このコードでは、GestureDescription オブジェクトを使用して、実行するジェスチャーのパスを指定し(この Codelab ではハードコードされた値を使用)、AccessibilityServicedispatchGesture() メソッドを使用して、ユーザーに代わってスワイプ ジェスチャーをディスパッチします。

次に、onServiceConnected()configureSwipeButton() を追加します。

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

画面上部のメニューバーから緑色の [Play] アイコン サービスを起動するために使用される Android Studio の再生ボタン をクリックします。次に、[設定] > [ユーザー補助] に移動し、グローバル アクション バー サービスを開始します。

スワイプ機能をテストする最も簡単な方法は、スマートフォンにインストールされている マップ アプリを開くことです。地図が読み込まれたら、[スワイプ] ボタンをタップすると画面が右にスワイプされます。

9. まとめ

おめでとうございます!これで、シンプルで機能的なユーザー補助サービスが完成しました。

このサービスはさまざまな方法で拡張できます。次に例を示します。

  1. アクションバーを移動可能にする(現在は画面上部に固定されている)。
  2. ユーザーが音量を上げたり下げたりできるようにします。
  3. ユーザーが左右にスワイプできるようにします。
  4. アクションバーが応答できる追加のジェスチャーのサポートを追加します。

この Codelab では、ユーザー補助 API が提供する機能のほんの一部のみを扱います。この API は、次のものも対象としています(一部を抜粋)。

  • 複数のウィンドウのサポート。
  • AccessibilityEvent のサポート。UI が変更されると、ユーザー補助サービスには AccessibilityEvent オブジェクトを使用して変更が通知されます。サービスは、UI の変更に適切に応答できます。
  • 拡大率を制御できます。

この Codelab では、ユーザー補助サービスの作成について説明します。特定のユーザー補助機能に関する問題をお持ちのユーザーを把握している場合は、そのユーザーを支援するサービスを構築できるようになりました。