나만의 접근성 서비스 만들기

접근성 서비스는 사용자 인터페이스를 개선하여 장애가 있거나 일시적으로 완전히 상호작용할 수 없는 사용자 할 수 있습니다. 예를 들어 운전 중이거나 유아를 돌보거나 매우 시끄러운 파티에 참석하는 경우 추가 또는 대체 인터페이스가 필요할 수 있습니다. 의견을 보냅니다.

Android는 다음과 같은 표준 접근성 서비스를 제공합니다. TalkBack , 개발자는 자신만의 서비스를 만들고 배포할 수 있습니다. 이 문서 에서는 접근성 서비스 구축의 기본사항을 설명합니다.

접근성 서비스는 일반 앱과 번들로 묶거나 실행할 수 있습니다 서비스를 만드는 단계는 어떤 상황이든 말이죠

접근성 서비스 만들기

프로젝트 내에서 확장되는 클래스를 만듭니다. 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?) {}
...
}

자바

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 클래스를 있습니다.

매니페스트 선언 및 권한

접근성 서비스를 제공하는 앱은 접근성 서비스로 취급되도록 앱 매니페스트를 있습니다. 이 섹션에서는 액세스할 수 있습니다

접근성 서비스 선언

앱을 접근성 서비스로 처리하려면 service를 포함합니다. 요소(activity 요소가 아닌 application 내) 요소를 지정할 수 있습니다. 또한 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"
/>

접근성 서비스 구성 파일에 대한 자세한 내용은 다음 참조를 확인하세요. 문서:

동적으로 설정할 수 있는 구성 설정에 관한 자세한 내용은 자세한 내용은 AccessibilityServiceInfo 드림 참조하세요.

접근성 서비스 구성

구성 변수를 설정할 때 다음을 고려하세요. 다음을 실행하는 방법을 시스템에 알려주는 접근성 서비스:

  • 어떤 이벤트 유형에 응답하시겠습니까?
  • 서비스가 모든 앱에 대해 활성화되어야 하나요, 아니면 특정 패키지에만 활성화되어야 하나요? 무엇인가요?
  • 서비스에서 사용하는 여러 피드백 유형 등을 알릴 수 있습니다.

이러한 변수를 설정하는 데는 두 가지 옵션이 있습니다. 이전 버전과 호환되는 옵션 코드에 이를 설정하고 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

}

자바

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

}

두 번째 옵션은 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을 사용하는 경우 <meta-data> 태그를 서비스 선언을 정의합니다. 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(): (선택사항) 시스템이 이 메서드를 호출합니다. 사용자 인증 정보를 제공합니다 이 방법을 사용하여 일회성 설정 수행 서비스 관련 단계(사용자 피드백 시스템에 연결 포함) 오디오 관리자나 기기 바이브레이터 같은 기능이 포함됩니다. 런타임 시 서비스 구성을 확인하거나 일회성 조정을 할 수 있습니다. 이는 setServiceInfo()를 호출하기에 편리한 위치입니다.

  • onAccessibilityEvent(): (필수) 다음과 같은 경우 시스템이 이 메서드를 다시 호출합니다. Kubernetes가 AccessibilityEvent 접근성에서 지정한 이벤트 필터링 매개변수와 일치하는 서비스(예: 사용자가 버튼을 탭하거나 사용자 인터페이스에 포커스를 두는 경우) 접근성 서비스에서 피드백을 제공하는 앱의 제어권을 제어할 수 있습니다. 날짜 시스템에서 이 메서드를 호출하고 연결된 AccessibilityEvent를 전달합니다. 서비스에서 해석하고 이를 사용하여 있습니다. 이 메서드는 수명 주기 동안 여러 번 호출할 수 있습니다. 있습니다.

  • onInterrupt(): (필수) 시스템이 이 메서드를 호출합니다. 서비스에서 제공하는 피드백을 중단하려는 경우 다른 컨트롤로 포커스를 이동하는 등 사용자 작업에 대한 응답 이 메서드는 서비스 수명 주기 동안 여러 번 호출할 수 있습니다.

  • onUnbind(): (선택사항) 시스템이 다음과 같을 때 이 메서드를 호출합니다. 접근성 서비스를 종료하려고 합니다. 이 메서드를 사용하여 사용자 피드백 시스템 할당 해제를 포함한 일회성 종료 절차 오디오 관리자나 기기 바이브레이터 같은 기능이 포함됩니다.

이러한 콜백 메서드는 접근성을 위한 기본 구조를 제공합니다. 있습니다. 다음에서 Android 시스템에서 제공하는 데이터를 처리하는 방법을 결정할 수 있습니다. AccessibilityEvent 객체의 양식을 사용하여 사용자에게 피드백을 제공합니다. 대상 접근성 이벤트에서 정보를 얻는 방법에 대한 자세한 내용은 이벤트 세부정보를 참고하세요.

접근성 이벤트 등록

접근성 서비스 구성의 가장 중요한 기능 중 하나는 매개변수를 사용하면 서비스에서 어떤 유형의 접근성 이벤트를 처리할 수 있습니다. 이 정보를 지정하면 접근성 서비스가 협력할 수 있습니다. 특정 이벤트만 처리할 수 있는 유연성을 제공합니다. 특정 앱의 유형을 가져올 수 있습니다. 이벤트 필터링에는 다음이 포함될 수 있습니다. 기준:

  • 패키지 이름: 접근성 기능이 포함된 앱의 패키지 이름을 지정합니다. 지정할 수 있습니다. 이 매개변수를 생략하면 접근성 서비스가 서비스 접근성에 사용 가능한 것으로 간주됨 사용할 수 있습니다. 접근성 서비스에서 이 매개변수를 설정할 수 있습니다. android:packageNames 속성이 포함된 구성 파일을 쉼표로 구분하거나 AccessibilityServiceInfo.packageNames 있습니다.

  • 이벤트 유형: 원하는 접근성 이벤트의 유형을 지정합니다. 처리해야 합니다 접근성 서비스에서 이 매개변수를 설정할 수 있습니다. android:accessibilityEventTypes 속성이 | 문자로 구분된 목록. 예: accessibilityEventTypes="typeViewClicked|typeViewFocused"입니다. 또는 이를 사용하여 AccessibilityServiceInfo.eventTypes 드림 있습니다.

