diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 8a92b45..bf376cf 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -195,6 +195,9 @@
+
diff --git a/app/src/main/java/com/ttstd/dialer/service/MyVoiceInteractionService.java b/app/src/main/java/com/ttstd/dialer/service/MyVoiceInteractionService.java
index b467813..e9930e9 100644
--- a/app/src/main/java/com/ttstd/dialer/service/MyVoiceInteractionService.java
+++ b/app/src/main/java/com/ttstd/dialer/service/MyVoiceInteractionService.java
@@ -1,15 +1,61 @@
package com.ttstd.dialer.service;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.os.Bundle;
import android.service.voice.VoiceInteractionService;
+import android.service.voice.VoiceInteractionSession;
import com.ttstd.dialer.utils.Logger;
+/**
+ * 语音交互服务类,作为语音助手的入口。
+ */
public class MyVoiceInteractionService extends VoiceInteractionService {
private static final String TAG = "MyVoiceInteractionService";
+ /**
+ * 自定义 Action,用于从外部启动语音交互会话
+ */
+ public static final String ACTION_SHOW_SESSION = "com.ttstd.dialer.ACTION_SHOW_SESSION";
+
@Override
public void onCreate() {
super.onCreate();
Logger.d(TAG, "onCreate");
}
+
+ @Override
+ public void onReady() {
+ super.onReady();
+ Logger.d(TAG, "onReady");
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ Logger.d(TAG, "onStartCommand: " + intent);
+ if (intent != null) {
+ String action = intent.getAction();
+ if (ACTION_SHOW_SESSION.equals(action)) {
+ // 确保服务是当前活跃的语音交互服务
+ if (isActiveService(this, new ComponentName(this, MyVoiceInteractionService.class))) {
+ Logger.d(TAG, "Showing session with assist and screenshot flags");
+ // 核心:调用 showSession 并请求辅助数据(AssistStructure)和截图
+ Bundle args = new Bundle();
+ showSession(args,
+ VoiceInteractionSession.SHOW_WITH_ASSIST |
+ VoiceInteractionSession.SHOW_WITH_SCREENSHOT);
+ } else {
+ Logger.w(TAG, "Service is not the active voice interaction service");
+ }
+ }
+ }
+ return START_STICKY;
+ }
+
+ @Override
+ public void onShutdown() {
+ super.onShutdown();
+ Logger.d(TAG, "onShutdown");
+ }
}
diff --git a/app/src/main/java/com/ttstd/dialer/service/MyVoiceInteractionSession.java b/app/src/main/java/com/ttstd/dialer/service/MyVoiceInteractionSession.java
index 841f85b..27a06d2 100644
--- a/app/src/main/java/com/ttstd/dialer/service/MyVoiceInteractionSession.java
+++ b/app/src/main/java/com/ttstd/dialer/service/MyVoiceInteractionSession.java
@@ -3,10 +3,15 @@ package com.ttstd.dialer.service;
import android.app.assist.AssistContent;
import android.app.assist.AssistStructure;
import android.content.Context;
+import android.graphics.Bitmap;
+import android.os.Build;
import android.os.Bundle;
import android.service.voice.VoiceInteractionSession;
import android.view.View;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import com.ttstd.dialer.utils.Logger;
public class MyVoiceInteractionSession extends VoiceInteractionSession {
@@ -16,24 +21,69 @@ public class MyVoiceInteractionSession extends VoiceInteractionSession {
super(context);
}
+ @Override
+ public void onShow(Bundle args, int showFlags) {
+ super.onShow(args, showFlags);
+ Logger.d(TAG, "onShow: flags=" + showFlags);
+ }
+
+ @Override
+ public void onHide() {
+ super.onHide();
+ Logger.d(TAG, "onHide");
+ }
+
@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);
+ Logger.d(TAG, "onHandleAssist: data=" + data);
+ processAssistStructure(structure);
+ }
+
+ // Android 12 (API 31) 及以上建议使用此方法
+ @Override
+ public void onHandleAssist(@NonNull AssistState state) {
+ super.onHandleAssist(state);
+ Logger.d(TAG, "onHandleAssist(AssistState)");
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ processAssistStructure(state.getAssistStructure());
+ }
+ }
+
+ @Override
+ public void onHandleScreenshot(@Nullable Bitmap screenshot) {
+ super.onHandleScreenshot(screenshot);
+ Logger.d(TAG, "onHandleScreenshot: " + (screenshot != null ? "has screenshot" : "null"));
+ }
+
+ private void processAssistStructure(AssistStructure structure) {
+ if (structure == null) {
+ Logger.w(TAG, "AssistStructure is null");
+ return;
+ }
+
+ int windowCount = structure.getWindowNodeCount();
+ Logger.d(TAG, "Window count: " + windowCount);
+ for (int i = 0; i < windowCount; i++) {
+ AssistStructure.WindowNode windowNode = structure.getWindowNodeAt(i);
if (windowNode != null) {
- traverseNode(windowNode.getRootViewNode(), "");
+ Logger.d(TAG, "Window[" + i + "]: title=" + windowNode.getTitle());
+ traverseNode(windowNode.getRootViewNode(), " ");
}
}
}
private void traverseNode(AssistStructure.ViewNode node, String prefix) {
if (node == null) return;
+
+ CharSequence text = node.getText();
+ String id = node.getIdEntry();
+ String className = node.getClassName();
- Logger.d(TAG, prefix + "View: text=" + node.getText() + ", id=" + node.getIdEntry());
-
+ if (text != null || id != null) {
+ Logger.d(TAG, prefix + "View: class=" + className + ", text=" + text + ", id=" + id);
+ }
+
for (int i = 0; i < node.getChildCount(); i++) {
traverseNode(node.getChildAt(i), prefix + " ");
}
@@ -41,7 +91,7 @@ public class MyVoiceInteractionSession extends VoiceInteractionSession {
@Override
public View onCreateContentView() {
- // 可以返回一个自定义布局,用于在屏幕上显示助手界面
- return super.onCreateContentView();
+ // 返回一个空的 View,以确保 Session 能够正确显示
+ return new View(getContext());
}
}
diff --git a/app/src/main/res/xml/my_recognition_service.xml b/app/src/main/res/xml/my_recognition_service.xml
new file mode 100644
index 0000000..e5891d6
--- /dev/null
+++ b/app/src/main/res/xml/my_recognition_service.xml
@@ -0,0 +1,3 @@
+
+