Google은 흑인 공동체를 위한 인종 간 평등을 진전시키기 위해 노력하고 있습니다. Google에서 어떤 노력을 하고 있는지 확인하세요.

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

접근성 서비스는 장애를 가진 사용자 또는 일시적으로 기기와 완벽하게 상호작용할 수 없는 사용자를 지원하기 위해 사용자 인터페이스를 향상하는 애플리케이션입니다. 예를 들어 운전 중이거나 어린 자녀를 돌보고 있거나 매우 시끄러운 파티에 참석 중인 사용자는 추가 또는 대체 인터페이스 피드백이 필요할 수 있습니다.

Android에서는 음성 안내 지원을 비롯한 표준 접근성 서비스를 제공하며 개발자는 나만의 서비스를 만들고 배포할 수 있습니다. 이 문서에서는 접근성 서비스 빌드의 기본사항에 관해 설명합니다.

참고: 앱에서는 장애를 가진 사용자가 앱과 상호작용하도록 지원하는 목적으로만 플랫폼 수준의 접근성 서비스를 사용해야 합니다.

접근성 서비스를 빌드하고 배포할 수 있는 기능은 Android 1.6(API 수준 4)에서 도입되었으며 Android 4.0(API 수준 14)에서 대폭 개선되었습니다. 또한 Android 지원 라이브러리도 Android 4.0 출시를 통해 Android 1.6의 향상된 접근성 기능을 지원하도록 업데이트되었습니다. 널리 호환되는 접근성 서비스를 목표로 하는 개발자는 지원 라이브러리를 사용하고 Android 4.0에 도입된 고급 접근성 기능을 개발하는 것이 좋습니다.

접근성 서비스 만들기

접근성 서비스는 일반 애플리케이션과 번들로 제공하거나 독립형 Android 프로젝트로 만들 수 있습니다. 서비스를 만드는 단계는 두 경우 모두 동일합니다. 프로젝트 내에서 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() {
    }

...
}

이 서비스를 위한 새 프로젝트를 만들었지만 서비스와 연결된 앱을 포함할 계획이 없다면 소스에서 starter 활동 클래스를 삭제할 수 있습니다.

매니페스트 선언 및 권한

접근성 서비스를 제공하는 애플리케이션에는 Android 시스템에서 접근성 서비스로 처리하도록 애플리케이션 매니페스트에 특정 선언을 포함해야 합니다. 이 섹션에서는 접근성 서비스의 필수 및 선택적 설정을 설명합니다.

접근성 서비스 선언

접근성 서비스로 처리하려면 매니페스트의 application 요소 내에 activity 요소가 아닌 service 요소를 포함해야 합니다. 또한 service 요소 내에 접근성 서비스 인텐트 필터도 포함해야 합니다. Android 4.1 이상과 호환되도록 하려면 시스템에서만 서비스에 연결할 수 있도록 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>

이러한 선언은 Android 1.6(API 수준 4) 이상에 배포된 모든 접근성 서비스에 필요합니다.

접근성 서비스 구성

접근성 서비스는 서비스가 처리하는 접근성 이벤트의 유형을 지정하는 구성 및 서비스에 관한 추가 정보도 제공해야 합니다. 접근성 서비스의 구성은 AccessibilityServiceInfo 클래스에 포함되어 있습니다. 서비스에서는 런타임 시 이 클래스의 인스턴스와 setServiceInfo()를 사용하여 구성을 빌드하고 설정할 수 있습니다. 그러나 이 메서드를 사용해도 일부 구성 옵션은 사용할 수 없습니다.

Android 4.0부터 다음 예에서와 같이 접근성 서비스의 모든 옵션을 설정할 수 있게 하는 <meta-data> 요소를 구성 파일의 참조와 함께 매니페스트에 포함할 수 있습니다.

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