접근성 서비스를 설정할 때 서비스는 이러한 이벤트를 처리하고 이러한 이벤트만 등록할 수 있습니다. 사용자가 활성화할 수 있으므로 접근성 서비스를 한 번에 2개 이상 사용하는 경우 서비스에서 처리할 수 없는 이벤트를 발생시킵니다. 다른 서비스에서 이러한 문제를 처리할 수도 이벤트를 사용하여 사용자 환경을 개선합니다.

접근성 볼륨

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

자바

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에 있는 플래그 속성의 값을 제공합니다. 그러면 서비스에서 콜백을 등록하여 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)
    }
}

자바

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

자바

// 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시 3분입니다.

다국어 TTS(텍스트 음성 변환)

Android 8.0 (API 수준 26)부터 Android의 텍스트 음성 변환 (TTS) 서비스 한 블록 내에서 여러 언어로 된 구문을 식별하고 말할 수 있습니다. 있습니다. 접근성 도구에서 이러한 자동 언어 전환 기능을 사용하려면 모든 문자열을 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)
    }
}

자바

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

자바

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

자바

// 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로 설정하여 서비스 구성 파일을 사용합니다. 이벤트 수신 시기: Cloud Shell에서 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()
        }
    }
    ...
}

자바

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패드의 버튼입니다.

사용자 인터페이스의 한 요소가 입력 포커스를 가질 수 있지만 다른 요소에 접근성 포커스가 있습니다. 접근성에 중점을 두는 목적은 볼 수 있는 사용자와 상호작용하는 방법을 화면의 요소(해당 요소가 시스템 관점에서 살펴봤습니다 접근성 서비스의 상호작용 보장 제대로 작동하는지 앱의 접근성을 사용하여 서비스를 테스트합니다. 콘텐츠를 생성할 수 있습니다.

접근성 서비스는 어떤 사용자 인터페이스 요소에 입력이 있는지 확인할 수 있습니다. 포커스 또는 접근성 포커스는 AccessibilityNodeInfo.findFocus() 드림 메서드를 사용하여 축소하도록 요청합니다. 입력 포커스를 사용하여 선택할 수 있는 요소를 검색할 수도 있습니다. 사용 focusSearch() 드림 메서드를 사용하여 축소하도록 요청합니다. 마지막으로 접근성 서비스는 performAction(AccessibilityNodeInfo.ACTION_SET_ACCESSIBILITY_FOCUS) 드림 메서드를 사용하여 축소하도록 요청합니다.

정보 수집하기

접근성 서비스에는 키를 수집하고 표현하는 표준 메서드가 있음 사용자가 제공한 정보의 단위입니다.

창(window) 변경 세부정보 가져오기

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는 접근성 이벤트에 대해 더 풍부한 세부정보를 제공할 수 있으므로 접근성 서비스는 사용자에게 더 유용한 피드백을 제공합니다.

접근성 서비스는 다음을 통해 사용자 인터페이스 이벤트에 관한 정보를 가져옵니다. AccessibilityEvent 시스템에서 서비스의 onAccessibilityEvent() 콜백 메서드와 함께 사용할 수 있습니다. 이 객체는 이벤트(예: 작업 중인 객체의 유형, 설명 텍스트 및 확인할 수 있습니다

  • AccessibilityEvent.getRecordCount() 드림 및 getRecord(int): 이러한 메서드를 사용하면 AccessibilityRecord 객체에 의해 전달된 AccessibilityEvent에 기여하는 객체는 있습니다. 이 세부정보 수준은 접근성 서비스를 트리거합니다.

  • AccessibilityRecord.getSource(): 이 메서드는 AccessibilityNodeInfo 객체를 반환합니다. 이 객체를 사용하면 현재 UI를 표시할 구성요소의 뷰 레이아웃 계층 구조 (상위 요소 및 하위 요소)를 접근성 이벤트를 시작합니다. 이 기능을 사용하면 조사합니다. 상태를 반환합니다.

를 통해 개인정보처리방침을 정의할 수 있습니다.

Android 플랫폼은 AccessibilityService가 쿼리하는 기능을 제공합니다. 뷰 계층 구조: 뷰 계층 구조를 생성하여 이벤트 및 그 상위 및 하위 이벤트입니다. 이렇게 하려면 다음과 같습니다.

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

자바

// 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)에는 도움말을 참조하세요. 사용 getTooltipText() 드림 를 사용하여 도움말 텍스트를 읽고 ACTION_SHOW_TOOLTIPACTION_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()님, 를 사용하여 범위의 극한값을 가져올 때 getMin()getMax(), Android 8.0 (API 레벨 26) 이상을 실행하는 기기는 단방향 범위를 표준화된 방식으로 변환:

접근성 이벤트에 응답

이제 서비스가 이벤트를 실행하고 수신 대기하도록 설정되었으므로 AccessibilityEvent이 도착하면 해야 할 작업을 알고 있습니다. 먼저 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