1. 소개
접근성 서비스는 Android 기기에 설치된 애플리케이션을 대신하여 사용자에게 대체 탐색 피드백을 제공하도록 설계된 Android 프레임워크의 기능입니다. 접근성 서비스는 사용자가 화면의 중요한 영역을 마우스로 가져갈 때 텍스트를 음성으로 변환하거나 햅틱 피드백을 제공하는 등 애플리케이션을 대신하여 사용자에게 전달할 수 있습니다. 이 Codelab에서는 매우 간단한 접근성 서비스를 만드는 방법을 보여줍니다.
접근성 서비스란 무엇인가요?
접근성 서비스는 장애가 있는 사용자가 Android 기기와 앱을 사용할 수 있도록 지원합니다. 화면에서 사용자가 정보를 처리하고 기기와 의미 있게 상호작용할 수 있도록 지원하는 장기 실행 권한 서비스입니다.
일반적인 접근성 서비스의 예
- 스위치 제어: 거동이 불편한 Android 사용자가 하나 이상의 스위치를 사용하여 기기와 상호작용할 수 있습니다.
- 음성 액세스 (베타): 거동이 불편한 Android 사용자가 음성 명령으로 기기를 제어할 수 있습니다.
- TalkBack: 시각 장애가 있는 사용자나 시각장애인이 일반적으로 사용하는 스크린 리더입니다.
접근성 서비스 빌드
Google은 Android 사용자를 위해 스위치 제어, 음성 액세스, TalkBack과 같은 서비스를 제공하지만 이러한 서비스가 장애가 있는 모든 사용자를 지원할 수는 없습니다. 장애가 있는 많은 사용자는 고유한 요구사항이 있으므로 접근성 서비스를 만들기 위한 Android API는 개방되어 있으며 개발자는 접근성 서비스를 만들어 Play 스토어를 통해 자유롭게 배포할 수 있습니다.
빌드할 항목
이 Codelab에서는 접근성 API를 사용하여 유용한 작업을 몇 가지 실행하는 간단한 서비스를 개발합니다. 기본 Android 앱을 작성할 수 있다면 유사한 서비스를 개발할 수 있습니다.
접근성 API는 강력합니다. 빌드할 서비스의 코드는 파일 4개에만 포함되어 있으며 코드 줄 수는 약 200개입니다.
최종 사용자
다음과 같은 특징을 가진 가상 사용자를 위한 서비스를 빌드합니다.
- 사용자가 기기의 측면 버튼에 도달하기 어려워합니다.
- 사용자가 스크롤하거나 스와이프하는 데 어려움을 겪습니다.
서비스 세부정보
서비스는 화면에 전역 작업 표시줄을 오버레이합니다. 사용자는 이 표시줄의 버튼을 터치하여 다음 작업을 실행할 수 있습니다.
- 휴대전화 측면에 있는 실제 전원 버튼을 누르지 않고 기기를 끕니다.
- 휴대전화 측면의 볼륨 버튼을 터치하지 않고 볼륨을 조정합니다.
- 실제로 스크롤하지 않고 스크롤 작업을 실행합니다.
- 스와이프 동작을 사용하지 않고 스와이프를 실행합니다.
필요한 항목
이 Codelab에서는 다음을 사용하는 것으로 가정합니다.
- Android 스튜디오를 실행하는 컴퓨터
- 간단한 셸 명령어를 실행하는 터미널
- 개발에 사용할 컴퓨터에 연결된 Android 7.0 (Nougat)을 실행하는 기기
지금 시작해 보세요.
2. 설정
터미널을 사용하여 작업할 디렉터리를 만듭니다. 이 디렉터리로 변경합니다.
코드 다운로드
이 Codelab의 코드가 포함된 저장소를 클론할 수 있습니다.
git clone https://github.com/android/codelab-android-accessibility.git
저장소에는 여러 Android 스튜디오 프로젝트가 포함되어 있습니다. Android 스튜디오를 사용하여 GlobalActionBarService를 엽니다.
스튜디오 아이콘을 클릭하여 Android 스튜디오를 실행합니다.

Import Project (Eclipse ADT, Gradle, etc.) 옵션을 선택합니다.

