Browse Source

Merge branch 'new_dev' of https://github.com/deepcloud2048/deep_voice into new_dev

newdev_shunjiawei
tanlongsheng 1 year ago
parent
commit
ff16df0d1e
  1. 16
      lib/modules/agent/controllers/agent_controller.dart
  2. 52
      lib/modules/home/controllers/home_controller.dart
  3. 27
      lib/modules/opus_test/controllers/opus_test_controller.dart
  4. 19
      lib/modules/translation/controllers/translation_controller.dart
  5. 38
      local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt
  6. 250
      local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureAsrHelper.kt
  7. 7
      local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureSpeechPlugin.kt
  8. 16
      local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/ChatApiService.kt
  9. 2
      pubspec.yaml

16
lib/modules/agent/controllers/agent_controller.dart

@ -649,11 +649,13 @@ class AgentController extends GetxController {
break; break;
case AgentServiceEventType.error: case AgentServiceEventType.error:
isListening.value = false; //验证错误码
isSpeaking.value = false; if (event.data['code'] == 1000) {
isProcessing.value = false; isListening.value = false;
isImageProcessing.value = false; isSpeaking.value = false;
isProcessing.value = false;
isImageProcessing.value = false;
}
// 移除临时的识别消息 // 移除临时的识别消息
final index = final index =
messages.lastIndexWhere((msg) => msg.isRecognizing && msg.isUser); messages.lastIndexWhere((msg) => msg.isRecognizing && msg.isUser);
@ -662,6 +664,8 @@ class AgentController extends GetxController {
// messages.refresh(); // messages.refresh();
} }
Get.snackbar('error'.tr, '${event.data['message']}');
Logger.e(TAG, '代理服务错误: ${event.data['message']}'); Logger.e(TAG, '代理服务错误: ${event.data['message']}');
break; break;
@ -1076,6 +1080,8 @@ class AgentController extends GetxController {
// } // }
try { try {
await AgentService.stopConversation();
await AgentService.recognizeCallback(); await AgentService.recognizeCallback();
if (_bluetoothManager.currentDeviceRx.value != null) { if (_bluetoothManager.currentDeviceRx.value != null) {
// await AgentService.restoreOriginalAudioState(); // await AgentService.restoreOriginalAudioState();

52
lib/modules/home/controllers/home_controller.dart

@ -23,6 +23,11 @@ class HomeController extends GetxController {
final _isFullyConnected = false.obs; final _isFullyConnected = false.obs;
String? _lastConnectedDeviceName; String? _lastConnectedDeviceName;
// 添加电池信息定时刷新相关变量
Timer? _batteryRefreshTimer;
static const Duration _batteryRefreshInterval =
Duration(seconds: 2); // 2秒刷新一次
bool get isFullyConnected => _isFullyConnected.value; bool get isFullyConnected => _isFullyConnected.value;
RxBool get isFullyConnectedRx => _isFullyConnected; RxBool get isFullyConnectedRx => _isFullyConnected;
BluetoothDevice? get currentDevice => _bluetoothManager.currentDevice; BluetoothDevice? get currentDevice => _bluetoothManager.currentDevice;
@ -58,6 +63,7 @@ class HomeController extends GetxController {
void onInit() { void onInit() {
super.onInit(); super.onInit();
_initWithCachedInfo(); _initWithCachedInfo();
_startBatteryRefreshTimer(); // 启动电池信息定时刷新
} }
@override @override
@ -65,6 +71,7 @@ class HomeController extends GetxController {
_deviceInfoWorker?.dispose(); _deviceInfoWorker?.dispose();
_classicBluetoothWorker?.dispose(); _classicBluetoothWorker?.dispose();
_bleConnectionWorker?.dispose(); _bleConnectionWorker?.dispose();
_stopBatteryRefreshTimer(); // 停止定时器
super.onClose(); super.onClose();
} }
@ -260,4 +267,49 @@ class HomeController extends GetxController {
? deviceInfo.value!.firmwareVersion ? deviceInfo.value!.firmwareVersion
: 'unknown'.tr; : 'unknown'.tr;
} }
/// 启动电池信息定时刷新
void _startBatteryRefreshTimer() {
_stopBatteryRefreshTimer(); // 先停止现有定时器
_batteryRefreshTimer = Timer.periodic(_batteryRefreshInterval, (timer) {
// 只有在设备完全连接时才刷新电池信息
if (_isFullyConnected.value) {
_refreshBatteryInfo();
}
});
Logger.d(_tag, '电池信息定时刷新已启动,间隔: ${_batteryRefreshInterval.inSeconds}秒');
}
/// 停止电池信息定时刷新
void _stopBatteryRefreshTimer() {
if (_batteryRefreshTimer != null) {
_batteryRefreshTimer!.cancel();
_batteryRefreshTimer = null;
Logger.d(_tag, '电池信息定时刷新已停止');
}
}
/// 刷新电池信息
Future<void> _refreshBatteryInfo() async {
try {
if (!_isFullyConnected.value) {
return;
}
Logger.d(_tag, '开始刷新电池信息...');
// 通过BLE管理器获取最新的电池信息
final success = await _bleManager.fetchBatteryInfo();
if (success) {
Logger.d(_tag, '电池信息刷新请求发送成功');
} else {
Logger.w(_tag, '电池信息刷新请求发送失败');
}
} catch (e) {
Logger.e(_tag, '刷新电池信息时发生错误: $e');
}
}
} }

27
lib/modules/opus_test/controllers/opus_test_controller.dart

@ -103,16 +103,31 @@ class OpusTestController extends GetxController {
Future<void> loadExternalFiles() async { Future<void> loadExternalFiles() async {
try { try {
externalFiles.clear(); externalFiles.clear();
statusMessage.value = '正在扫描外部存储...'; statusMessage.value = '正在扫描存储...';
print('正在扫描存储...');
// 获取外部存储目录 List<Directory> directories = [];
final directories = await getExternalStorageDirectories();
if (directories == null || directories.isEmpty) { if (Platform.isAndroid) {
statusMessage.value = '未找到外部存储目录'; // Android: 使用外部存储目录
final androidDirs = await getExternalStorageDirectories();
if (androidDirs != null && androidDirs.isNotEmpty) {
directories.addAll(androidDirs);
}
} else if (Platform.isIOS) {
// iOS: 使用Documents目录
final iosDir = await getApplicationDocumentsDirectory();
directories.add(iosDir);
}
if (directories.isEmpty) {
statusMessage.value = '未找到存储目录';
return; return;
} }
// 遍历所有外部存储目录 print('存储目录: $directories');
// 遍历所有存储目录
for (final directory in directories) { for (final directory in directories) {
final dirPath = directory.path; final dirPath = directory.path;
final dir = Directory(dirPath); final dir = Directory(dirPath);

19
lib/modules/translation/controllers/translation_controller.dart

@ -68,11 +68,7 @@ class TranslationController extends GetxController {
static const String _targetLanguageKey = 'translation_target_language'; static const String _targetLanguageKey = 'translation_target_language';
// 分页加载相关状态 // 分页加载相关状态
final isLoadingMore = false.obs; // 是否正在加载更多历史记录
final hasMoreHistory = true.obs; // 是否还有更多历史记录
final currentPage = 1.obs; // 当前页码
final pageSize = 20; // 每页记录数量 final pageSize = 20; // 每页记录数量
final isLoadingMoreError = false.obs; // 加载更多时是否出错
bool _scrollListenerSetup = false; // 滚动监听器设置标志 bool _scrollListenerSetup = false; // 滚动监听器设置标志
RxInt activeSpeaker = 0.obs; // 0=无激活, 1=按钮1, 2=按钮2 RxInt activeSpeaker = 0.obs; // 0=无激活, 1=按钮1, 2=按钮2
@ -110,6 +106,10 @@ class TranslationController extends GetxController {
RxList<TranslationItem> get translationHistory => RxList<TranslationItem> get translationHistory =>
_historyManager.translationHistory; _historyManager.translationHistory;
ScrollController get scrollController => _historyManager.scrollController; ScrollController get scrollController => _historyManager.scrollController;
// 添加历史管理器状态访问器
RxBool get hasMoreHistory => _historyManager.hasMoreHistory;
RxBool get isLoadingMore => _historyManager.isLoadingMore;
RxBool get isLoadingMoreError => _historyManager.isLoadingMoreError;
// 其他 // 其他
StreamSubscription? _recognitionSubscription; StreamSubscription? _recognitionSubscription;
StreamSubscription? _voiceInteractionSubscription; StreamSubscription? _voiceInteractionSubscription;
@ -572,11 +572,11 @@ class TranslationController extends GetxController {
} }
Future<void> _startAsrService() async { Future<void> _startAsrService() async {
if (bluetoothManager.currentDeviceRx.value != null) { // if (bluetoothManager.currentDeviceRx.value != null) {
restoreOriginalAudioState(); // restoreOriginalAudioState();
} else { // } else {
disableBluetoothAudio(); // disableBluetoothAudio();
} // }
await _asrService.startContinuousRecognition(_audioSourceType); await _asrService.startContinuousRecognition(_audioSourceType);
} }
@ -612,6 +612,7 @@ class TranslationController extends GetxController {
latestRecognitionText.value = event.text; latestRecognitionText.value = event.text;
print("处理麦:${event.text}"); print("处理麦:${event.text}");
} else if (event.type == RecognitionEventType.error) { } else if (event.type == RecognitionEventType.error) {
Get.snackbar('error'.tr, event.error);
isRecognizing.value = false; isRecognizing.value = false;
Logger.error('语音识别错误: ${event.error}'); Logger.error('语音识别错误: ${event.error}');
} else if (event.type == RecognitionEventType.canceled) { } else if (event.type == RecognitionEventType.canceled) {

38
local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt

@ -435,18 +435,7 @@ object AgentService : CoroutineScope {
Log.d(TAG, "通话模式:禁用空闲检测") Log.d(TAG, "通话模式:禁用空闲检测")
return return
} }
"ble_wakeup" -> {
// BLE唤醒模式:使用默认的空闲检测
startIdleCheck(maxIdleSeconds)
}
"push_to_talk" -> {
// 按住说话模式:使用较长的空闲检测时间
startIdleCheck(maxIdleSeconds)
}
"normal" -> {
// 普通模式:使用默认的空闲检测
startIdleCheck(maxIdleSeconds)
}
else -> { else -> {
// 未知模式:使用默认的空闲检测 // 未知模式:使用默认的空闲检测
Log.w(TAG, "未知识别模式: $mode,使用默认空闲检测") Log.w(TAG, "未知识别模式: $mode,使用默认空闲检测")
@ -543,9 +532,7 @@ object AgentService : CoroutineScope {
return false return false
} }
// 设置当前识别模式
currentRecognitionMode = mode
Log.d(TAG, "设置语音识别模式: $mode")
_isRecognitionActive.set(true) _isRecognitionActive.set(true)
_hasSpeechDetected.set(false) _hasSpeechDetected.set(false)
@ -561,7 +548,9 @@ object AgentService : CoroutineScope {
// 根据模式启动相应的空闲检测 // 根据模式启动相应的空闲检测
startIdleCheckForMode(mode) startIdleCheckForMode(mode)
// 设置当前识别模式
currentRecognitionMode = mode
Log.d(TAG, "设置语音识别模式: $mode")
return true return true
} catch (e: Exception) { } catch (e: Exception) {
_isRecognitionActive.set(false) _isRecognitionActive.set(false)
@ -584,12 +573,12 @@ object AgentService : CoroutineScope {
override fun onRecognizing(recognizing: String, detectedLanguage: String) { override fun onRecognizing(recognizing: String, detectedLanguage: String) {
if (recognizing.isNotEmpty()) { if (recognizing.isNotEmpty()) {
// 检测到语音,更新状态 // 检测到语音,更新状态
val previousHasSpeech = hasSpeechDetected
_hasSpeechDetected.set(true) _hasSpeechDetected.set(true)
if (!previousHasSpeech) {
restartIdleCheck() restartIdleCheck()
}
sendEvent( sendEvent(
"recognizing", mapOf( "recognizing", mapOf(
@ -637,19 +626,16 @@ object AgentService : CoroutineScope {
processWithChatApi(result) processWithChatApi(result)
} }
// 重置状态,继续识别
val previousHasSpeech = hasSpeechDetected
_hasSpeechDetected.set(false) _hasSpeechDetected.set(false)
if (previousHasSpeech) {
restartIdleCheck() restartIdleCheck()
}
} }
override fun onSessionStarted() { override fun onSessionStarted() {
sendEvent("recognition_started", mapOf("status" to "started")) sendEvent("recognition_started", mapOf("status" to "started"))
// 使用模式感知的空闲检测启动方法 // 使用模式感知的空闲检测启动方法
startIdleCheckForMode(currentRecognitionMode)
audioPlayer?.playAudio(R.raw.start) audioPlayer?.playAudio(R.raw.start)
} }
@ -677,14 +663,14 @@ object AgentService : CoroutineScope {
recognizeCallback() recognizeCallback()
} }
override fun onError(error: String) { override fun onError(code: Int, error: String) {
_isRecognitionActive.set(false) _isRecognitionActive.set(false)
stopIdleCheck() stopIdleCheck()
BleService.closeCodec() BleService.closeCodec()
Log.d(TAG, "语音识别错误: $error") Log.d(TAG, "语音识别错误: $error")
sendEvent( sendEvent(
"error", mapOf( "error", mapOf(
"code" to "RECOGNITION_ERROR", "code" to code,
"message" to error "message" to error
) )
) )

250
local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureAsrHelper.kt

@ -35,6 +35,12 @@ import java.util.concurrent.atomic.AtomicBoolean
* 针对回音消除的场景,使用拉流模式 (PullAudioInputStreamCallback) 实现,避免复杂的协程处理 * 针对回音消除的场景,使用拉流模式 (PullAudioInputStreamCallback) 实现,避免复杂的协程处理
* 通过检测扬声器和耳机状态,自动决定是否启用回音消除 * 通过检测扬声器和耳机状态,自动决定是否启用回音消除
*/ */
import android.net.ConnectivityManager
import android.net.Network
import android.net.NetworkCapabilities
import android.net.NetworkRequest
import android.os.Build
class AzureAsrHelper(private val context: Context) { class AzureAsrHelper(private val context: Context) {
private val tag = "AzureAsrHelper" private val tag = "AzureAsrHelper"
@ -60,7 +66,11 @@ class AzureAsrHelper(private val context: Context) {
// 音频处理 // 音频处理
var audioStream: SimpleAudioReceiver? = null var audioStream: SimpleAudioReceiver? = null
// 网络状态监听相关
private var connectivityManager: ConnectivityManager? = null
private var networkCallback: ConnectivityManager.NetworkCallback? = null
private var isNetworkAvailable = true
private var isNetworkRecovering = false
@ -142,6 +152,9 @@ class AzureAsrHelper(private val context: Context) {
} }
// 初始化网络监听
initNetworkMonitoring()
return true return true
} catch (e: Exception) { } catch (e: Exception) {
Log.e(tag, "初始化失败: ${e.message}") Log.e(tag, "初始化失败: ${e.message}")
@ -233,7 +246,7 @@ class AzureAsrHelper(private val context: Context) {
audioSourceType: AudioSourceType = AudioSourceType.MICROPHONE audioSourceType: AudioSourceType = AudioSourceType.MICROPHONE
) { ) {
if (speechConfig == null) { if (speechConfig == null) {
callback.onError("语音服务未初始化") callback.onError(1002,"语音服务未初始化")
return return
} }
@ -268,26 +281,59 @@ class AzureAsrHelper(private val context: Context) {
?: supportedLanguages[0] ?: supportedLanguages[0]
callback.onResult(result.text, detectedLanguage) callback.onResult(result.text, detectedLanguage)
} else { } else {
callback.onError("未能识别语音") callback.onError(1004,"未能识别语音")
} }
} catch (e: Exception) { } catch (e: Exception) {
stopAudioProcessing() stopAudioProcessing()
callback.onError("识别异常: ${e.message}") callback.onError(1003,"识别异常: ${e.message}")
}
}
/**
* 检查当前网络状态
* @return true表示网络可用,false表示网络不可用
*/
private fun checkNetworkStatus(): Boolean {
try {
val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val network = connectivityManager.activeNetwork ?: return false
val networkCapabilities = connectivityManager.getNetworkCapabilities(network) ?: return false
return networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) &&
networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
} else {
@Suppress("DEPRECATION")
val networkInfo = connectivityManager.activeNetworkInfo
return networkInfo?.isConnectedOrConnecting == true
}
} catch (e: Exception) {
Log.e(tag, "检查网络状态失败: ${e.message}")
return false
} }
} }
/** /**
* 开始连续语音识别 * 开始连续语音识别
* @param audioSourceType 音频源类型 * @param audioSourceType 音频源类型
* @param audioDataCallback 音频数据回调接口 * @param audioDataCallback 音频数据回调
* @return 是否成功启动 * @return 是否成功开始识别
*/ */
fun startContinuousRecognition( fun startContinuousRecognition(
audioSourceType: AudioSourceType = AudioSourceType.MICROPHONE, audioSourceType: AudioSourceType = AudioSourceType.MICROPHONE,
audioDataCallback: SimpleAudioReceiver.AudioDataCallback? = null audioDataCallback: SimpleAudioReceiver.AudioDataCallback? = null
): Boolean { ): Boolean {
Log.d(tag, "startContinuousRecognition:$isContinuousRecognitionActive ") Log.d(tag, "startContinuousRecognition:$isContinuousRecognitionActive ")
// 检查网络状态
if (!checkNetworkStatus()) {
Log.e(tag, "网络不可用,无法开始语音识别")
continuousCallback?.onError(1000,"网络连接不可用,请检查网络设置")
return false
}
if (isContinuousRecognitionActive) { if (isContinuousRecognitionActive) {
return true return true
} }
@ -299,15 +345,15 @@ class AzureAsrHelper(private val context: Context) {
} }
} }
this.audioSourceType = audioSourceType this.audioSourceType = audioSourceType
try { try {
Log.d(tag, "startContinuousRecognition: ${audioStream!!.isWriting()}")
Log.d(tag, "startContinuousRecognition: ")
// 启动音频处理(若已启动则跳过) // 启动音频处理(若已启动则跳过)
if (!audioStream!!.isWriting()) { if (!audioStream!!.isWriting()) {
Log.d(tag, "startAudioRecord:$audioSourceType")
audioStream!!.startAudioRecord( audioStream!!.startAudioRecord(
when (audioSourceType) { when (audioSourceType) {
AudioSourceType.MICROPHONE -> SimpleAudioReceiver.AudioSourceType.MICROPHONE AudioSourceType.MICROPHONE -> SimpleAudioReceiver.AudioSourceType.MICROPHONE
AudioSourceType.EXTERNAL -> SimpleAudioReceiver.AudioSourceType.EXTERNAL AudioSourceType.EXTERNAL -> SimpleAudioReceiver.AudioSourceType.EXTERNAL
}, },
@ -316,7 +362,7 @@ class AzureAsrHelper(private val context: Context) {
} else { } else {
Log.d(tag, "音频已启动,跳过重复启动") Log.d(tag, "音频已启动,跳过重复启动")
} }
recognizer?.startContinuousRecognitionAsync() recognizer?.startContinuousRecognitionAsync()
isContinuousRecognitionActive = true isContinuousRecognitionActive = true
return true return true
@ -334,7 +380,7 @@ class AzureAsrHelper(private val context: Context) {
fun setupEventListeners(callback: ContinuousRecognizeCallback): Boolean { fun setupEventListeners(callback: ContinuousRecognizeCallback): Boolean {
Log.d(tag, "设置ssssss监听器:${speechConfig} ") Log.d(tag, "设置ssssss监听器:${speechConfig} ")
if (speechConfig == null) { if (speechConfig == null) {
callback.onError("语音服务未初始化") callback.onError(1002,"语音服务未初始化")
return false return false
} }
@ -399,18 +445,31 @@ class AzureAsrHelper(private val context: Context) {
} }
) )
// 取消事件 // 取消事件
recognizer?.canceled?.addEventListener( recognizer?.canceled?.addEventListener(
EventHandler<SpeechRecognitionCanceledEventArgs> { _, event -> EventHandler<SpeechRecognitionCanceledEventArgs> { _, event ->
val errorDetails = event.errorDetails ?: "未知错误" val errorDetails = event.errorDetails ?: "未知错误"
val reason = event.reason.toString() val reason = event.reason.toString()
callback.onCanceled(reason, errorDetails)
Log.d(tag, "识别被取消: reason=$reason, details=$errorDetails")
// 检查是否是网络相关错误
if (isNetworkRelatedError(reason, errorDetails)) {
Log.d(tag, "检测到网络相关错误,停止音频处理")
stopAudioProcessingImmediately()
// 如果不是正在恢复网络,通知上层网络错误
if (!isNetworkRecovering) {
callback.onError(1000,"启动连续识别失败,网络连接已断开")
}
} else {
// 非网络错误,正常处理
callback.onCanceled(reason, errorDetails)
}
} }
) )
} catch (e: Exception) { } catch (e: Exception) {
callback.onError("启动连续识别失败: ${e.message}") callback.onError(1001,"启动连续识别失败: ${e.message}")
return false return false
} }
return true return true
@ -428,9 +487,9 @@ class AzureAsrHelper(private val context: Context) {
return false return false
} }
if (!isContinuousRecognitionActive) { // if (!isContinuousRecognitionActive) {
return true // return true
} // }
try { try {
@ -485,6 +544,14 @@ class AzureAsrHelper(private val context: Context) {
// recognizer?.stopContinuousRecognitionAsync()?.get(1000, TimeUnit.MILLISECONDS) // recognizer?.stopContinuousRecognitionAsync()?.get(1000, TimeUnit.MILLISECONDS)
// 停止音频处理 // 停止音频处理
stopAudioProcessing() stopAudioProcessing()
// 清理网络监听
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && networkCallback != null) {
connectivityManager?.unregisterNetworkCallback(networkCallback!!)
networkCallback = null
}
connectivityManager = null
// 停止录音 // 停止录音
if (audioStream!!.recordfile != null) { if (audioStream!!.recordfile != null) {
audioStream!!.recordfile!!.closeFile(true) audioStream!!.recordfile!!.closeFile(true)
@ -647,9 +714,10 @@ class AzureAsrHelper(private val context: Context) {
/** /**
* 识别错误时调用 * 识别错误时调用
* *
* @param code 错误码
* @param error 错误信息 * @param error 错误信息
*/ */
fun onError(error: String) fun onError(code: Int,error: String)
} }
/** /**
@ -696,10 +764,140 @@ class AzureAsrHelper(private val context: Context) {
* *
* @param error 错误信息 * @param error 错误信息
*/ */
fun onError(error: String) fun onError(code: Int,error: String)
} }
/**
* 检查是否是网络相关错误
*/
private fun isNetworkRelatedError(reason: String, errorDetails: String): Boolean {
val networkErrorKeywords = arrayOf(
"network", "connection", "timeout", "unreachable",
"dns", "socket", "ssl", "tls", "certificate",
"网络", "连接", "超时", "不可达"
)
val combinedText = "$reason $errorDetails".lowercase()
return networkErrorKeywords.any { keyword ->
combinedText.contains(keyword.lowercase())
}
}
/**
* 初始化网络状态监听
*/
private fun initNetworkMonitoring() {
try {
connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
networkCallback = object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
super.onAvailable(network)
Log.d(tag, "网络连接可用")
handleNetworkAvailable()
}
override fun onLost(network: Network) {
super.onLost(network)
Log.d(tag, "网络连接丢失")
handleNetworkLost()
}
override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
super.onCapabilitiesChanged(network, networkCapabilities)
val hasInternet = networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
Log.d(tag, "网络能力变化,是否有网络: $hasInternet")
if (hasInternet && !isNetworkAvailable) {
handleNetworkAvailable()
} else if (!hasInternet && isNetworkAvailable) {
handleNetworkLost()
}
}
}
val networkRequest = NetworkRequest.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.build()
connectivityManager?.registerNetworkCallback(networkRequest, networkCallback!!)
}
} catch (e: Exception) {
Log.e(tag, "初始化网络监听失败: ${e.message}")
}
}
/**
* 处理网络连接可用
*/
private fun handleNetworkAvailable() {
if (!isNetworkAvailable) {
isNetworkAvailable = true
isNetworkRecovering = true
Log.d(tag, "网络恢复,准备重新启动识别")
// 如果之前在进行连续识别,重新启动
Handler(Looper.getMainLooper()).postDelayed({
restartContinuousRecognition()
}, 1000) // 延迟1秒确保网络稳定
}
}
/**
* 处理网络连接丢失
*/
private fun handleNetworkLost() {
if (isNetworkAvailable) {
isNetworkAvailable = false
Log.d(tag, "网络断开,停止音频处理")
stopAudioProcessingImmediately()
}
}
/**
* 立即停止音频处理(网络断开时使用)
*/
private fun stopAudioProcessingImmediately() {
try {
Log.d(tag, "立即停止音频处理")
stopContinuousRecognition()
// 不改变isContinuousRecognitionActive状态,保持识别意图
} catch (e: Exception) {
Log.e(tag, "立即停止音频处理失败: ${e.message}")
}
}
/**
* 重新启动连续识别(网络恢复时使用)
*/
private fun restartContinuousRecognition() {
try {
Log.d(tag, "重新启动连续识别")
// 检查continuousCallback是否为空
val callback = continuousCallback
if (callback == null) {
Log.e(tag, "无法重新启动连续识别:回调为空")
return
}
setupEventListeners(callback)
isNetworkRecovering = false
Log.d(tag, "连续识别重新启动成功")
// 通知上层网络已恢复
//continuousCallback?.onError("网络已恢复")
} catch (e: Exception) {
Log.e(tag, "重新启动连续识别失败: ${e.message}")
isNetworkRecovering = false
}
}
} }

7
local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureSpeechPlugin.kt

@ -256,10 +256,11 @@ class AzureSpeechPlugin : BleService.Callback, FlutterPlugin {
} }
override fun onError(error: String) { override fun onError(code: Int, error: String) {
sendAsrEvent( sendAsrEvent(
mapOf( mapOf(
"type" to "error", "type" to "error",
"code" to code,
"message" to error "message" to error
) )
) )
@ -386,8 +387,8 @@ class AzureSpeechPlugin : BleService.Callback, FlutterPlugin {
) )
} }
override fun onError(error: String) { override fun onError(code: Int, error: String) {
result.error("RECOGNITION_ERROR", error, null) result.error(code.toString(), "错误码: $code, 错误信息: $error", null)
} }
}, audioSourceType) }, audioSourceType)
} catch (e: Exception) { } catch (e: Exception) {

16
local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/ChatApiService.kt

@ -404,12 +404,24 @@ class ChatApiService(private val context: android.content.Context? = null) : Cor
throw IllegalStateException("OpenAI客户端未初始化") throw IllegalStateException("OpenAI客户端未初始化")
} }
Log.e("ChatApiService", "liwei---------,发送AI请求 model:${currentModel} messages:${chatMessages}") Log.e("ChatApiService", "liwei---------,发送AI请求 model:${currentModel} messages:${chatMessages}")
val flow = openAI!!.chatCompletions(chatCompletionRequest) // 添加超时控制,10秒超时
val flow = withTimeout(10.seconds) {
openAI!!.chatCompletions(chatCompletionRequest)
}
Log.d(TAG, "liwei-------------------------开始AI 对话 6") Log.d(TAG, "liwei-------------------------开始AI 对话 6")
flow flow
} catch (e: Exception) { } catch (e: Exception) {
Log.e("ChatApiService", "创建ChatCompletionRequest或调用chatCompletions失败: ${e.message}", e) Log.e("ChatApiService", "创建ChatCompletionRequest或调用chatCompletions失败: ${e.message}", e)
throw e if (sessionid == currSessionId) {
try {
getSessionCallback(sessionid)?.onError(sessionid,ChatApiException("流式请求失败: ${e.message}"))
} catch (ex: Exception) {
Log.e(TAG, "onError回调异常: ${ex.message}", ex)
}
clearCurrentSession()
}
return@launch
// throw e
} }
var replyContext = ""; var replyContext = "";

2
pubspec.yaml

@ -53,7 +53,7 @@ dependencies:
flutter_local_notifications: ^18.0.1 flutter_local_notifications: ^18.0.1
device_info_plus: ^11.5.0 device_info_plus: ^11.5.0
uuid: ^4.3.3 uuid: ^4.3.3
record: ^5.2.1 record: ^6.0.0
share_plus: ^11.0.0 share_plus: ^11.0.0
image_picker: ^1.1.2 image_picker: ^1.1.2
audio_waveforms: ^1.3.0 audio_waveforms: ^1.3.0

Loading…
Cancel
Save