feat(service): 添加内容捕获与语音交互服务
This commit is contained in:
34
README.md
Normal file
34
README.md
Normal file
@@ -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", "无障碍服务已尝试静默开启");
|
||||
}
|
||||
@@ -31,6 +31,8 @@
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
|
||||
<uses-permission android:name="android.permission.BIND_CONTENT_CAPTURE_SERVICE" />
|
||||
<uses-permission android:name="android.permission.BIND_VOICE_INTERACTION" />
|
||||
|
||||
|
||||
<!--baidumap start-->
|
||||
@@ -156,6 +158,35 @@
|
||||
android:resource="@xml/accessibility_service_config" />
|
||||
</service>
|
||||
|
||||
<!-- Content Capture Service -->
|
||||
<service
|
||||
android:name=".service.MyContentCaptureService"
|
||||
android:exported="true"
|
||||
android:permission="android.permission.BIND_CONTENT_CAPTURE_SERVICE">
|
||||
<intent-filter>
|
||||
<action android:name="android.service.contentcapture.ContentCaptureService" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<!-- Assist API / Voice Interaction Service -->
|
||||
<service
|
||||
android:name=".service.MyVoiceInteractionService"
|
||||
android:exported="true"
|
||||
android:label="拨号助手辅助"
|
||||
android:permission="android.permission.BIND_VOICE_INTERACTION">
|
||||
<meta-data
|
||||
android:name="android.voice_interaction"
|
||||
android:resource="@xml/voice_interaction_service" />
|
||||
<intent-filter>
|
||||
<action android:name="android.service.voice.VoiceInteractionService" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<service
|
||||
android:name=".service.MyVoiceInteractionSessionService"
|
||||
android:exported="true"
|
||||
android:permission="android.permission.BIND_VOICE_INTERACTION" />
|
||||
|
||||
<!-- <receiver-->
|
||||
<!-- android:name=".receiver.AppChangedReceiver"-->
|
||||
<!-- android:enabled="true"-->
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
7
app/src/main/res/xml/voice_interaction_service.xml
Normal file
7
app/src/main/res/xml/voice_interaction_service.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<voice-interaction-service xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:sessionService="com.ttstd.dialer.service.MyVoiceInteractionSessionService"
|
||||
android:recognitionService="com.ttstd.dialer.service.MyVoiceInteractionService"
|
||||
android:supportsAssist="true"
|
||||
android:supportsLaunchFromKeyguard="true"
|
||||
android:supportsLocalInteraction="true" />
|
||||
Reference in New Issue
Block a user