meta-data 요소는 애플리케이션의 리소스 디렉터리에 만드는 XML 파일(<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 참조 문서를 확인하세요.

접근성 서비스 구성

접근성 서비스의 구성 변수를 설정하면 접근성 서비스를 실행할 방법과 시기를 시스템에 알릴 수 있습니다. 예를 들어 응답할 이벤트 유형, 서비스를 모든 애플리케이션에서 활성화해야 하는지 또는 특정 패키지 이름에만 활성화해야 하는지 여부, 서비스에서 사용하는 여러 피드백 유형 등을 알릴 수 있습니다.

이러한 변수를 설정하는 방법에는 두 가지 옵션이 있습니다. 이전 버전과 호환되는 옵션은 setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)를 사용해 코드에 변수를 설정하는 것입니다. 이렇게 하려면 onServiceConnected() 메서드를 재정의하고 재정의된 메서드에 서비스를 구성합니다.

Kotlin

override fun onServiceConnected() {
    info.apply {
        // Set the type of events that this service wants to listen to. Others
        // won't be passed to this service.
        eventTypes = AccessibilityEvent.TYPE_VIEW_CLICKED or AccessibilityEvent.TYPE_VIEW_FOCUSED

        // If you only want this service to work with specific applications, set their
        // package names here. Otherwise, when the service is activated, it will listen
        // to events from all applications.
        packageNames = arrayOf("com.example.android.myFirstApp", "com.example.android.mySecondApp")

        // Set the type of feedback your service will provide.
        feedbackType = AccessibilityServiceInfo.FEEDBACK_SPOKEN

        // Default services are invoked only if no package-specific ones are present
        // for the type of AccessibilityEvent generated. This service *is*
        // application-specific, so the flag isn't necessary. If this was a
        // general-purpose service, it would be worth considering 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
    // won't be passed to this service.
    info.eventTypes = AccessibilityEvent.TYPE_VIEW_CLICKED |
            AccessibilityEvent.TYPE_VIEW_FOCUSED;

    // If you only want this service to work with specific applications, set their
    // package names here. Otherwise, when the service is activated, it will listen
    // to events from all applications.
    info.packageNames = new String[]
            {"com.example.android.myFirstApp", "com.example.android.mySecondApp"};

    // Set the type of feedback your service will provide.
    info.feedbackType = AccessibilityServiceInfo.FEEDBACK_SPOKEN;

    // Default services are invoked only if no package-specific ones are present
    // for the type of AccessibilityEvent generated. This service *is*
    // application-specific, so the flag isn't necessary. If this was a
    // general-purpose service, it would be worth considering 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 경로를 사용하는 경우 서비스 선언에 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() - (선택사항) 이 시스템은 접근성 서비스에 연결되면 이 메서드를 호출합니다. 오디오 관리자나 기기 바이브레이터와 같은 사용자 피드백 시스템 서비스에 연결하는 등 서비스의 모든 일회성 설정 단계를 진행하려면 이 메서드를 사용하세요. 런타임 시 서비스 구성을 설정하거나 한 번 조정하려는 경우 setServiceInfo()를 호출하기에 편리한 위치입니다.
  • onAccessibilityEvent() - (필수) 접근성 서비스에서 지정한 이벤트 필터링 매개변수와 일치하는 AccessibilityEvent를 이 메서드에서 감지하면 시스템에서 이 메서드를 다시 호출합니다. 예를 들어 접근성 서비스를 통해 피드백이 제공되는 애플리케이션에서 사용자가 버튼을 클릭하거나 사용자 인터페이스 컨트롤에 포커스를 맞춘 경우가 여기에 해당합니다. 이 경우 시스템에서 이 메서드를 호출하여 연결된 AccessibilityEvent를 전달하면 서비스에서 이를 해석하고 사용하여 사용자에게 피드백을 제공할 수 있습니다. 이 메서드는 서비스 수명 주기 동안 여러 번 호출될 수 있습니다.
  • onInterrupt() - (필수) 사용자가 다른 컨트롤로 포커스를 옮기는 등 사용자 작업에 응답하기 위해 시스템에서 서비스가 제공 중인 피드백을 중단하려는 경우 호출되는 메서드입니다. 이 메서드는 서비스 수명 주기 동안 여러 번 호출될 수 있습니다.
  • onUnbind() - (선택사항) 시스템에서 접근성 서비스를 종료하려고 할 때 호출되는 메서드입니다. 오디오 관리자나 기기 바이브레이터와 같은 사용자 피드백 시스템 서비스를 할당 해제하는 등 서비스의 모든 일회성 종료 절차를 진행하려면 이 메서드를 사용하세요.

이러한 콜백 메서드는 접근성 서비스의 기본 구조를 제공합니다. Android 시스템에서 AccessibilityEvent 객체의 형태로 제공하는 데이터를 처리하고 사용자에게 피드백을 제공하는 방법을 결정하는 것은 개발자에게 달려 있습니다. 접근성 이벤트에서 정보를 가져오는 방법에 관한 자세한 내용은 이벤트 세부정보 가져오기를 참조하세요.

접근성 이벤트 등록

접근성 서비스 구성 매개변수에서 가장 중요한 함수 중 하나는 서비스에서 처리할 수 있는 접근성 이벤트의 유형을 개발자가 지정할 수 있도록 하는 것입니다. 이 정보를 지정할 수 있으면 접근성 서비스가 서로 협력할 수 있으며 개발자가 특정 애플리케이션의 특정 이벤트 유형만 유연하게 처리할 수 있습니다. 이벤트 필터링에 포함할 수 있는 기준은 다음과 같습니다.

  • 패키지 이름 - 서비스에서 처리하도록 할 접근성 이벤트가 있는 애플리케이션의 패키지 이름을 지정합니다. 이 매개변수를 생략하면 접근성 서비스는 모든 애플리케이션의 서비스 접근성 이벤트에 사용 가능한 것으로 간주됩니다. 이 매개변수는 접근성 서비스 구성 파일에서 android:packageNames 속성을 통해 쉼표로 구분된 목록으로 설정하거나 AccessibilityServiceInfo.packageNames 멤버를 사용하여 설정할 수 있습니다.
  • 이벤트 유형 - 서비스에서 처리하도록 할 접근성 이벤트의 유형을 지정합니다. 이 매개변수는 접근성 서비스 구성 파일에서 android:accessibilityEventTypes 속성을 통해 | 문자로 구분된 목록으로 설정(예: accessibilityEventTypes="typeViewClicked|typeViewFocused")하거나 AccessibilityServiceInfo.eventTypes 멤버를 사용하여 설정할 수 있습니다.

접근성 서비스를 설정할 때 서비스에서 처리할 수 있는 이벤트를 신중하게 고려하고 이러한 이벤트만 등록하세요. 사용자는 한 번에 둘 이상의 접근성 서비스를 활성화할 수 있으므로 서비스에서 처리할 수 없는 이벤트를 사용해서는 안 됩니다. 사용자의 환경을 개선하기 위해 다른 서비스에서 이러한 이벤트를 처리할 수도 있습니다.

접근성 볼륨

Android 8.0(API 수준 26) 이상을 실행하는 기기에는 기기의 다른 소리와 관계없이 접근성 서비스의 오디오 출력 볼륨을 제어할 수 있게 하는 STREAM_ACCESSIBILITY 볼륨 카테고리가 포함되어 있습니다.

FLAG_ENABLE_ACCESSIBILITY_VOLUME 옵션을 설정하면 접근성 서비스에서 이 스트림 유형을 사용할 수 있습니다. 그런 다음 기기의 AudioManager 인스턴스에서 adjustStreamVolume() 메서드를 호출하여 기기의 접근성 오디오 볼륨을 변경할 수 있습니다.

다음 코드 스니펫에서는 접근성 서비스에서 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) 이상을 실행하는 기기에서 사용자는 두 볼륨 키를 동시에 길게 눌러 어느 화면에서나 원하는 접근성 서비스를 사용 또는 중지할 수 있습니다. 이 단축키는 기본적으로 음성 안내 지원을 사용하거나 중지하지만 사용자는 개발자의 서비스를 비롯하여 자신의 기기에 설치된 모든 서비스를 사용하거나 중지하도록 버튼을 구성할 수 있습니다.

사용자가 접근성 단축키에서 특정 접근성 서비스에 액세스하려면 서비스가 시작될 때 서비스에서 런타임 시 기능을 요청해야 합니다.

자세한 내용은 Google I/O 2017의 Android 접근성의 새로운 기능 세션 동영상을 13분 25초부터 참조하세요.

접근성 버튼

소프트웨어에서 렌더링한 탐색 영역을 사용하며 Android 8.0(API 수준 26) 이상을 실행하는 기기에는 탐색 메뉴 오른쪽에 접근성 버튼이 있습니다. 사용자가 이 버튼을 누르면 현재 화면에 표시된 콘텐츠에 따라 사용 설정된 여러 접근성 기능 및 서비스 중 하나를 호출할 수 있습니다.

사용자가 접근성 버튼을 사용하여 특정 접근성 서비스를 호출할 수 있도록 하려면 서비스에서 AccessibilityServiceInfo 객체의 android:accessibilityFlags 속성에 FLAG_REQUEST_ACCESSIBILITY_BUTTON 플래그를 추가해야 합니다. 그런 다음 서비스에서 registerAccessibilityButtonCallback()을 사용하여 콜백을 등록할 수 있습니다.

참고: 이 기능은 소프트웨어에서 렌더링한 탐색 영역을 제공하는 기기에서만 사용할 수 있습니다. 서비스에서는 항상 isAccessibilityButtonAvailable()을 사용해야 하며 onAvailabilityChanged()를 구현하여 접근성 버튼의 가용성에 따라 변경사항에 응답해야 합니다. 이렇게 하면 접근성 버튼이 지원되지 않거나 사용할 수 없는 경우에도 사용자가 서비스의 기능에 항상 액세스할 수 있습니다.

다음 코드 스니펫에서는 사용자가 접근성 버튼을 누를 때 이에 응답하도록 접근성 서비스를 구성할 수 있는 방법을 보여줍니다.

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_FINGERPRINT 권한 및 CAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES 기능을 선언합니다.
  2. android:accessibilityFlags 속성 내에 FLAG_REQUEST_FINGERPRINT_GESTURES 플래그를 설정합니다.
  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"
    ... />

MyFingerprintGestureService.java

Kotlin

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

자바

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초부터 참조하세요.

사용자를 위한 작업 실행

Android 4.0(API 수준 14)부터 접근성 서비스에서는 사용자를 대신하여 입력 포커스 변경, 사용자 인터페이스 요소 선택(활성화) 등의 작업을 할 수 있습니다. Android 4.1(API 수준 16)에서는 작업 범위가 목록 스크롤 및 텍스트 필드와의 상호작용을 포함하도록 확장되었습니다. 접근성 서비스에서는 홈 화면으로 이동, 뒤로 버튼 누르기, 알림 화면 및 최근 애플리케이션 목록 열기 등의 전역 작업을 할 수도 있습니다. Android 4.1에는 표시된 모든 요소를 접근성 서비스에서 선택할 수 있도록 하는 새로운 유형의 포커스인 접근성 포커스도 포함되어 있습니다.

이러한 새로운 기능을 통해 접근성 서비스 개발자는 동작 탐색과 같은 대체 탐색 모드를 만들고 장애가 있는 사용자의 Android 기기 제어를 향상할 수 있습니다.

동작 수신 대기

접근성 서비스는 특정 동작을 수신 대기하고 사용자를 대신하여 작업함으로써 응답할 수 있습니다. Android 4.1(API 수준 16)에 추가된 이 기능을 사용하려면 접근성 서비스에서 터치하여 탐색 기능의 활성화를 요청해야 합니다. 다음 예에서와 같이 서비스 AccessibilityServiceInfo 인스턴스의 flags 멤버를 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 객체를 둘 이상 포함하는 프로그래매틱 동작 지원이 포함되어 있습니다.

획 순서를 지정할 때는 다음 코드 스니펫에서와 같이 GestureDescription.StrokeDescription 생성자에 마지막 인수 willContinue를 사용해 해당 획이 동일한 프로그래매틱 동작에 속한다고 지정해야 합니다.

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초부터 참조하세요.

접근성 작업 사용

접근성 서비스는 사용자 대신 작업을 실행하여 더 간단하고 생산적으로 애플리케이션과 상호작용할 수 있습니다. 작업을 실행할 수 있는 접근성 서비스 기능은 Android 4.0(API 수준 14)에서 추가되었으며 Android 4.1(API 수준 16)에서 크게 확장되었습니다.

사용자를 대신하여 작업하려면 접근성 서비스에서 일부 또는 많은 애플리케이션의 이벤트를 등록하여 수신하고 서비스 구성 파일android:canRetrieveWindowContenttrue로 설정하여 애플리케이션의 콘텐츠를 볼 수 있는 권한을 요청해야 합니다. 서비스가 이벤트를 수신하면 getSource()를 사용해 이벤트에서 AccessibilityNodeInfo 객체를 가져올 수 있습니다. 그런 다음 서비스는 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

            // take action 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

        // take action on behalf of the user
        nodeInfo.performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);

        // recycle the nodeInfo object
        nodeInfo.recycle();
    }
    ...
}

