1. はじめに
ユーザー補助サービスは Android フレームワークの機能です。Android デバイスにインストールされているアプリに代わって、代替ナビゲーションのフィードバックをユーザーに提示します。ユーザー補助サービスは、アプリに代わってユーザーとやり取りできます。たとえば、テキストを音声に変換したり、ユーザーが画面の重要な領域にカーソルを合わせたときに触覚フィードバックを提供したりできます。この Codelab では、非常にシンプルなユーザー補助サービスを作成する方法について説明します。
ユーザー補助サービスとは
ユーザー補助サービスは、障がいのあるユーザーが Android デバイスやアプリを使用できるよう支援するサービスです。長期にわたって稼働する特権サービスで、画面上の情報を処理し、ユーザーがデバイスを有意義に操作できるようにします。
一般的なユーザー補助サービスの例
- スイッチ アクセス: 運動障がいのある Android ユーザーが、1 つ以上のスイッチを使用してデバイスを操作できます。
- Voice Access(ベータ版): 運動障がいのある Android ユーザーが音声コマンドでデバイスを操作できるようにします。
- TalkBack: 視覚障がいのあるユーザーが一般的に使用するスクリーン リーダーです。
ユーザー補助サービスの構築
Google は、Android ユーザー向けにスイッチ アクセス、Voice Access、Talkback などのサービスを提供していますが、障がいのあるすべてのユーザーにサービスを提供しているわけではありません。障がいのあるユーザーの多くは独自のニーズを持っているため、ユーザー補助サービスを作成するための Android の API は公開されています。デベロッパーはユーザー補助サービスを自由に作成して、Google Play ストアで配布できます。
作業内容
この Codelab では、Accessibility API を使用して便利な処理を行うシンプルなサービスを作成します。基本的な Android アプリを作成できる場合は、同様のサービスを開発できます。
Accessibility API は便利です。これから構築するサービスのコードは、たった 4 つのファイルに含まれており、使用するコードは約 200 行です。
エンドユーザー
次の特性を持つ架空のユーザーのために、サービスを構築します。
- デバイスのサイドボタンにうまくアクセスできない。
- スクロールやスワイプが困難である。
サービスの詳細
サービスが画面上にグローバル アクションバーをオーバーレイします。ユーザーはこのバーのボタンをタップして、次の操作を行うことができます。
- スマートフォンの側面にある電源ボタンに手を伸ばさずにデバイスの電源を切ります。
- スマートフォンの側面にある音量ボタンを使わずに音量を調整します。
- 実際にスクロールせずにスクロール操作を実行する。
- スワイプ操作をしなくてもスワイプを実行する。
必要なもの
この Codelab では、以下を使用することを前提としています。
- Android Studio を実行しているパソコン。
- 単純なシェルコマンドを実行するためのターミナル。
- Android 7.0(Nougat)を搭載したデバイスを、開発に使用するパソコンに接続している。
では始めましょう。
2. 設定方法
ターミナルを使用して、作業を行うディレクトリを作成します。このディレクトリに移動します。
コードをダウンロードする
この Codelab のコードを含むリポジトリのクローンを作成できます。
git clone https://github.com/android/codelab-android-accessibility.git
このリポジトリには、複数の Android Studio プロジェクトが含まれています。Android Studio を使用して GlobalActionBarService を開きます。
Studio アイコンをクリックして Android Studio を起動します。
[Import project (Eclipse ADT, Gradle など)] オプションを選択します。
ソースのクローンを作成した場所に移動し、[GlobalActionBarService.] を選択します。
次に、ターミナルを使用してルート ディレクトリに移動します。
3. 開始時のコードについて
開いたプロジェクトを確認します。
ユーザー補助サービスの必要最小限のスケルトンはすでに作成されています。この Codelab で記述するコードは、次の 4 つのファイルに限定されます。
- app/src/main/AndroidManifest.xml
- app/src/main/res/layout/action_bar.xml
- app/src/main/res/xml/global_action_bar_service.xml
- 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>
AndroidManifest.xml では、次の 3 つの必須アイテムが宣言されています。
- ユーザー補助サービスにバインドする権限:
<service
...
android:permission = "android.permission.BIND_ACCESSIBILITY_SERVICE">
...
</service>
- AccessibilityService インテントには次の特徴があります。
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
- 作成するサービスのメタデータを含むファイルの場所:
<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> 要素を使用して、次のメタデータが定義されています。
- このサービスのフィードバック タイプ(この Codelab では feedbackGeneric を使用しますが、これは適切なデフォルトです)。
- サービスのユーザー補助フラグ(この Codelab ではデフォルトのフラグを使用しています)。
- このサービスに必要な機能は次のとおりです。
- スワイプを行うには、android:canPerformGestures を true に設定します。
- ウィンドウのコンテンツを取得するには、android:canRetrieveWindowContent を true に設定します。
GlobalActionBarService.java
ユーザー補助サービスのコードのほとんどは、GlobalActionBarService.java 内にあります。このファイルには、最初にユーザー補助サービスに必要な最低限のコードが含まれています。
- AccessibilityService を拡張するクラス。
- オーバーライドされる必須のメソッドがいくつかあります(この 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] アイコン をクリックします。これにより、作業中のアプリが起動します。
[設定] に移動します。ユーザー補助。グローバル アクションバー サービスがデバイスにインストールされている。
[Global Action Bar Service] をクリックしてオンにします。次の権限ダイアログが表示されます。
ユーザー補助サービスは、ユーザーの代わりにユーザー操作の監視、ウィンドウ コンテンツの取得、ジェスチャーの実行を行う権限をリクエストします。サードパーティのユーザー補助サービスを利用する場合は、信頼できる提供元であることを確認してください。
まだ機能が追加されていないため、サービスを実行してもあまり役に立ちません。それでは始めましょう。
4. ボタンの作成
res/layout で action_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 を使用してサービスを起動する前に、実行の設定が正しく構成されていることを確認する必要があります。
実行構成を編集します(トップメニューから [Run] を使用し、[Edit Configurations] に移動します)。次に、プルダウンを使用して、起動オプションを [デフォルトのアクティビティ] から変更します。「Nothing」に変更します。
Android Studio を使用してサービスを起動できるようになりました。
画面上部のメニューバーから緑色の [Play] アイコン をクリックします。次に、[設定 >[ユーザー補助] を選択し、[グローバル アクション バー サービス] をオンにします。
画面に表示されるコンテンツの上に、サービス UI を形成する 4 つのボタンが重なって表示されます。
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] アイコン をクリックします。次に、[設定 >ユーザー補助を選択し、グローバル アクション バー サービスを開始します。
電源ボタンを押して、電源ダイアログを表示します。
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] アイコン をクリックします。次に、[設定] >ユーザー補助] に移動し、グローバル アクションバー サービスを開始します。
音量ボタンを押して音量を変更します。
仮にデバイス側面の音量コントロールにアクセスできないユーザーは、グローバル アクションバー サービスを使用して音量を変更(増加)できます。
7. スクロールボタンを構成する
このセクションでは、2 つのメソッドをコーディングします。1 つ目のメソッドはスクロール可能なノードを検出し、2 つ目のメソッドはユーザーに代わってスクロール アクションを実行します。
findScrollableNode メソッドを 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;
}
ユーザー補助サービスは、画面上の実際のビューにアクセスできません。その代わりに、画面上に表示されている内容を、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() を作成します。スクロール可能なノードの検索を試み、成功するとスクロール アクションを実行します。
次に、configureScrollButton()を configureScrollButton() に追加します。
@Override
protected void onServiceConnected() {
...
configureScrollButton();
}
画面上部のメニューバーから緑色の [Play] アイコン をクリックします。次に、[設定] >ユーザー補助] に移動し、グローバル アクションバー サービスを開始します。
戻るボタンを押して、[設定 >ユーザー補助。ユーザー補助設定アクティビティの項目はスクロール可能です。スクロール ボタンをタップすると、スクロール操作が行われます。スクロール操作を簡単に実行できない架空のユーザーは、スクロールボタンを使用して項目のリストをスクロールできるようになりました。
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 ではハードコードした値を使用します)。その後、AccessibilityService の dispatchGesture() メソッドを使用して、ユーザーに代わってスワイプ ジェスチャーをディスパッチします。
次に、configureSwipeButton() を onServiceConnected() に追加します。
@Override
protected void onServiceConnected() {
...
configureSwipeButton();
}
画面上部のメニューバーから緑色の [Play] アイコン をクリックします。次に、[設定] >ユーザー補助] に移動し、グローバル アクションバー サービスを開始します。
スワイプ機能をテストするには、スマートフォンにインストールされているマップ アプリを開くのが最も簡単な方法です。地図が読み込まれたら、[スワイプ] ボタンをタップすると、画面が右にスワイプします。
9. まとめ
これで、シンプルで機能的なユーザー補助サービスが作成されました。
このサービスはさまざまな方法で拡張できます。例:
- アクションバーを移動可能にする(現時点では画面上部に表示されています)。
- 音量の調節と音量調整の両方を行えるようにします。
- ユーザーが左右にスワイプできるようにします。
- アクションバーが応答できる追加の操作のサポートを追加します。
この Codelab では、Accessibility API によって提供される機能のほんの一部だけを取り上げています。この API は以下にも対応しています(一部)。
- 複数のウィンドウのサポート。
- AccessibilityEvent のサポート。UI が変更されると、ユーザー補助サービスは AccessibilityEvent オブジェクトを使用してその変更を通知します。その後、サービスは UI の変更に適宜対応できます。
- 拡大を制御できます。
この Codelab では、ユーザー補助サービスの作成を開始します。特定のユーザー補助の問題に対処したいユーザーを把握している場合は、そのユーザーをサポートするサービスを構築できます。