独自のアクセシビリティ サービスを作成する

ユーザー補助サービスは、ユーザー インターフェースを強化して、 障がいのあるユーザー、または一時的に完全に操作できなくなる可能性があるユーザー 必要があります。たとえば 車を運転したり 子どもの世話をしているユーザーや 大音量のパーティーに参加すると、追加のインターフェースや代替インターフェースが できます。

Android は、次のような標準のユーザー補助サービスを提供しています。 TalkBack があり、デベロッパーは独自のサービスを作成して配布できます。このドキュメント では、ユーザー補助サービスの構築の基本について説明します。

ユーザー補助サービスは、通常のアプリにバンドルしたり、 スタンドアロンの Android プロジェクト。サービスの作成手順は、 できます。

ユーザー補助サービスを作成する

プロジェクト内で、Terraform Registry を拡張する 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() {
    }

...
}

この Service の新しいプロジェクトを作成し、アプリをリリースする予定がない場合 スターター Activity クラスをモジュールから削除できます。 あります。

マニフェストの宣言と権限

ユーザー補助サービスを提供するアプリは、 アプリ マニフェストを Android でユーザー補助サービスとして扱うよう設定している ありませんこのセクションでは、Google Compute Engine の必須の設定とオプションの設定について説明します。 ユーザー補助サービスも提供しています

ユーザー補助サービスの宣言

アプリをユーザー補助サービスとして扱うには、service を追加してください 要素(activity 要素ではない)を application 内で指定する 要素を宣言する必要があります。さらに、service 要素内に ユーザー補助サービスのインテント フィルタ。マニフェストでは、Service で使用する Service を 「 BIND_ACCESSIBILITY_SERVICE システムだけがバインドできるようにする必要があります。次の例をご覧ください。

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

ユーザー補助サービスの設定

ユーザー補助サービスでは、ユーザーが認識できるコンテンツの種類を サービスが処理するユーザー補助イベントと、 提供します。ユーザー補助サービスの構成は、 AccessibilityServiceInfo クラスです。このサービスでは、このオブジェクトのインスタンスを使用して、構成のビルドと設定を行うことができます。 クラスと setServiceInfo() 実行時に決定できますただし、この方法ではすべての構成オプションを メソッドを呼び出します。

マニフェストに <meta-data> 要素を追加して、 構成ファイルを使用して、サービス アカウントにすべてのオプションを 渡す必要があります。

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

この <meta-data> 要素は、 アプリケーションのリソース ディレクトリ: <project_dir>/res/xml/accessibility_service_config.xml>。次のコードでは、 に、サービス構成ファイルの内容の例を示します。

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

モジュールで使用できる XML 属性について詳しくは、 詳しくは、次のリファレンスをご覧ください。 ドキュメントをご覧ください。

動的に設定可能な構成設定について詳しくは、 詳細については、 AccessibilityServiceInfo ご覧ください。

ユーザー補助サービスを設定する

モジュールの構成変数を設定する場合は、 ユーザー補助サービスを使用して、実行する方法とタイミングをシステムに指示します。

  • どのタイプのイベントに応答しますか?
  • サービスはすべてのアプリに対してアクティブにする必要がありますか、それとも特定のパッケージに対してのみアクティブにする必要がありますか? ありますか?
  • どのようなフィードバック タイプを使用するかを伝えます。

これらの変数を設定するには、2 つの方法があります。下位互換性のあるオプションは コード内に設定する方法です。 setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo) そのためには、 onServiceConnected() メソッドを実行し、そこで次の例に示すようにサービスを構成します。

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

}