performAction() 메서드를 사용하면 서비스가 애플리케이션 내에서 작업할 수 있습니다. 서비스에서 홈 화면 탐색, 뒤로 버튼 누르기, 알림 화면 또는 최근 애플리케이션 목록 열기 등 전역 작업을 해야 하는 경우 performGlobalAction() 메서드를 사용하세요.

포커스 유형 사용

Android 4.1(API 수준 16)에는 접근성 포커스라는 새로운 유형의 사용자 인터페이스 포커스가 도입되었습니다. 접근성 서비스는 이러한 유형의 포커스를 사용하여 표시되는 사용자 인터페이스 요소를 선택하고 작업할 수 있습니다. 이 포커스 유형은 더 잘 알려진 입력 포커스와는 다릅니다. 입력 포커스는 사용자가 문자를 입력하거나 키보드에서 Enter를 누르거나 D패드 컨트롤의 가운데 버튼을 누르면 화면에 표시된 어떤 사용자 인터페이스 요소에서 입력을 수신할지 판단합니다.

접근성 포커스는 입력 포커스와 완전히 별개이며 독립적입니다. 실제로 사용자 인터페이스의 한 요소에는 입력 포커스가 있고 다른 요소에는 접근성 포커스가 있을 수 있습니다. 접근성 포커스의 목적은 요소가 시스템 관점에서 입력에 포커스를 맞출 수 있는지 여부와 상관없이 화면에 표시되는 모든 요소와 상호작용하는 방법을 접근성 서비스에 제공하는 것입니다. 접근성 동작을 테스트하여 작동 중인 접근성 포커스를 확인할 수 있습니다. 이 기능 테스트에 관한 자세한 내용은 동작 탐색 테스트를 참조하세요.

