원격으로 터치하는 기능을 만들기 위해서 방법들을 찾아봤는데, 앱 내부에서만 가능한 터치이거나, 루팅을 통해서 터치하는 방법이 많았다. 앱 내부 터치, 루팅 권한을 뺀 나머지 기능을 정리할려고 한다.
ADB 방식
시스템 앱의 경우 adb shell 방식을 사용할 수 있다. adb shell input tap x y 명령어를 내리면 그 좌표로 터치를 할 수 있다.
Runtime runtime = Runtime.getRuntime();
runtime.exec("input tap " + x + " " + y);
단점으로는 속도가 3초, 5초뒤에 발생한다. (테블릿 성능이 안좋아서 그럴 수도 있다) 그리고 플레이 스토어에 올리지 못한다.
그래서 Accessibility Service를 사용해야한다.
Accessibility Service
장애가 있는 사용자를 돕기 위해 나온 서비스이다. manifest에서 설정과 시스템 설정에서 접근성 서비스를 허용하지 않으면 실행이 안된다는 단점이 있다.
AndroidManifest.xml
<service
android:name=".data.GestureDispatchService"
android:exported="false"
android:label="@string/app_name"
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/config_gesture" />
</service>
// @xml/config_gesture
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:canPerformGestures="true" />
xml/config_gesutre는 resource(res) 폴더에서 xml 패키지를 만든 뒤 config_gesture 파일을 만들면 된다.
GestureDispatchService
public class GestureDispatchService extends AccessibilityService {
private void simulateGesture(Integer x1, Integer y1, Integer x2, Integer y2, int duration) {
GestureDescription.Builder gestureBuilder = new GestureDescription.Builder();
if (x2 == null || y2 == null) {
// Tap
Path clickPath = new Path();
clickPath.moveTo(x1, y1);
GestureDescription.StrokeDescription clickStroke =
new GestureDescription.StrokeDescription(clickPath, 0, duration);
gestureBuilder.addStroke(clickStroke);
Log.d("REMOTE", "Simulating a gesture: tap, x1=" + x1 + ", y1=" + y1 + ", duration=" + duration);
} else {
// Swipe
Path clickPath = new Path();
clickPath.moveTo(x1, y1);
clickPath.lineTo(x2, y2);
GestureDescription.StrokeDescription clickStroke =
new GestureDescription.StrokeDescription(clickPath, 0, duration);
gestureBuilder.addStroke(clickStroke);
Log.d("REMOTE", "Simulating a gesture: swipe, x1=" + x1 + ", y1=" + y1 + ", x2=" + x2 + ", y2=" + y2 + ", duration=" + duration);
}
boolean result = dispatchGesture(gestureBuilder.build(), null, null);
Log.d("REMOTE", "Gesture dispatched, result=" + result);
}
@Override
public void onAccessibilityEvent(AccessibilityEvent accessibilityEvent) {
// Not Used
}
@Override
public void onInterrupt() {
// Not Used
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent == null || intent.getAction() == null) {
return Service.START_STICKY;
}
String action = intent.getAction();
if (action.equals("GESTURE")) {
String event = intent.getStringExtra("EVENT");
if (event != null) {
String[] token = event.replace(" ", "").replace("{", "").replace("}", "").split(",");
int x = Integer.parseInt(token[1]);
int y = Integer.parseInt(token[2]);
simulateGesture(x, y, null, null, 103);
}
}
return Service.START_STICKY;
}
}
코드는 생각보다 간단하다. simulateGesture부분을 보면 어떤 식으로 탭을 하는지 swipe하는지 쉽게 확인할 수 있다.
Intent intent = new Intent(context, GestureDispatchService.class);
intent.setAction("GESTURE");
intent.putExtra("EVENT", msg);
context.getApplicationContext().startService(intent);
이 코드는 내가 서비스를 불러올 때 사용한 코드다. 굳이 이 형식을 지킬 필요는 없다.
'안드로이드 > 잡다한 지식' 카테고리의 다른 글
JetPack Compose에서 Compose 멀티 플렛폼으로 전환 (0) | 2024.04.29 |
---|---|
[안드로이드] Rxjava + retrofit2 연결 도중 취소하기 (0) | 2023.12.26 |
[안드로이드] MQTT 라이브러리 정리 (0) | 2023.10.26 |
[안드로이드] DroidKnight 2023 (0) | 2023.08.16 |
[안드로이드] 안드로이드 14 베타 1 알아보기 - 1 (2) | 2023.05.26 |