소스를 클론한 위치로 이동하여 GlobalActionBarService를 선택합니다.
그런 다음 터미널을 사용하여 루트 디렉터리로 변경합니다.
3. 시작 코드 이해
연 프로젝트를 살펴봅니다.
접근성 서비스의 기본 스켈레톤은 이미 만들어져 있습니다. 이 Codelab에서 작성할 모든 코드는 다음 네 파일로 제한됩니다.
- app/src/main/AndroidManifest.xml
- app/src/main/res/layout/action_bar.xml
- app/src/main/res/xml/global_action_bar_service.xml
- app/src/main/java/com/example/android/globalactionbarservice/GlobalActionBarService.java
다음은 각 파일의 콘텐츠를 둘러보는 안내입니다.
AndroidManifest.xml
접근성 서비스에 관한 정보는 매니페스트에 선언됩니다.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.android.globalactionbarservice">
<application>
<service
android:name=".GlobalActionBarService"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
android:exported="true">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
<meta-data
android:name="android.accessibilityservice"
android:resource="@xml/global_action_bar_service" />
</service>
</application>
</manifest>
다음 세 가지 필수 항목은 AndroidManifest.xml에 선언되어 있습니다.
- 접근성 서비스에 바인딩할 수 있는 권한:
<service
...
android:permission = "android.permission.BIND_ACCESSIBILITY_SERVICE">
...
</service>
- AccessibilityService 인텐트:
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
- 생성 중인 서비스의 메타데이터가 포함된 파일의 위치:
<meta-data
...
android:resource="@xml/global_action_bar_service" />
</service>
global_action_bar_service.xml
이 파일에는 서비스의 메타데이터가 포함되어 있습니다.
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityFeedbackType="feedbackGeneric"
android:accessibilityFlags="flagDefault"
android:canPerformGestures="true"
android:canRetrieveWindowContent="true" />
<accessibility-service> 요소를 사용하여 다음 메타데이터가 정의되었습니다.
- 이 서비스의 피드백 유형입니다 (이 Codelab에서는 기본값으로 적합한 feedbackGeneric을 사용함).
- 서비스의 접근성 플래그입니다 (이 Codelab에서는 기본 플래그를 사용함).
- 서비스에 필요한 기능:
- 스와이프를 실행하기 위해 android:canPerformGestures가 true로 설정됩니다.
- 창 콘텐츠를 가져오기 위해 android:canRetrieveWindowContent가 true로 설정됩니다.
GlobalActionBarService.java
접근성 서비스의 대부분 코드는 GlobalActionBarService.java에 있습니다. 처음에 파일에는 접근성 서비스의 최소한의 코드가 포함됩니다.
- AccessibilityService를 확장하는 클래스
- 필수 재정의 메서드 몇 개 (이 Codelab에서는 비워 둠)
public class GlobalActionBarService extends AccessibilityService {
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
}
@Override
public void onInterrupt() {
}
}
Codelab 중에 이 파일에 코드를 추가할 것입니다.
action_bar.xml
서비스는 버튼 4개가 있는 UI를 노출하고 action_bar.xml 레이아웃 파일에는 이러한 버튼을 표시하는 마크업이 포함되어 있습니다.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</LinearLayout>
이 파일에는 현재 빈 LinearLayout이 포함되어 있습니다. Codelab 중에 버튼의 마크업을 추가합니다.
애플리케이션 실행
기기가 컴퓨터에 연결되어 있는지 확인합니다. 화면 상단의 메뉴 바에서 녹색의 재생 아이콘
을 누릅니다. 그러면 작업 중인 앱이 실행됩니다.
설정 > 접근성으로 이동합니다. 전역 작업 표시줄 서비스가 기기에 설치되어 있습니다.

Global Action Bar Service를 클릭하고 사용 설정합니다. 다음 권한 대화상자가 표시됩니다.