참고: 접근성 포커스를 사용하는 접근성 서비스는 요소에서 이러한 유형의 포커스를 가질 수 있을 때 현재 입력 포커스를 동기화해야 합니다. 입력 포커스를 접근성 포커스와 동기화하지 않는 서비스는 특정 작업이 실행될 때 입력 포커스가 특정 위치에 있을 것으로 예상되는 애플리케이션에서 문제를 일으킬 위험이 있습니다.

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

정보 수집하기

접근성 서비스에는 이벤트 세부정보, 텍스트, 숫자와 같은 사용자 제공 정보의 주요 단위를 수집하고 나타내는 표준 방법도 포함되어 있습니다.

이벤트 세부정보 가져오기

Android 시스템에서는 AccessibilityEvent 객체를 통해 사용자 인터페이스 상호작용에 관한 정보를 접근성 서비스에 제공합니다. Android 4.0 이전에는 접근성 이벤트에서 사용할 수 있는 정보에서 사용자가 선택한 사용자 인터페이스 컨트롤에 관한 많은 양의 세부정보를 제공한 반면 컨텍스트 정보는 제한적으로 제공했습니다. 대부분의 경우 이렇게 누락된 컨텍스트 정보는 선택된 컨트롤의 의미를 파악하는 데 매우 중요할 수도 있습니다.

