添加犯困表情

This commit is contained in:
peng 2025-09-17 15:35:44 +08:00
parent e01fd86961
commit 2efff01dcd
7 changed files with 335 additions and 9 deletions

View File

@ -22,6 +22,7 @@
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<!-- BLE功能特性 -->
<uses-feature

View File

@ -144,13 +144,13 @@ public class MainActivity extends AppCompatActivity {
// 初始化UI控件
ivFace = (ImageView) this.findViewById(R.id.iv_face);
// 初始化动画管理器
animationManager = new RobotFaceAnimationManager(this, ivFace);
// 初始化动画管理器单例
animationManager = RobotFaceAnimationManager.getInstance(this, ivFace);
// 初始化长按检测器
initLongPressDetector();
// 预加载所有动画资源
// 预加载动画资源
animationManager.loadFaceAnimation(RobotFaceAnimationManager.FACE_LOOP_0);
// playRandomFaceAnimation();
// new Thread(() -> {
@ -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);

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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());
}
}

View File

@ -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

View File

@ -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;
}
}