2 つ目のオプションは、XML ファイルを使用してサービスを構成する方法です。確実 構成オプション(例: canRetrieveWindowContent XML を使用してサービスを設定する場合にのみ使用できます。構成 上記の例のオプションは、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"
/>

XML を使用する場合は、マニフェストで XML 参照に <meta-data> タグを XML ファイルを指すサービス宣言を指定します。XML ファイルを res/xml/serviceconfig.xml の場合、新しいタグは次のようになります。

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

ユーザー補助サービスのメソッド

ユーザー補助サービスは、AccessibilityService クラスを拡張し、 そのクラスの次のメソッドをオーバーライドします。これらの方法は、 Android システムが呼び出す順序(サービスの開始時) (onServiceConnected())、実行中 (onAccessibilityEvent()onInterrupt())、 シャットダウンするまで (onUnbind())。

  • onServiceConnected(): (省略可)システムがこのメソッドを呼び出すと、 ユーザー補助サービスに接続しますこの方法で 1 回限りの設定を行います サービスに必要な手順(ユーザー フィードバック システムへの接続など) サービス(オーディオ マネージャーやデバイス バイブレーターなど)が含まれます。ルールの サービスの構成を実行時に変更したり 1 回限りの調整を行ったりする setServiceInfo() を呼び出すのに便利な場所です。

  • onAccessibilityEvent(): (必須)次の場合にシステムがこのメソッドをコールバックします。 マルウェアや AccessibilityEvent 指定したイベント フィルタ パラメータに一致する (ユーザーがボタンをタップしたときやユーザー インターフェースにフォーカスしたときなど) フィードバックを提供するアプリ内の コントロール。日時 このメソッドが呼び出されると、関連する AccessibilityEvent が渡され、 その情報を解釈、使用してユーザーにフィードバックを提供する できます。このメソッドは、システムのライフサイクル全体で何度も あります。

  • onInterrupt(): (必須)システムが実行時にこのメソッドを呼び出します。 フィードバックへの中断を伴います。 別のコントロールにフォーカスを移動するなど、ユーザー操作への応答として処理できます。この メソッドはサービスのライフサイクル全体で何度も呼び出すことができます。

  • onUnbind(): (省略可)システムが稼働しているときに、システムがこのメソッドを呼び出します。 シャットダウンする場合ですこの方法は、 ユーザー フィードバック システムの割り当て解除を含む、1 回限りのシャットダウン手順 サービス(オーディオ マネージャーやデバイス バイブレーターなど)が含まれます。

これらのコールバック メソッドは、ユーザー補助機能の基本構造を提供します。 あります。Android システムから提供されるデータの処理方法は、 AccessibilityEvent オブジェクトの形式で提供し、ユーザーにフィードバックを提供します。対象 ユーザー補助イベントから情報を取得する方法について詳しくは、 イベントの詳細をご覧ください

ユーザー補助イベントに登録する

ユーザー補助サービスの設定で特に重要な機能の一つは、 パラメータを使用して、サービスでどのような種類のユーザー補助イベント 対処できます。この情報を指定すると、ユーザー補助サービスが連携して 特定のイベントのみを柔軟に処理できます 表示することもできます。イベントのフィルタリングには、以下が含まれます。 条件:

  • パッケージ名: ユーザー補助機能を利用できるアプリのパッケージ名を指定します。 指定することもできます。このパラメータを省略すると、 ユーザー補助サービスは、Google Cloud のユーザーが 管理できます。このパラメータは、ユーザー補助サービスまたは android:packageNames 属性を含む構成ファイルを カンマ区切りのリストを指定するか、 AccessibilityServiceInfo.packageNames できます。

  • イベントの種類: 希望するユーザー補助イベントの種類を指定します。 指定します。このパラメータは、ユーザー補助サービスまたは android:accessibilityEventTypes 属性を含む構成ファイルを | 文字で区切られたリスト(例: accessibilityEventTypes="typeViewClicked|typeViewFocused"。または 使用して AccessibilityServiceInfo.eventTypes できます。

ユーザー補助サービスを設定するときは、 サービスはそれらのイベントを処理し、登録のみを行うことができます。ユーザーは 複数のユーザー補助サービスを同時に使用する場合、そのサービスで 対処できません。他のサービスはこれらの処理を 改善し、ユーザー エクスペリエンスを向上させます。

ユーザー補助機能の音量

Android 8.0(API レベル 26)以降を搭載しているデバイスには、 STREAM_ACCESSIBILITY 音量カテゴリでは、ユーザー補助機能の音量を調整できます デバイス上の他の音とは別にサービスの音声を出力する。

ユーザー補助サービスでこのストリーム タイプを使用するには、 FLAG_ENABLE_ACCESSIBILITY_VOLUME 選択します。その後、次の番号に電話をかけて、デバイスのユーザー補助の音声の音量を変更できます。 adjustStreamVolume() メソッドを、デバイスのインスタンスの AudioManager

次のコード スニペットは、ユーザー補助機能サービスで STREAM_ACCESSIBILITY ボリューム カテゴリ:

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

詳しくは、Google I/O 2017 の Android ユーザー補助の新機能のセッション動画( 6:35。

ユーザー補助のショートカット

Android 8.0(API レベル 26)以降を搭載しているデバイスでは、 設定したいユーザー補助サービスをどの画面からでも無効にするには、 音量大と音量小の両方のボタンを同時に長押しします。このショートカットを使用すると、 TalkBack をデフォルトで無効にします。ユーザーはこのボタンを デバイスにインストールされているすべてのサービスを無効にします。

ユーザーがユーザー補助サービスから特定のユーザー補助サービス サービスは実行時にその機能をリクエストする必要があります。

詳しくは、Google I/O 2017 の Android ユーザー補助の新機能のセッション動画( 13:25。

ユーザー補助機能ボタン

ソフトウェア レンダリングのナビゲーション領域を使用し、Android 8.0 を搭載したデバイスの場合 (API レベル 26)以降では、ナビゲーション バーの右側に ユーザー補助機能ボタン。ユーザーがこのボタンを押すと、次のどちらかを起動できます。 コンテンツに応じて、有効になっているいくつかのユーザー補助機能とサービス 表示されます。

ユーザーがユーザー補助機能を使用して特定のユーザー補助サービスを呼び出せるようにする ボタンをクリックすると、 FLAG_REQUEST_ACCESSIBILITY_BUTTON AccessibilityServiceInfo オブジェクトの android:accessibilityFlags にあるフラグ 属性です。サービスは、API 呼び出しを使用してコールバックを登録できます。 registerAccessibilityButtonCallback()

次のコード スニペットは、ユーザー補助機能の設定方法を示しています。 サービスを実装して、ユーザーがユーザー補助機能ボタンを押したときに応答します。

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

詳しくは、Google I/O 2017 の Android ユーザー補助の新機能のセッション動画( 16:28。

指紋認証センサーでの操作

Android 8.0(API レベル 26)以降を搭載しているデバイス上のユーザー補助サービス デバイスの方向に沿ったスワイプ(上、下、左、右)に反応できます。 指紋認証センサーこれらのイベントに関するコールバックを受け取るようにサービスを構成するには、 次のシーケンスを実行します。

  1. USE_BIOMETRIC を宣言する 権限と、 CAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES 備えています。
  2. FLAG_REQUEST_FINGERPRINT_GESTURES を設定します。 android:accessibilityFlags 属性で指定する必要があります。
  3. registerFingerprintGestureCallback() を使用してコールバックを登録します。
で確認できます。

指紋認証センサーのないデバイスもあることを忘れないでください。特定 デバイスがセンサーをサポートしているかどうかについては、 isHardwareDetected() メソッドを呼び出します。指紋認証センサーが内蔵されているデバイスでも、 センサーは、認証目的で使用するときに使用されます。いつ特定するか センサーが利用可能になったら、 isGestureDetectionAvailable() メソッドを実行し、 onGestureDetectionAvailabilityChanged() 呼び出すことができます。

次のコード スニペットは、指紋認証センサーでの操作を使った仮想ゲームボードのナビゲーションの例を示します。

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

詳しくは、Google I/O 2017 の Android ユーザー補助の新機能のセッション動画( 9:03。

多言語でのテキスト読み上げ

Android 8.0(API レベル 26)以降、Android のテキスト読み上げ(TTS)サービス 1 つのブロックで複数の言語のフレーズを識別して発話できます。 あります。ユーザー補助機能でこの自動言語切り替え機能を有効にするには、 すべての文字列を 次に示すように、LocaleSpan オブジェクト これを次のコード スニペットに示します。

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

詳しくは、Google I/O 2017 の Android ユーザー補助の新機能のセッション動画( 10:59。

ユーザーに代わって行動する

2011 年以降、ユーザー補助サービスはユーザーの代わりに 入力フォーカスの変更、ユーザー インターフェース要素の選択(有効化)を行います。イン 2012 年 - リストのスクロールや操作など、アクションの範囲を拡大 説明しましたユーザー補助サービスは、 ホーム画面に移動し、[戻る] ボタンを押し、 通知画面と最近使ったアプリのリストです。2012 年以降、Android には ユーザー補助機能フォーカス: 表示されるすべての要素を ユーザー補助サービスを提供します

こうした機能により、ユーザー補助サービスのデベロッパーは、 ジェスチャー ナビゲーションなどのナビゲーション モードを提供し、障がいのあるユーザーのために Android 搭載デバイスの操作が改善されました。

ジェスチャーをリッスンする

ユーザー補助サービスは、特定のジェスチャーを認識し、 使用できます。この機能を使用するには、ユーザー補助サービス リクエストで タッチガイド機能の有効化サービスはこれを 有効にするには flags サービスの AccessibilityServiceInfo インスタンスの FLAG_REQUEST_TOUCH_EXPLORATION_MODE, 必要があります。

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

サービスがタッチガイドの有効化をリクエストした後は、 この機能がまだ有効になっていない場合は、オンにできます。この機能が アクティブな場合、サービスは サービスの onGesture() コールバック メソッドを呼び出し、ユーザーに代わって応答できます。

連続ジェスチャー

Android 8.0(API レベル 26)を搭載したデバイスは、「連続」ジェスチャーをサポートしています。 複数の操作を含むプログラムによる操作 Path オブジェクト。

ストロークのシーケンスを指定する際、そのストロークが 最後の引数 willContinue を使用して、 GestureDescription.StrokeDescription 実装します。これを次のコード スニペットに示します。

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

詳しくは、Google I/O 2017 の Android ユーザー補助の新機能のセッション動画( 15:47。

ユーザー補助アクションを使用する

ユーザー補助サービスは、ユーザーの代わりに、 生産性向上に役立ちますユーザー補助機能は 実行アクションは 2011 年に追加され、2012 年には大幅に拡大しました。

ユーザーに代わって動作するには、ユーザー補助サービスを登録する必要があります アプリからイベントを受信し、コンテンツを表示する権限をリクエストする 設定するには、android:canRetrieveWindowContenttrue に設定して、 サービス構成ファイル。イベントを受信すると、 サービスからそのサービス アカウントを AccessibilityNodeInfo イベントから getSource()AccessibilityNodeInfo オブジェクトを使用すると、サービスはビューを探索できます。 ユーザーに代わって行うアクションを決定し 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();
    }
    ...
}

performAction() メソッドを使用すると、サービスは 。サービスが次のようなグローバルなアクションを実行する必要がある場合、 ホーム画面に移動する、戻るボタンをタップする、または 通知画面または最近使ったアプリのリストを表示し、 performGlobalAction() メソッドを呼び出します。

フォーカス タイプを使用する

2012 年、Android は「ユーザー補助フォーカス」というユーザー インターフェースのフォーカスを導入しました。 ユーザー補助サービスは、このフォーカスを使用して、表示されるユーザー インターフェースを選択できます それに基づいて操作できますこのフォーカス タイプは、入力フォーカスとは異なります。 ユーザーが画面上のどのユーザー インターフェース要素が入力を受け取るかを決定する キーボードの Enter キーを押すか、中央の D-pad のボタンです

ユーザー インターフェースの 1 つの要素に入力フォーカスがあっても、 別の要素にユーザー補助のフォーカスが 設定されていますユーザー補助を重視する目的は 視覚的要素とやり取りする方法をユーザー補助サービスに提供し、 その要素がどこから入力フォーカス可能かにかかわらず、画面上の要素 説明しましたユーザー補助サービスとの連携を 「新規顧客の獲得」目標をガイドラインに従って、アプリの入力要素をテストする サービスをテストするために アプリを操作した場合です

ユーザー補助サービスは、どのユーザー インターフェース要素に入力があるかを判断できる [フォーカス] または [ユーザー補助機能のフォーカス] AccessibilityNodeInfo.findFocus() メソッドを呼び出します。入力フォーカスで選択できる要素を検索することもできます 使用 focusSearch() メソッドを呼び出します。最後に、ユーザー補助サービスは、 performAction(AccessibilityNodeInfo.ACTION_SET_ACCESSIBILITY_FOCUS) メソッドを呼び出します。

情報を入手する

ユーザー補助サービスには、鍵を収集して表す標準的な方法がある ユーザーが提供する情報(予定の詳細、テキスト、番号など)の単位。

ウィンドウの変更の詳細を取得する

Android 9(API レベル 28)以降では、アプリがウィンドウの更新を アプリが複数のウィンドウを同時に再描画します。特定の TYPE_WINDOWS_CHANGED イベントが発生したら、 getWindowChanges() API を使用してウィンドウがどのように変化するかを判断します。マルチウィンドウの更新中、各ウィンドウは 独自のイベントセットが生成されます。getSource() メソッドはルートを 各イベントに関連付けられたウィンドウを 表示できます

アプリでユーザー補助機能ペインが定義されている場合 タイトル View 個のオブジェクトがある場合、サービスは アプリの UI が更新されます。特定の TYPE_WINDOW_STATE_CHANGED イベントが発生したときに返される型を使用して、 getContentChangeTypes() ウィンドウの変化を決定しますたとえばフレームワークは 新しいタイトルが付けられたり、ペインが消えたりしたときに通知を受け取れます。

イベントの詳細を取得する

Android はユーザー インターフェースに関する情報をユーザー補助サービスに提供 AccessibilityEvent オブジェクトを介してやり取りできます。以前のバージョンの Android では、 情報を提供することで、アクセシビリティに関する重要な内容を ユーザーが選択したユーザー インターフェース コントロールの詳細(制限付き) コンテキスト情報を提供します。多くの場合、この欠落しているコンテキスト情報は、 これは、選択したコントロールの意味を理解するうえで非常に重要です。

コンテキストが重要なインターフェースの例はカレンダーや日である 選択しますユーザーが月曜日から金曜日までのリストから午後 4 時の時間帯を選択した場合 ユーザー補助サービスは「午後 4 時」とアナウンスするが、平日は通知されない 名前、日付、月の名前のいずれかのみを検索した場合、 混乱を招きますこの場合、ユーザー インターフェース コントロールのコンテキストが 追加することもできます。

2011 年以降、Android はユーザーが利用する情報量を ユーザー インターフェースのインタラクションに関する情報を、 ビュー階層に基づくユーザー補助機能イベントですビュー階層とは コンポーネント(その親)とユーザーを含むユーザー インターフェース コンポーネント そのコンポーネント(その子)に含まれる可能性のあるインターフェース要素を定義します。イン このようにして、Android はユーザー補助イベントに関するより詳細な情報を提供し、 ユーザー補助サービスは、ユーザーにとってより有用なフィードバックを提供します。

ユーザー補助サービスは、API 経由でユーザー インターフェース イベントに関する情報を取得します。 システムによってサービスのAccessibilityEventonAccessibilityEvent() コールバック メソッド。このオブジェクトは、kubectl の これには、操作されたオブジェクトのタイプ、説明テキスト、 その他の詳細が表示されます。

  • AccessibilityEvent.getRecordCount() および getRecord(int): これらのメソッドを使用すると、 AccessibilityRecord によって渡される AccessibilityEvent に寄与するオブジェクト ありませんこの詳細レベルでは、アラートが発生したイベントのコンテキストが ユーザー補助サービスがトリガーされます。

  • AccessibilityRecord.getSource(): このメソッドは AccessibilityNodeInfo オブジェクトを返します。このオブジェクトを使用すると、 ビュー レイアウト階層(親と子)をリクエストする イベントの発生元を返します。この機能により イベントの完全なコンテキストを調査します。これにはコンテンツや 親ビューまたは子ビューの状態です。

で確認できます。

Android プラットフォームには、AccessibilityService でクエリを実行できる機能が備わっています。 情報を生成する UI コンポーネントに関する情報が収集されます。 その親と子の両方が含まれますそのためには、次の行を設定します。 必要があります。

android:canRetrieveWindowContent="true"

その後、getSource() を使用して AccessibilityNodeInfo オブジェクトを取得します。 この呼び出しは、イベントが発生したウィンドウが アクティブウィンドウのままですそうでない場合は null を返すため、それに応じて動作します。

次の例では、イベントを受信したときにコードが次の処理を行います。

  1. イベントが発生したビューの親をすぐに取得します。
  2. このビューで、子ビューとしてラベルとチェックボックスを探します。
  3. 見つかった場合は、ユーザーに報告する文字列を作成し、 チェックしたかどうかを確認します

ビュー階層の走査中に null 値が返された場合は、 手法は静かにあきらめます。

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

以上で、正常に機能するユーザー補助サービスが完成しました。どのように構成するかを ユーザーとやり取りするために、Android のテキスト読み上げ機能を エンジン または Vibrator を使用して触覚を提供 できます。

テキストを処理する

Android 8.0(API レベル 26)以上を搭載するデバイスには、画面上に表示される特定のテキストを、ユーザー補助サービスが識別して操作できるようにするテキスト処理機能がいくつか用意されています。

ツールチップ

Android 9(API レベル 28)では、 アプリの UI のツールチップ。使用 getTooltipText() ツールチップのテキストを読み取り、 ACTION_SHOW_TOOLTIP および ACTION_HIDE_TOOLTIP View のインスタンスにツールチップの表示 / 非表示を指示するコードが追加されました。

ヒントのテキスト

2017 年以降、Android では、 テキストベースのオブジェクトのヒントテキスト:

  • isShowingHintText() および setShowingHintText() メソッドは、ノードの現在のテキストが入力テキストかどうかを content はノードのヒントテキストを表します。
  • getHintText() ヒントのテキスト自体にアクセスできます。オブジェクトが表示されていない場合でも ヒントのテキスト。getHintText() の呼び出しは成功します。

画面上のテキスト文字の場所

Android 8.0(API レベル 26)以降を搭載しているデバイスでは、ユーザー補助サービス 表示される文字の境界ボックスの画面座標を特定できます TextView ウィジェット内で作成できます。サービス 座標を見つけるには refreshWithExtraData() 渡しています EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY を最初の引数として指定し、さらに Bundle オブジェクトとして使用します。 渡します。メソッドが実行されると、 次の Parcelable 配列が指定された Bundle 引数 Rect オブジェクト。各 Rect オブジェクト 特定の文字の境界ボックスを表します。

標準化された片側範囲値

一部の AccessibilityNodeInfo オブジェクトは、 AccessibilityNodeInfo.RangeInfo UI 要素が値の範囲を取ることができることを示します。ルールの作成時は、 使用 RangeInfo.obtain() または、BigQuery を使用して範囲の極端な値を取得する場合に、 getMin() および getMax(), Android 8.0(API レベル 26)以降を搭載しているデバイスは、 標準化された方法での片側範囲:

ユーザー補助イベントに応答する

イベントを実行してリッスンするようにサービスを設定したら、次に、 AccessibilityEvent が到着したときに行うべき処理を把握している。まず、Terraform で onAccessibilityEvent(AccessibilityEvent) メソッドを呼び出します。そのメソッドで、 getEventType() イベントのタイプを特定し、 getContentDescription() イベントを発生させるビューに関連付けられたラベルテキストを抽出します。

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

参考情報

詳細については、次のリソースをご覧ください。

ガイド

Codelab