컨텍스트가 중요한 인터페이스의 예로는 캘린더 또는 일정 계획표가 있습니다. 사용자가 월요일~금요일 목록의 오후 4시 시간대를 선택하고 접근성 서비스에서 '오후 4시'는 알리는데 월, 날짜, 요일을 알리지 않는다면 알림 피드백이 혼란스러울 수 있습니다. 이 경우 사용자 인터페이스 컨트롤의 컨텍스트는 회의를 예약하려는 사용자에게 매우 중요합니다.

Android 4.0에서는 뷰 계층 구조를 기반으로 접근성 이벤트를 작성하여 접근성 서비스에서 사용자 인터페이스 상호작용에 관해 얻을 수 있는 정보의 양을 크게 늘립니다. 뷰 계층 구조는 구성요소를 포함하는 사용자 인터페이스 구성요소(상위 구성요소) 및 그 구성요소가 포함할 수 있는 사용자 인터페이스 요소(하위 구성요소)의 집합입니다. Android 시스템에서는 이런 방식으로 접근성 이벤트에 관해 훨씬 더 자세한 정보를 제공하여 접근성 서비스에서 사용자에게 더 유용한 피드백을 제공할 수 있게 합니다.

접근성 서비스는 시스템에서 서비스의 onAccessibilityEvent() 콜백 메서드로 전달한 AccessibilityEvent를 통해 사용자 인터페이스 이벤트에 관한 정보를 얻습니다. 이 객체는 처리 중인 객체의 유형, 설명 텍스트, 기타 세부정보를 비롯한 이벤트의 세부정보를 제공합니다. Android 4.0(및 이전 출시에서는 지원 라이브러리의 AccessibilityEventCompat 객체를 통해 지원됨)부터 다음 호출을 사용해 이벤트에 관한 추가 정보를 얻을 수 있습니다.

  • AccessibilityEvent.getRecordCount()getRecord(int) - 이 메서드를 사용하면 시스템에서 전달한 AccessibilityEvent에 제공된 AccessibilityRecord 객체의 집합을 가져올 수 있습니다. 이 세부정보 수준은 접근성 서비스를 트리거한 이벤트에 관해 더 많은 컨텍스트를 제공합니다.
  • AccessibilityEvent.getSource() - 이메서드는 AccessibilityNodeInfo 객체를 반환합니다. 이 객체를 사용하면 접근성 이벤트를 시작한 구성요소의 뷰 레이아웃 계층 구조(상위 및 하위)를 요청할 수 있습니다. 이 기능을 통해 접근성 서비스는 포함된 모든 뷰 또는 하위 뷰의 콘텐츠 및 상태를 비롯한 이벤트의 전체 컨텍스트를 조사할 수 있습니다.

    중요: AccessibilityEvent의 뷰 계층 구조를 조사할 수 있는 기능은 접근성 서비스에 비공개 사용자 정보를 노출할 수 있습니다. 따라서 서비스에서 canRetrieveWindowContent 속성을 포함하고 이 속성을 true로 설정하여 접근성 서비스 구성 XML 파일을 통해 이 액세스 수준을 요청해야 합니다. 서비스 구성 XML 파일에 이 설정을 포함하지 않으면 getSource()를 호출할 수 없습니다.

    참고: Android 4.1(API 수준 16) 이상에서는 getSource() 메서드뿐 아니라 AccessibilityNodeInfo.getChild()getParent()에서도 접근성에 중요하다고 간주되는 뷰(콘텐츠를 그리거나 사용자 작업에 응답하는 뷰) 객체만 반환합니다. 서비스에 모든 뷰가 필요하면 서비스 AccessibilityServiceInfo 인스턴스의 flags 멤버를 FLAG_INCLUDE_NOT_IMPORTANT_VIEWS로 설정하여 모든 뷰를 요청할 수 있습니다.

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

