From 2efff01dcd85c14785eb707ffa683030ba85cfa4 Mon Sep 17 00:00:00 2001
From: peng <704047449@qq.com>
Date: Wed, 17 Sep 2025 15:35:44 +0800
Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E7=8A=AF=E5=9B=B0=E8=A1=A8?=
=?UTF-8?q?=E6=83=85?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
app/src/main/AndroidManifest.xml | 1 +
.../com/ismart/ib86/app/MainActivity.java | 14 +-
.../NotificationDialogFragment.java | 22 +++
.../feature/audio/SystemVolumeController.java | 152 ++++++++++++++++++
.../feature/audio/VolumeControlExample.java | 66 ++++++++
.../feature/network/NetworkStateManager.java | 4 +-
.../robotFace/RobotFaceAnimationManager.java | 85 +++++++++-
7 files changed, 335 insertions(+), 9 deletions(-)
create mode 100644 app/src/main/java/com/ismart/ib86/feature/audio/SystemVolumeController.java
create mode 100644 app/src/main/java/com/ismart/ib86/feature/audio/VolumeControlExample.java
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 01d83c8..705df03 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -22,6 +22,7 @@
+
{
@@ -1069,9 +1069,11 @@ public class MainActivity extends AppCompatActivity {
handleConversationStarted();
break;
case EVENT_HUMAN_SPEAKING_DETAIL:
+ Log.d(TAG_AI, "EVENT_HUMAN_SPEAKING_DETAIL");
handleSpeakingDetail(event.getResponse(), true);
break;
case EVENT_RESPONDING_DETAIL:
+ Log.d(TAG_AI, "EVENT_RESPONDING_DETAIL");
handleSpeakingDetail(event.getResponse(), false);
handleResponseCommand(event.getResponse());
break;
@@ -1116,6 +1118,7 @@ public class MainActivity extends AppCompatActivity {
private void handleResponseCommand(String response) {
String command = parseResponseCommand(response);
if (command != null && authParams.getChainMode() == Constant.ChainMode.WEBSOCKET) {
+ Log.d(TAG_AI, "command:" + command);
isExecutingCommand = true;
ThreadPoolUtil.runOnSubThread(() -> executeCommand(command));
}
@@ -1206,6 +1209,7 @@ public class MainActivity extends AppCompatActivity {
JSONObject extraInfo = new JSONObject(output.getString("extra_info"));
if (extraInfo.has("commands")) {
String commands = extraInfo.getString("commands");
+ Log.d(TAG_AI, "commands len:" + commands.length());
if (commands.length() > 6) { // 过滤空命令
return commands;
}
@@ -1240,11 +1244,11 @@ public class MainActivity extends AppCompatActivity {
/////////////////////////////////////// 命令执行 ///////////////////////////////////////
private void executeCommand(String command) {
- Log.d(TAG, "执行命令: " + command);
+ Log.d(TAG_AI, "执行命令: " + command);
try {
String cmdName = new JSONArray(command).getJSONObject(0).getString("name");
- Log.d(TAG, "执行命令: " + cmdName);
+ Log.d(TAG_AI, "执行命令: " + cmdName);
switch (cmdName) {
case "check_battery":
multiModalDialog.requestToRespond("transcript", "当前电量为87%", null);
diff --git a/app/src/main/java/com/ismart/ib86/common/notification/NotificationDialogFragment.java b/app/src/main/java/com/ismart/ib86/common/notification/NotificationDialogFragment.java
index 0967b70..717aa3c 100644
--- a/app/src/main/java/com/ismart/ib86/common/notification/NotificationDialogFragment.java
+++ b/app/src/main/java/com/ismart/ib86/common/notification/NotificationDialogFragment.java
@@ -16,6 +16,7 @@ import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.TextView;
import com.ismart.ib86.app.R;
+import com.ismart.ib86.robotFace.RobotFaceAnimationManager;
/**
* 通知弹窗DialogFragment
@@ -286,6 +287,27 @@ public class NotificationDialogFragment extends DialogFragment {
public void onDestroy() {
super.onDestroy();
+ // 记录当前销毁的NotificationType
+ if (notificationType != null) {
+ Log.d(TAG, "NotificationDialogFragment destroyed, NotificationType: " + notificationType);
+ RobotFaceAnimationManager animationManager = RobotFaceAnimationManager.getInstance();
+ if (animationManager != null) {
+ if (notificationType.equals(NotificationType.NETWORK_LOST.name())) {
+ // 网络连接失败时,加载 sad 表情
+ Log.d(TAG, "Loading sad face animation for network lost");
+ animationManager.loadFaceAnimation(RobotFaceAnimationManager.FACE_SAD, false, 1);
+ } else if (notificationType.equals(NotificationType.NETWORK_CONNECTED.name())) {
+ // 网络连接成功时,加载 happy 表情
+ Log.d(TAG, "Loading happy face animation for network connected");
+ animationManager.loadFaceAnimation(RobotFaceAnimationManager.FACE_HAPPY, false, 1);
+ }
+ } else {
+ Log.w(TAG, "RobotFaceAnimationManager instance is null");
+ }
+ } else {
+ Log.d(TAG, "NotificationDialogFragment destroyed, NotificationType is null");
+ }
+
// 通知NotificationManager重置显示状态
if (getActivity() != null) {
android.content.Intent intent = new android.content.Intent(ACTION_NOTIFICATION_CLOSED);
diff --git a/app/src/main/java/com/ismart/ib86/feature/audio/SystemVolumeController.java b/app/src/main/java/com/ismart/ib86/feature/audio/SystemVolumeController.java
new file mode 100644
index 0000000..6afd380
--- /dev/null
+++ b/app/src/main/java/com/ismart/ib86/feature/audio/SystemVolumeController.java
@@ -0,0 +1,152 @@
+package com.ismart.ib86.feature.audio;
+
+import android.content.Context;
+import android.media.AudioManager;
+import android.util.Log;
+
+/**
+ * 系统音量控制器
+ * 提供读取当前系统媒体音量、静音、调节音量大小等功能
+ */
+public class SystemVolumeController {
+ private static final String TAG = "SystemVolumeController";
+
+ private AudioManager audioManager;
+ private int maxVolume;
+ private int currentVolume;
+ private boolean isMuted = false;
+ private int streamType = AudioManager.STREAM_MUSIC;
+
+ /**
+ * 构造函数
+ * @param context 上下文
+ */
+ public SystemVolumeController(Context context) {
+ audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+ if (audioManager != null) {
+ maxVolume = audioManager.getStreamMaxVolume(streamType);
+ currentVolume = audioManager.getStreamVolume(streamType);
+ } else {
+ Log.e(TAG, "无法获取AudioManager服务");
+ }
+ }
+
+ /**
+ * 获取当前媒体音量
+ * @return 当前音量值
+ */
+ public int getCurrentVolume() {
+ if (audioManager != null) {
+ currentVolume = audioManager.getStreamVolume(streamType);
+ return currentVolume;
+ }
+ return 0;
+ }
+
+ /**
+ * 获取最大音量
+ * @return 最大音量值
+ */
+ public int getMaxVolume() {
+ return maxVolume;
+ }
+
+ /**
+ * 设置音量
+ * @param volume 音量值
+ * @param showUI 是否显示系统音量UI
+ */
+ public void setVolume(int volume, boolean showUI) {
+ if (audioManager != null) {
+ // 限制音量在有效范围内
+ if (volume < 0) volume = 0;
+ if (volume > maxVolume) volume = maxVolume;
+
+ int flags = 0;
+ if (showUI) {
+ flags |= AudioManager.FLAG_SHOW_UI;
+ }
+
+ audioManager.setStreamVolume(streamType, volume, flags);
+ currentVolume = volume;
+ isMuted = (volume == 0);
+ }
+ }
+
+ /**
+ * 调节音量(增加或减少)
+ * @param delta 音量变化量,正数为增加,负数为减少
+ * @param showUI 是否显示系统音量UI
+ */
+ public void adjustVolume(int delta, boolean showUI) {
+ if (audioManager != null) {
+ int flags = AudioManager.FLAG_PLAY_SOUND;
+ if (showUI) {
+ flags |= AudioManager.FLAG_SHOW_UI;
+ }
+
+ audioManager.adjustStreamVolume(streamType, delta > 0 ? AudioManager.ADJUST_RAISE : AudioManager.ADJUST_LOWER, flags);
+ currentVolume = audioManager.getStreamVolume(streamType);
+ isMuted = (currentVolume == 0);
+ }
+ }
+
+ /**
+ * 静音/取消静音
+ * @param mute true为静音,false为取消静音
+ */
+ public void mute(boolean mute) {
+ if (audioManager != null) {
+ if (mute) {
+ // 保存当前音量后再设置为0
+ if (currentVolume > 0) {
+ audioManager.setStreamVolume(streamType, 0, 0);
+ isMuted = true;
+ }
+ } else {
+ // 恢复音量
+ int restoreVolume = (currentVolume > 0) ? currentVolume : maxVolume / 2;
+ audioManager.setStreamVolume(streamType, restoreVolume, 0);
+ currentVolume = restoreVolume;
+ isMuted = false;
+ }
+ }
+ }
+
+ /**
+ * 切换静音状态
+ */
+ public void toggleMute() {
+ mute(!isMuted);
+ }
+
+ /**
+ * 是否处于静音状态
+ * @return true为静音,false为非静音
+ */
+ public boolean isMuted() {
+ return isMuted;
+ }
+
+ /**
+ * 获取音量百分比
+ * @return 音量百分比 (0-100)
+ */
+ public int getVolumePercentage() {
+ if (maxVolume <= 0) return 0;
+ return (int) ((currentVolume * 100.0f) / maxVolume);
+ }
+
+ /**
+ * 根据百分比设置音量
+ * @param percentage 音量百分比 (0-100)
+ * @param showUI 是否显示系统音量UI
+ */
+ public void setVolumeByPercentage(int percentage, boolean showUI) {
+ if (percentage < 0) percentage = 0;
+ if (percentage > 100) percentage = 100;
+
+ int volume = (int) ((percentage * maxVolume) / 100.0f);
+ setVolume(volume, showUI);
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/ismart/ib86/feature/audio/VolumeControlExample.java b/app/src/main/java/com/ismart/ib86/feature/audio/VolumeControlExample.java
new file mode 100644
index 0000000..bd36fb4
--- /dev/null
+++ b/app/src/main/java/com/ismart/ib86/feature/audio/VolumeControlExample.java
@@ -0,0 +1,66 @@
+package com.ismart.ib86.feature.audio;
+
+import android.content.Context;
+import android.util.Log;
+
+/**
+ * 音量控制使用示例
+ */
+public class VolumeControlExample {
+ private static final String TAG = "VolumeControlExample";
+
+ private SystemVolumeController volumeController;
+
+ public VolumeControlExample(Context context) {
+ volumeController = new SystemVolumeController(context);
+ }
+
+ /**
+ * 演示音量控制功能
+ */
+ public void demonstrateVolumeControl() {
+ // 获取当前音量
+ int currentVolume = volumeController.getCurrentVolume();
+ Log.d(TAG, "当前音量: " + currentVolume);
+
+ // 获取最大音量
+ int maxVolume = volumeController.getMaxVolume();
+ Log.d(TAG, "最大音量: " + maxVolume);
+
+ // 获取音量百分比
+ int volumePercentage = volumeController.getVolumePercentage();
+ Log.d(TAG, "音量百分比: " + volumePercentage + "%");
+
+ // 增加音量
+ volumeController.adjustVolume(1, true);
+ Log.d(TAG, "增加音量后当前音量: " + volumeController.getCurrentVolume());
+
+ // 减少音量
+ volumeController.adjustVolume(-1, true);
+ Log.d(TAG, "减少音量后当前音量: " + volumeController.getCurrentVolume());
+
+ // 设置特定音量值
+ volumeController.setVolume(maxVolume / 2, true);
+ Log.d(TAG, "设置音量为一半: " + volumeController.getCurrentVolume());
+
+ // 根据百分比设置音量
+ volumeController.setVolumeByPercentage(80, true);
+ Log.d(TAG, "设置音量为80%: " + volumeController.getCurrentVolume());
+
+ // 静音
+ volumeController.mute(true);
+ Log.d(TAG, "静音后当前音量: " + volumeController.getCurrentVolume() + ", 是否静音: " + volumeController.isMuted());
+
+ // 取消静音
+ volumeController.mute(false);
+ Log.d(TAG, "取消静音后当前音量: " + volumeController.getCurrentVolume() + ", 是否静音: " + volumeController.isMuted());
+
+ // 切换静音状态
+ volumeController.toggleMute();
+ Log.d(TAG, "切换静音状态后当前音量: " + volumeController.getCurrentVolume() + ", 是否静音: " + volumeController.isMuted());
+
+ // 再次切换静音状态
+ volumeController.toggleMute();
+ Log.d(TAG, "再次切换静音状态后当前音量: " + volumeController.getCurrentVolume() + ", 是否静音: " + volumeController.isMuted());
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/ismart/ib86/feature/network/NetworkStateManager.java b/app/src/main/java/com/ismart/ib86/feature/network/NetworkStateManager.java
index 8af55cf..454dde4 100644
--- a/app/src/main/java/com/ismart/ib86/feature/network/NetworkStateManager.java
+++ b/app/src/main/java/com/ismart/ib86/feature/network/NetworkStateManager.java
@@ -10,6 +10,7 @@ import com.ismart.ib86.app.R;
import com.ismart.ib86.common.notification.NotificationType;
import com.ismart.ib86.common.notification.NotificationManager;
import com.ismart.ib86.common.notification.NotificationPriority;
+import com.ismart.ib86.robotFace.RobotFaceAnimationManager;
/**
* 网络状态管理器
@@ -41,7 +42,8 @@ public class NetworkStateManager {
public void onNetworkLost() {
Log.d(TAG, "onNetworkLost called");
// 网络断开使用高优先级
- showNotification(NotificationType.NETWORK_LOST, "设备已断开网络连接。", R.drawable.ic_network_lost, 10, NotificationPriority.HIGH);
+ showNotification(NotificationType.NETWORK_LOST, "设备已断开网络连接。", R.drawable.ic_network_lost, 5, NotificationPriority.HIGH);
+
}
@Override
diff --git a/app/src/main/java/com/ismart/ib86/robotFace/RobotFaceAnimationManager.java b/app/src/main/java/com/ismart/ib86/robotFace/RobotFaceAnimationManager.java
index 321def4..27bec5a 100644
--- a/app/src/main/java/com/ismart/ib86/robotFace/RobotFaceAnimationManager.java
+++ b/app/src/main/java/com/ismart/ib86/robotFace/RobotFaceAnimationManager.java
@@ -12,10 +12,13 @@ import com.ismart.ib86.app.R;
/**
* 机器人表情动画管理器
* 负责加载、控制和切换不同的表情动画
+ * 提供单例模式访问
*/
public class RobotFaceAnimationManager {
+ // 单例实例
+ private static RobotFaceAnimationManager instance;
+ private static final String TAG = "RobotFaceAnimManager";
// 动画类型常量
-
public static final int FACE_LOOP_0 = 0; // 循环表情0
public static final int FACE_LOOP_1 = 1; // 循环表情1
public static final int FACE_LOOP_2 = 2; // 循环表情2
@@ -26,7 +29,6 @@ public class RobotFaceAnimationManager {
// 动画资源ID数组
private static final int[] FACE_ANIMATIONS = {
-
R.drawable.anim_face_xunhuan, // 循环表情0
R.drawable.anim_face_xunhuan1, // 循环表情1
R.drawable.anim_face_xunhuan2, // 循环表情2
@@ -66,13 +68,48 @@ public class RobotFaceAnimationManager {
this.handler = new Handler(Looper.getMainLooper());
}
- private static final String TAG = "RobotFaceAnimManager";
+ /**
+ * 获取单例实例
+ * @param context 上下文
+ * @param imageView 用于显示动画的ImageView
+ * @return RobotFaceAnimationManager 实例
+ */
+ public static synchronized RobotFaceAnimationManager getInstance(Context context, ImageView imageView) {
+ if (instance == null) {
+ instance = new RobotFaceAnimationManager(context, imageView);
+ }
+ return instance;
+ }
+
+ /**
+ * 获取单例实例(需要先调用带参数的getInstance方法初始化)
+ * @return RobotFaceAnimationManager 实例
+ */
+ public static synchronized RobotFaceAnimationManager getInstance() {
+ return instance;
+ }
+
+
+ /**
+ * 加载指定类型的表情动画
+ * @param faceType 表情类型
+ */
/**
* 加载指定类型的表情动画
* @param faceType 表情类型
*/
public void loadFaceAnimation(int faceType) {
+ loadFaceAnimation(faceType, true, -1); // 默认循环播放,无限次数
+ }
+
+ /**
+ * 加载指定类型的表情动画
+ * @param faceType 表情类型
+ * @param loop 是否循环播放
+ * @param repeatCount 重复次数,-1表示无限循环
+ */
+ public void loadFaceAnimation(int faceType, boolean loop, int repeatCount) {
if (faceType < 0 || faceType >= FACE_ANIMATIONS.length) {
faceType = FACE_LOOP_0;
}
@@ -96,6 +133,9 @@ public class RobotFaceAnimationManager {
return;
}
+ // 设置循环属性
+ currentAnimation.setOneShot(!loop);
+
// 应用速度设置
// if (speedFactor != 1.0f) {
// applySpeed(speedFactor);
@@ -107,6 +147,11 @@ public class RobotFaceAnimationManager {
// 如果不是暂停状态,则开始播放
if (!isPaused) {
currentAnimation.start();
+
+ // 如果指定了重复次数且不为无限循环,则在指定次数后停止动画
+ if (!loop && repeatCount > 0) {
+ scheduleStopAfterRepeats(repeatCount);
+ }
}
} catch (Exception e) {
Log.e(TAG, "加载表情动画失败: " + FACE_NAMES[faceType], e);
@@ -226,6 +271,37 @@ public class RobotFaceAnimationManager {
imageView.setImageDrawable(currentAnimation);
}
+ /**
+ * 在指定重复次数后停止动画,并随机选择一个循环表情继续播放
+ * @param repeatCount 重复次数
+ */
+ private void scheduleStopAfterRepeats(int repeatCount) {
+ if (currentAnimation == null) return;
+
+ // 计算总持续时间
+ long totalDuration = 0;
+ int frameCount = currentAnimation.getNumberOfFrames();
+ for (int i = 0; i < frameCount; i++) {
+ totalDuration += currentAnimation.getDuration(i);
+ }
+
+ // 总时间 = 单次播放时间 * 重复次数
+ long delay = totalDuration * repeatCount;
+
+ // 使用Handler在指定时间后停止动画并随机选择循环表情
+ handler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ if (currentAnimation != null && currentAnimation.isRunning()) {
+ currentAnimation.stop();
+ Log.d(TAG, "动画已播放完成指定次数: " + repeatCount);
+
+ loadFaceAnimation(FACE_LOOP_0, true, -1); // 循环播放,无限次数
+ }
+ }
+ }, delay);
+ }
+
/**
* 切换到下一个表情
* @return 新表情的名称
@@ -310,5 +386,8 @@ public class RobotFaceAnimationManager {
context = null;
imageView = null;
handler = null;
+
+ // 清除单例实例
+ instance = null;
}
}
\ No newline at end of file