From 8783f953071f25db5950d587c9a84f47db2650ad Mon Sep 17 00:00:00 2001 From: TongTongStudio Date: Mon, 27 Jul 2026 08:51:26 +0800 Subject: [PATCH] =?UTF-8?q?feat(service):=20=E6=B7=BB=E5=8A=A0=E5=86=85?= =?UTF-8?q?=E5=AE=B9=E6=8D=95=E8=8E=B7=E4=B8=8E=E8=AF=AD=E9=9F=B3=E4=BA=A4?= =?UTF-8?q?=E4=BA=92=E6=9C=8D=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 34 ++++++++++++++ app/src/main/AndroidManifest.xml | 31 ++++++++++++ .../service/MyContentCaptureService.java | 42 +++++++++++++++++ .../service/MyVoiceInteractionService.java | 15 ++++++ .../service/MyVoiceInteractionSession.java | 47 +++++++++++++++++++ .../MyVoiceInteractionSessionService.java | 17 +++++++ .../res/xml/voice_interaction_service.xml | 7 +++ 7 files changed, 193 insertions(+) create mode 100644 README.md create mode 100644 app/src/main/java/com/ttstd/dialer/service/MyContentCaptureService.java create mode 100644 app/src/main/java/com/ttstd/dialer/service/MyVoiceInteractionService.java create mode 100644 app/src/main/java/com/ttstd/dialer/service/MyVoiceInteractionSession.java create mode 100644 app/src/main/java/com/ttstd/dialer/service/MyVoiceInteractionSessionService.java create mode 100644 app/src/main/res/xml/voice_interaction_service.xml diff --git a/README.md b/README.md new file mode 100644 index 0000000..4ddf9e4 --- /dev/null +++ b/README.md @@ -0,0 +1,34 @@ +启用 Content Capture: +Shell Script +settings put secure content_capture_service com.ttstd.dialer/com.ttstd.dialer.service.MyContentCaptureService + +启用 系统助手 (Assist): +Shell Script +settings put secure assistant com.ttstd.dialer/com.ttstd.dialer.service.MyVoiceInteractionService +手动触发 Assist 采集: 在你的 Activity 或 Service 中,可以通过 showAssist(new Bundle()) 主动触发 onHandleAssist 回调,从而瞬间抓取屏幕信息。 + +对于拥有系统签名的应用,可以通过 adb 或在代码中通过 settings 命令静默开启无障碍服务。 +# 1. 使用 ADB 命令手动开启 + 在电脑终端输入以下两条命令: + +## 1. 将服务加入已启用列表 +adb shell settings put secure enabled_accessibility_services com.ttstd.dialer/com.ttstd.dialer.accessibility.DialerAccessibilityService + +## 2. 开启无障碍服务总开关 +adb shell settings put secure accessibility_enabled 1 + +# 2. 在代码中自动开启 (利用系统签名) + 既然你的应用有系统签名,你可以直接通过你现有的 CmdUtil 在程序启动时自动激活自己,无需用户操作: + + Java + public void autoEnableAccessibility(Context context) { + String serviceName = context.getPackageName() + "/com.ttstd.dialer.accessibility.DialerAccessibilityService"; + + // 1. 设置启用服务(如果已有其他服务,建议先获取当前值再追加,这里演示直接覆盖) + CmdUtil.execute("settings put secure enabled_accessibility_services " + serviceName); + + // 2. 开启总开关 + CmdUtil.execute("settings put secure accessibility_enabled 1"); + + Logger.d("Accessibility", "无障碍服务已尝试静默开启"); + } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 264cf34..6294571 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -31,6 +31,8 @@ + + @@ -156,6 +158,35 @@ android:resource="@xml/accessibility_service_config" /> + + + + + + + + + + + + + + + + + diff --git a/app/src/main/java/com/ttstd/dialer/service/MyContentCaptureService.java b/app/src/main/java/com/ttstd/dialer/service/MyContentCaptureService.java new file mode 100644 index 0000000..b80bc1f --- /dev/null +++ b/app/src/main/java/com/ttstd/dialer/service/MyContentCaptureService.java @@ -0,0 +1,42 @@ +package com.ttstd.dialer.service; + +import android.os.Build; +import android.service.contentcapture.ContentCaptureService; +import android.view.contentcapture.ContentCaptureEvent; +import android.view.contentcapture.ContentCaptureSessionId; + +import androidx.annotation.NonNull; +import androidx.annotation.RequiresApi; + +import com.ttstd.dialer.utils.Logger; + +@RequiresApi(api = Build.VERSION_CODES.Q) +public class MyContentCaptureService extends ContentCaptureService { + private static final String TAG = "MyContentCaptureService"; + + @Override + public void onConnected() { + super.onConnected(); + Logger.d(TAG, "onConnected"); + } + + @Override + public void onDisconnected() { + super.onDisconnected(); + Logger.d(TAG, "onDisconnected"); + } + + @Override + public void onContentCaptureEvent(@NonNull ContentCaptureSessionId sessionId, @NonNull ContentCaptureEvent event) { + // 这是核心方法,所有的界面变化都会在这里回调 + Logger.v(TAG, "onContentCaptureEvent: type=" + event.getType()); + + if (event.getType() == ContentCaptureEvent.TYPE_VIEW_APPEARED) { + if (event.getViewNode() != null) { + CharSequence text = event.getViewNode().getText(); + String id = event.getViewNode().getIdEntry(); + Logger.d(TAG, "View Appeared: text=" + text + ", id=" + id); + } + } + } +} diff --git a/app/src/main/java/com/ttstd/dialer/service/MyVoiceInteractionService.java b/app/src/main/java/com/ttstd/dialer/service/MyVoiceInteractionService.java new file mode 100644 index 0000000..b467813 --- /dev/null +++ b/app/src/main/java/com/ttstd/dialer/service/MyVoiceInteractionService.java @@ -0,0 +1,15 @@ +package com.ttstd.dialer.service; + +import android.service.voice.VoiceInteractionService; + +import com.ttstd.dialer.utils.Logger; + +public class MyVoiceInteractionService extends VoiceInteractionService { + private static final String TAG = "MyVoiceInteractionService"; + + @Override + public void onCreate() { + super.onCreate(); + Logger.d(TAG, "onCreate"); + } +} diff --git a/app/src/main/java/com/ttstd/dialer/service/MyVoiceInteractionSession.java b/app/src/main/java/com/ttstd/dialer/service/MyVoiceInteractionSession.java new file mode 100644 index 0000000..841f85b --- /dev/null +++ b/app/src/main/java/com/ttstd/dialer/service/MyVoiceInteractionSession.java @@ -0,0 +1,47 @@ +package com.ttstd.dialer.service; + +import android.app.assist.AssistContent; +import android.app.assist.AssistStructure; +import android.content.Context; +import android.os.Bundle; +import android.service.voice.VoiceInteractionSession; +import android.view.View; + +import com.ttstd.dialer.utils.Logger; + +public class MyVoiceInteractionSession extends VoiceInteractionSession { + private static final String TAG = "MyVoiceInteractionSession"; + + public MyVoiceInteractionSession(Context context) { + super(context); + } + + @Override + public void onHandleAssist(Bundle data, AssistStructure structure, AssistContent content) { + super.onHandleAssist(data, structure, content); + // 这是核心方法,structure 包含了整个屏幕的 View 树 + Logger.d(TAG, "onHandleAssist"); + if (structure != null) { + AssistStructure.WindowNode windowNode = structure.getWindowNodeAt(0); + if (windowNode != null) { + traverseNode(windowNode.getRootViewNode(), ""); + } + } + } + + private void traverseNode(AssistStructure.ViewNode node, String prefix) { + if (node == null) return; + + Logger.d(TAG, prefix + "View: text=" + node.getText() + ", id=" + node.getIdEntry()); + + for (int i = 0; i < node.getChildCount(); i++) { + traverseNode(node.getChildAt(i), prefix + " "); + } + } + + @Override + public View onCreateContentView() { + // 可以返回一个自定义布局,用于在屏幕上显示助手界面 + return super.onCreateContentView(); + } +} diff --git a/app/src/main/java/com/ttstd/dialer/service/MyVoiceInteractionSessionService.java b/app/src/main/java/com/ttstd/dialer/service/MyVoiceInteractionSessionService.java new file mode 100644 index 0000000..2dae993 --- /dev/null +++ b/app/src/main/java/com/ttstd/dialer/service/MyVoiceInteractionSessionService.java @@ -0,0 +1,17 @@ +package com.ttstd.dialer.service; + +import android.os.Bundle; +import android.service.voice.VoiceInteractionSession; +import android.service.voice.VoiceInteractionSessionService; + +import com.ttstd.dialer.utils.Logger; + +public class MyVoiceInteractionSessionService extends VoiceInteractionSessionService { + private static final String TAG = "MyVoiceInteractionSessionService"; + + @Override + public VoiceInteractionSession onNewSession(Bundle args) { + Logger.d(TAG, "onNewSession"); + return new MyVoiceInteractionSession(this); + } +} diff --git a/app/src/main/res/xml/voice_interaction_service.xml b/app/src/main/res/xml/voice_interaction_service.xml new file mode 100644 index 0000000..1394e15 --- /dev/null +++ b/app/src/main/res/xml/voice_interaction_service.xml @@ -0,0 +1,7 @@ + +