Android 9(API 수준 28) 이상에서는 앱에서 여러 창(window)을 동시에 다시 그릴 때 창(window) 업데이트를 계속 추적할 수 있습니다. TYPE_WINDOWS_CHANGED 이벤트가 발생하면 getWindowChanges() API를 사용하여 창(window)이 어떻게 변경되었는지 확인할 수 있습니다. 다중 창(window)을 업데이트하는 동안 각 창(window)에서는 자체 이벤트 집합을 생성합니다. getSource() 메서드는 각 이벤트와 연결된 창(window)의 루트 뷰를 반환합니다.

앱에서 View 객체의 접근성 창(pane) 제목을 정의한 경우 앱의 UI가 업데이트되면 서비스에서 이를 인식할 수 있습니다. TYPE_WINDOW_STATE_CHANGED 이벤트가 발생하면 getContentChangeTypes()에서 반환한 유형을 사용하여 창(window)이 어떻게 변경되었는지 확인합니다. 예를 들어 프레임워크는 창(pane)에 새 제목이 있거나 창(pane)이 사라지면 이를 감지할 수 있습니다.

접근성 노드 세부정보 수집

이 단계는 선택사항이지만 매우 유용합니다. Android 플랫폼에서는 뷰 계층 구조를 쿼리하여 이벤트, 상위 이벤트, 하위 이벤트를 생성한 UI 구성요소에 관한 정보를 수집하기 위해 AccessibilityService의 기능을 제공합니다. 이 기능을 제공하려면 XML 구성에 다음 행을 설정해야 합니다.

