Foggy day

[Android] 접근성 서비스(AccessibilityService) - Click Event 본문

Android

[Android] 접근성 서비스(AccessibilityService) - Click Event

jinhan38 2024. 3. 24. 14:42

이번 포스팅에서는 Android의 접근성 서비스(AccessibilityService)에 대해 알아보겠습니다. 

 

접근성 서비스는 기본적으로 장애가 있는 사용자를 위한 서비스입니다. 하지만 디바이스의 화면을 컨트롤 하기 위해 많이 사용되곤 합니다. 

Accessibility services should only be used to assist users with disabilities in using Android devices and apps
https://developer.android.com/reference/android/accessibilityservice/AccessibilityService

 

AccessibilityService  |  Android Developers

 

developer.android.com

 

 

1. AccessibilityService 클래스 생성

2. Manifest 추가

3. accessibility_service_config 작성 

4. runtime 권한 획득

5. Click Gesture Event 발생

 

 

 

1. AccessibilityService 클래스 생성

AccessibilityService 클래스를 상속받은 클래스를 생성해주세요. 

코드에서는 3개의 함수를 override 했습니다. 

onAccessibilityEvent 함수는 사용자가 화면을 터치 했을 때 진입하는 함수입니다. 

onStartCommand 함수는 MyAccessibilityService 클래스로 intent를 사용해서 데이터를 전달할 때 호출됩니다.

onInterrupt 함수는 해당 서비스가 종료되면 호출됩니다. 

import android.accessibilityservice.AccessibilityService
import android.accessibilityservice.GestureDescription
import android.content.Intent
import android.graphics.Path
import android.view.accessibility.AccessibilityEvent

class MyAccessibilityService : AccessibilityService() {

    // 화면 터치 이벤트 발생 시 호출됨
    override fun onAccessibilityEvent(event: AccessibilityEvent?) {
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
    // 뒤에서 구현 예정
        return super.onStartCommand(intent, flags, startId)
    }

    // 서비스 중단 시 호출
    override fun onInterrupt() {
    }
}

 

 

 

2. Manifest 추가

Manifest에 service 태그를 추가해주세요. 

따로 추가할 uses-permission은 없습니다. 

meta-data의 resource에 accessibility_service_config를 사용해야 합니다.

<service
    android:name=".MyAccessibilityService"
    android:exported="true"
    android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
    <intent-filter>
        <action android:name="android.accessibilityservice.AccessibilityService" />
    </intent-filter>
    <meta-data
        android:name="android.accessibilityservice"
        android:resource="@xml/accessibility_service_config" />
</service>

 

 

 

3. accessibility_service_config 작성

res/xml/ 경로에 accessibility_service_config.xml 파일을 만들겠습니다. 

아래 특성 중 canPerformGestures 값을 true로 설정해야 Gesture 이벤트를 발생시킬 수 있습니다. 

<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
    android:accessibilityEventTypes="typeAllMask"
    android:accessibilityFeedbackType="feedbackAllMask"
    android:canPerformGestures="true"
    android:canRetrieveWindowContent="true"
    android:description="@string/accessibility_service_description" />

 

 

 

 

4. runtime 권한 획득

runtime에서 접근성 권한을 획득해야 합니다. 

먼저 접근성 권한이 있는지 체크해줍니다. 

/**
 * 접근성 권한 있는지 체크 
 */
private fun isAccessibilityServiceEnabled(context: Context, service: Class<out AccessibilityService>): Boolean {
    val expectedComponentName = ComponentName(context, service)

    val enabledServicesSetting = Settings.Secure.getString(
        context.contentResolver,
        Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES
    ) ?: return false

    val colonSplitter = TextUtils.SimpleStringSplitter(':')
    colonSplitter.setString(enabledServicesSetting)

    while (colonSplitter.hasNext()) {
        val componentNameString = colonSplitter.next()
        val enabledComponentName = ComponentName.unflattenFromString(componentNameString)
        if (enabledComponentName != null && enabledComponentName == expectedComponentName)
            return true
    }

    return false
}

 

권한이 없을 경우 접근성 권한을 활성화 시킬 수 있는 설정 화면으로 이동시켜줘야 합니다.

 startActivity(Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS))

 

 

 

5. Click Gesture Event 발생(접근성 권한 승인 이후 진행)

코드로 Click 이벤트를 발생시켜보겠습니다.

먼저 MyAccessibilityService를 수정하겠습니다. 앞선 코드와는 다르게 onStartCommand에서 intent의 데이터를 받고 clickScreen 함수를 호출하고 있습니다.

import android.accessibilityservice.AccessibilityService
import android.accessibilityservice.GestureDescription
import android.content.Intent
import android.graphics.Path
import android.view.accessibility.AccessibilityEvent

class MyAccessibilityService : AccessibilityService() {

    // 화면 터치 이벤트 발생 시 호출됨
    override fun onAccessibilityEvent(event: AccessibilityEvent?) {
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        if (intent?.action == "touch") {
            val x = intent.getIntExtra("x", 0)
            val y = intent.getIntExtra("y", 0)
            clickScreen(x.toFloat(), y.toFloat())
        }
        return super.onStartCommand(intent, flags, startId)
    }

    /**
     * 화면 클릭
     */
    private fun clickScreen(x: Float, y: Float, startTime: Long = 100, duration: Long = 100) {
        val gestureBuilder = GestureDescription.Builder()
        val path = Path()
        path.moveTo(x, y)
        gestureBuilder.addStroke(GestureDescription.StrokeDescription(path, startTime, duration))
        val gesture = gestureBuilder.build()
        dispatchGesture(gesture, object : GestureResultCallback() {
            override fun onCompleted(gestureDescription: GestureDescription?) {
                super.onCompleted(gestureDescription)
                // 화면 터치 제스처 실행 완료
            }

            override fun onCancelled(gestureDescription: GestureDescription?) {
                super.onCancelled(gestureDescription)
                // 화면 터치 제스처 실행 취소
            }
        }, null)
    }

    // 서비스 중단 시 호출
    override fun onInterrupt() {
    }
}

 

 

onStartCommand 함수에 데이터를 전달하기 위해서는 intent를 만들어서 startService를 호출해야 합니다.

action을 touch로 세팅한 후 x, y 값을 추가해줬습니다. 

val intentTouch = Intent(this, MyAccessibilityService::class.java)
intentTouch.action = "touch"
intentTouch.putExtra("x", 100)
intentTouch.putExtra("y", 100)
startService(intentTouch)