접근성 서비스는 사용자 작업을 관찰하고, 창 콘텐츠를 가져오고, 사용자를 대신하여 동작을 실행할 수 있는 권한을 요청합니다. 서드 파티 접근성 서비스를 사용하는 경우 출처를 신뢰할 수 있는지 확인하세요.
아직 기능을 추가하지 않았으므로 서비스를 실행해도 별다른 작업이 실행되지 않습니다. 그럼 시작해 보겠습니다.
4. 버튼 만들기
res/layout에서 action_bar.xml을 엽니다. 현재 비어 있는 LinearLayout 내에 마크업을 추가합니다.
<LinearLayout ...>
<Button
android:id="@+id/power"
android:text="@string/power"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<Button
android:id="@+id/volume_up"
android:text="@string/volume"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<Button
android:id="@+id/scroll"
android:text="@string/scroll"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<Button
android:id="@+id/swipe"
android:text="@string/swipe"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
이렇게 하면 사용자가 기기에서 작업을 트리거하기 위해 누르는 버튼이 생성됩니다.
GlobalActionBarService.java를 열고 작업 모음의 레이아웃을 저장하는 변수를 추가합니다.
public class GlobalActionBarService extends AccessibilityService {
FrameLayout mLayout;
...
}
이제 onServiceStarted() 메서드를 추가합니다.
public class GlobalActionBarService extends AccessibilityService {
FrameLayout mLayout;
@Override
protected void onServiceConnected() {
// Create an overlay and display the action bar
WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
mLayout = new FrameLayout(this);
WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
lp.type = WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
lp.format = PixelFormat.TRANSLUCENT;
lp.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
lp.width = WindowManager.LayoutParams.WRAP_CONTENT;
lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
lp.gravity = Gravity.TOP;
LayoutInflater inflater = LayoutInflater.from(this);
inflater.inflate(R.layout.action_bar, mLayout);
wm.addView(mLayout, lp);
}
}
이 코드는 레이아웃을 확장하고 화면 상단에 작업 모음을 추가합니다.
서비스가 연결되면 onServiceConnected() 메서드가 실행됩니다. 이제 접근성 서비스가 작동하는 데 필요한 모든 권한이 있습니다. 여기에서 사용할 키 권한은 WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY 권한입니다. 이 권한을 사용하면 복잡한 권한 흐름을 거치지 않고 기존 콘텐츠 위의 화면에 직접 그릴 수 있습니다.
접근성 서비스 수명 주기
접근성 서비스의 수명 주기는 시스템에서만 관리하며 설정된 서비스 수명 주기를 따릅니다.
- 접근성 서비스는 사용자가 기기 설정에서 서비스를 명시적으로 사용 설정할 때 시작됩니다.
- 시스템이 서비스에 바인드된 후 onServiceConnected()를 호출합니다. 이 메서드는 바인드 후 설정을 실행하려는 서비스에 의해 재정의될 수 있습니다.
- 접근성 서비스는 사용자가 기기 설정에서 사용 중지하거나 disableSelf()를 호출할 때 중지됩니다.
서비스 실행
Android 스튜디오를 사용하여 서비스를 실행하려면 실행 설정이 올바르게 구성되어 있어야 합니다.
실행 구성을 수정합니다 (상단 메뉴에서 실행을 사용하고 구성 수정으로 이동). 그런 다음 드롭다운을 사용하여 실행 옵션을 '기본 활동'에서 '없음'으로 변경합니다.

이제 Android 스튜디오를 사용하여 서비스를 실행할 수 있습니다.
화면 상단의 메뉴 바에서 녹색의 재생 아이콘
을 누릅니다. 그런 다음 설정 > 접근성으로 이동하여 전역 작업 표시줄 서비스를 사용 설정합니다.
화면에 표시된 콘텐츠 위에 서비스 UI를 구성하는 4개의 버튼이 오버레이되어 표시됩니다.