android:canRetrieveWindowContent="true"

설정이 완료되면 getSource()를 사용해 AccessibilityNodeInfo 객체를 가져옵니다. 이 호출은 이벤트가 발생한 창(window)이 계속 활성 창(window)인 경우에만 객체를 반환합니다. 활성 창(window)이 아니면 null을 반환하므로 적절하게 설정하세요. 다음은 이벤트를 받으면 다음 작업을 하는 코드 스니펫의 예입니다.

  1. 이벤트가 발생한 뷰의 상위 뷰를 즉시 가져옵니다.
  2. 상위 뷰에서 하위 뷰로 라벨 및 체크박스를 찾습니다.
  3. 라벨과 체크박스를 찾으면 사용자에게 보고할 문자열을 만듭니다. 이 문자열은 라벨 및 라벨이 선택되었는지 여부를 나타냅니다.
  4. 뷰 계층 구조를 순회하는 동안 언제든지 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 fired 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 or not it's complete, based on
    // the text inside the label, and the state of the check-box.
    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 fired 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 or not it's complete, based on
    // the text inside the label, and the state of the check-box.
    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_TOOLTIPACTION_HIDE_TOOLTIP을 사용해 도움말을 표시하거나 숨기도록 View의 인스턴스에 지시할 수 있습니다.

힌트 텍스트

Android 8.0(API 수준 26)에는 텍스트 기반 객체의 힌트 텍스트와 상호작용하는 여러 메서드가 포함되어 있습니다.

  • isShowingHintText()setShowingHintText() 메서드는 각각 노드의 현재 텍스트 콘텐츠가 노드의 힌트 텍스트를 나타내는지 여부를 나타내고 설정합니다.
  • 힌트 텍스트 자체에 액세스하려면 getHintText()를 사용합니다. 객체에서 현재 힌트 텍스트를 표시하지 않는 경우에도 getHintText() 호출이 성공합니다.

화면에 표시되는 텍스트 문자의 위치

Android 8.0(API 수준 26) 이상을 실행하는 기기에서 접근성 서비스는 TextView 위젯 내에 표시되는 각 문자의 경계 상자에 해당하는 화면 좌표를 파악할 수 있습니다. 서비스에서는 refreshWithExtraData()를 호출하고 첫 번째 인수로 EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY를 전달하며 두 번째 인수로 Bundle 객체를 전달하여 이러한 좌표를 찾습니다. 메서드가 실행되면 시스템에서 Rect 객체의 parcelable 배열로 Bundle 인수를 채웁니다. 각 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)
    ...
}

자바

@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