이제 사용자가 유용한 작업을 실행하기 위해 터치할 수 있도록 네 개의 버튼에 기능을 추가합니다.
5. 전원 버튼 구성
configurePowerButton()에 configurePowerButton() 메서드를 추가합니다.
private void configurePowerButton() {
Button powerButton = (Button) mLayout.findViewById(R.id.power);
powerButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
performGlobalAction(GLOBAL_ACTION_POWER_DIALOG);
}
});
}
전원 버튼 메뉴에 액세스하기 위해 configurePowerButton()은 AccessibilityService에서 제공하는 performGlobalAction() 메서드를 사용합니다. 방금 추가한 코드는 간단합니다. 버튼을 클릭하면 onClickListener()가 트리거됩니다. 그러면 performGlobalAction(GLOBAL_ACTION_POWER_DIALOG)가 호출되고 사용자에게 전원 대화상자가 표시됩니다.
전역 작업은 어떤 뷰에도 연결되지 않습니다. 뒤로 버튼, 홈 버튼, 최근 버튼을 누르는 것도 전역 작업의 예입니다.
이제 onServiceConnected() 메서드의 끝에 configurePowerButton()을 추가합니다.
@Override
protected void onServiceConnected() {
...
configurePowerButton();
}
화면 상단의 메뉴 바에서 녹색의 재생 아이콘
을 누릅니다. 그런 다음 설정 > 접근성으로 이동하여 전역 작업 표시줄 서비스를 시작합니다.
전원 버튼을 눌러 전원 대화상자를 표시합니다.
6. 볼륨 버튼 구성
configureVolumeButton()에 configureVolumeButton() 메서드를 추가합니다.
private void configureVolumeButton() {
Button volumeUpButton = (Button) mLayout.findViewById(R.id.volume_up);
volumeUpButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
AudioManager audioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC,
AudioManager.ADJUST_RAISE, AudioManager.FLAG_SHOW_UI);
}
});
}
configureVolumeButton() 메서드는 사용자가 볼륨 버튼을 누를 때 트리거되는 onClickListener()를 추가합니다. 이 리스너 내에서 configureVolumeButton()은 AudioManager를 사용하여 스트림 볼륨을 조정합니다.
누구나 볼륨을 제어할 수 있습니다 (접근성 서비스가 아니어도 됨).
이제 onServiceConnected() 메서드의 끝에 configureVolumeButton()을 추가합니다.
@Override
protected void onServiceConnected() {
...
configureVolumeButton();
}
화면 상단의 메뉴 바에서 녹색의 재생 아이콘
을 누릅니다. 그런 다음 설정 > 접근성으로 이동하여 전역 작업 표시줄 서비스를 시작합니다.
볼륨 버튼을 눌러 볼륨을 변경합니다.
기기 측면의 볼륨 컨트롤에 도달할 수 없는 가상의 사용자는 이제 전역 작업 표시줄 서비스를 사용하여 볼륨을 변경 (증가)할 수 있습니다.
7. 스크롤 버튼 구성
이 섹션에서는 두 가지 메서드를 코딩합니다. 첫 번째 메서드는 스크롤 가능한 노드를 찾고 두 번째 메서드는 사용자를 대신하여 스크롤 작업을 실행합니다.
findScrollableNode에 findScrollableNode 메서드를 추가합니다.
private AccessibilityNodeInfo findScrollableNode(AccessibilityNodeInfo root) {
Deque<AccessibilityNodeInfo> deque = new ArrayDeque<>();
deque.add(root);
while (!deque.isEmpty()) {
AccessibilityNodeInfo node = deque.removeFirst();
if (node.getActionList().contains(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD)) {
return node;
}
for (int i = 0; i < node.getChildCount(); i++) {
deque.addLast(node.getChild(i));
}
}
return null;
}
접근성 서비스는 화면의 실제 뷰에 액세스할 수 없습니다. 대신 AccessibilityNodeInfo 객체로 구성된 트리 형태로 화면에 표시된 항목의 반영을 확인합니다. 이러한 객체에는 뷰가 나타내는 정보 (뷰의 위치, 뷰와 연결된 텍스트, 접근성을 위해 추가된 메타데이터, 뷰에서 지원하는 작업 등)가 포함됩니다. findScrollableNode() 메서드는 루트 노드에서 시작하여 이 트리를 너비 우선으로 순회합니다. 스크롤 가능한 노드 (즉, AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD 작업을 지원하는 노드)를 찾으면 이를 반환하고, 그렇지 않으면 null을 반환합니다.)
이제 configureScrollButton()에 configureScrollButton() 메서드를 추가합니다.
private void configureScrollButton() {
Button scrollButton = (Button) mLayout.findViewById(R.id.scroll);
scrollButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
AccessibilityNodeInfo scrollable = findScrollableNode(getRootInActiveWindow());
if (scrollable != null) {
scrollable.performAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD.getId());
}
}
});
}
이 메서드는 스크롤 버튼을 클릭할 때 실행되는 onClickListener()를 만듭니다. 스크롤 가능한 노드를 찾으려고 시도하고 성공하면 스크롤 작업을 실행합니다.
이제 onServiceConnected()에 configureScrollButton()을 추가합니다.
@Override
protected void onServiceConnected() {
...
configureScrollButton();
}
화면 상단의 메뉴 바에서 녹색의 재생 아이콘
을 누릅니다. 그런 다음 설정 > 접근성으로 이동하여 전역 작업 표시줄 서비스를 시작합니다.
뒤로 버튼을 눌러 설정 > 접근성으로 이동합니다. 접근성 설정 활동의 항목은 스크롤할 수 있으며 스크롤 버튼을 터치하면 스크롤 작업이 실행됩니다. 이제 스크롤 작업을 쉽게 실행할 수 없는 가상의 사용자가 스크롤 버튼을 사용하여 항목 목록을 스크롤할 수 있습니다.
8. 스와이프 버튼 구성
configureSwipeButton()에 configureSwipeButton() 메서드를 추가합니다.
private void configureSwipeButton() {
Button swipeButton = (Button) mLayout.findViewById(R.id.swipe);
swipeButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Path swipePath = new Path();
swipePath.moveTo(1000, 1000);
swipePath.lineTo(100, 1000);
GestureDescription.Builder gestureBuilder = new GestureDescription.Builder();
gestureBuilder.addStroke(new GestureDescription.StrokeDescription(swipePath, 0, 500));
dispatchGesture(gestureBuilder.build(), null, null);
}
});
}
configureSwipeButton() 메서드는 사용자를 대신하여 동작을 실행하는 N에 추가된 새 API를 사용합니다. 이 코드에서는 GestureDescription 객체를 사용하여 실행할 동작의 경로를 지정하고 (이 Codelab에서는 하드코딩된 값을 사용함) AccessibilityService dispatchGesture() 메서드를 사용하여 사용자를 대신하여 스와이프 동작을 디스패치합니다.
이제 onServiceConnected()에 configureSwipeButton()을 추가합니다.
@Override
protected void onServiceConnected() {
...
configureSwipeButton();
}
화면 상단의 메뉴 바에서 녹색의 재생 아이콘
을 누릅니다. 그런 다음 설정 > 접근성으로 이동하여 전역 작업 표시줄 서비스를 시작합니다.
스와이프 기능을 테스트하는 가장 쉬운 방법은 휴대전화에 설치된 지도 애플리케이션을 여는 것입니다. 지도가 로드되면 스와이프 버튼을 터치하면 화면이 오른쪽으로 스와이프됩니다.
9. 요약
축하합니다. 간단하고 기능적인 접근성 서비스를 빌드했습니다.
다양한 방법으로 이 서비스를 확장할 수 있습니다. 예를 들면 다음과 같습니다.
- 작업 모음을 이동 가능하게 만듭니다 (현재는 화면 상단에만 표시됨).
- 사용자가 볼륨을 높이거나 낮출 수 있도록 허용합니다.
- 사용자가 왼쪽과 오른쪽으로 모두 스와이프할 수 있도록 허용합니다.
- 작업 표시줄이 응답할 수 있는 추가 동작 지원
이 Codelab에서는 접근성 API에서 제공하는 기능의 일부만 다룹니다. API는 다음도 다룹니다 (부분 목록).
- 여러 창 지원
- AccessibilityEvent 지원 UI가 변경되면 접근성 서비스는 AccessibilityEvent 객체를 사용하여 이러한 변경사항에 관한 알림을 받습니다. 그러면 서비스는 UI 변경에 적절하게 응답할 수 있습니다.
- 배율을 제어할 수 있습니다.
이 Codelab에서는 접근성 서비스 작성을 시작합니다. 해결하고 싶은 특정 접근성 문제가 있는 사용자를 알고 있다면 이제 해당 사용자를 지원하는 서비스를 구축할 수 있습니다.