From a843dd1f31ca05dc1180752958c00d50d929985c Mon Sep 17 00:00:00 2001 From: lxm <1274844347@qq.com> Date: Tue, 9 Sep 2025 15:00:25 +0800 Subject: [PATCH 1/2] =?UTF-8?q?fix(ble=5Fmanager):=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E6=99=BA=E8=83=BD=E7=94=B5=E6=B1=A0=E5=80=BC=E9=80=89=E6=8B=A9?= =?UTF-8?q?=E6=96=B9=E6=B3=95=E9=98=B2=E6=AD=A2=E6=9C=89=E6=95=88=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E8=A2=AB0=E8=A6=86=E7=9B=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增_getValidBatteryValue方法处理电池数据更新逻辑,避免有效电池值被0异常数据覆盖。当新值为0且当前值有效时保持原值,并记录警告日志。 --- lib/data/services/ble_manager.dart | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/lib/data/services/ble_manager.dart b/lib/data/services/ble_manager.dart index ee309c286..1bd4b11c5 100644 --- a/lib/data/services/ble_manager.dart +++ b/lib/data/services/ble_manager.dart @@ -395,7 +395,7 @@ class BleManager extends GetxService { Logger.info('lxm-- overallBattery: $overallBattery'); Logger.info('lxm--准备调用_updateDeviceInfoIfNeeded'); - + // 更新设备信息 _updateDeviceInfoIfNeeded( batteryLevel: overallBattery, @@ -488,8 +488,10 @@ class BleManager extends GetxService { deviceInfo.value = deviceInfo.value!.copyWith( batteryLevel: batteryLevel ?? deviceInfo.value?.batteryLevel, - leftEarBattery: leftEarBattery ?? deviceInfo.value?.leftEarBattery, - rightEarBattery: rightEarBattery ?? deviceInfo.value?.rightEarBattery, + leftEarBattery: _getValidBatteryValue( + leftEarBattery, deviceInfo.value?.leftEarBattery), + rightEarBattery: _getValidBatteryValue( + rightEarBattery, deviceInfo.value?.rightEarBattery), caseBattery: caseBattery ?? deviceInfo.value?.caseBattery, leftEarCharging: leftEarCharging ?? deviceInfo.value!.leftEarCharging, rightEarCharging: @@ -537,6 +539,24 @@ class BleManager extends GetxService { } } + // 新增智能电池值选择方法 + int? _getValidBatteryValue(int? newValue, int? currentValue) { + // 如果新值为null,使用当前值 + if (newValue == null) return currentValue; + + // 如果当前值为null或0,使用新值(即使新值为0) + if (currentValue == null || currentValue == 0) return newValue; + + // 如果新值为0但当前值不为0,保持当前值(避免有效数据被0覆盖) + if (newValue == 0 && currentValue > 0) { + Logger.warning('检测到电池数据异常(新值为0),保持当前值: $currentValue'); + return currentValue; + } + + // 其他情况使用新值 + return newValue; + } + /// 清除设备信息(用于完全断开连接或切换设备时) void clearDeviceInfo() { Logger.info('清除设备信息'); From cfa77f275ecea44285942dbb89d6b8faa444a427 Mon Sep 17 00:00:00 2001 From: fdp <1286779656@qq.com> Date: Tue, 9 Sep 2025 18:40:54 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=AE=89=E5=8D=93?= =?UTF-8?q?=E5=88=87=E6=8D=A2=E5=88=B0=E5=90=8E=E5=8F=B0=E4=B8=8D=E8=83=BD?= =?UTF-8?q?=E7=BB=A7=E7=BB=AD=E5=BD=95=E9=9F=B3=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../android/src/main/AndroidManifest.xml | 15 ++- .../tools/AudioRecordingForegroundService.kt | 125 ++++++++++++++++++ .../azure_speech/tools/SimpleAudioReceiver.kt | 16 ++- 3 files changed, 152 insertions(+), 4 deletions(-) create mode 100644 local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/tools/AudioRecordingForegroundService.kt diff --git a/local_plugins/azure_speech/android/src/main/AndroidManifest.xml b/local_plugins/azure_speech/android/src/main/AndroidManifest.xml index df2a770c6..88f2910c1 100644 --- a/local_plugins/azure_speech/android/src/main/AndroidManifest.xml +++ b/local_plugins/azure_speech/android/src/main/AndroidManifest.xml @@ -3,4 +3,17 @@ package="com.yunqiinnovation.azure_speech"> - \ No newline at end of file + + + + + + + + + + \ No newline at end of file diff --git a/local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/tools/AudioRecordingForegroundService.kt b/local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/tools/AudioRecordingForegroundService.kt new file mode 100644 index 000000000..4b03f4ec3 --- /dev/null +++ b/local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/tools/AudioRecordingForegroundService.kt @@ -0,0 +1,125 @@ +package com.yunqiinnovation.azure_speech.tools + +import android.app.Notification +import android.app.NotificationChannel +import android.app.NotificationManager +import android.app.PendingIntent +import android.app.Service +import android.content.Context +import android.content.Intent +import android.os.Build +import android.os.IBinder +import androidx.core.app.NotificationCompat +import android.util.Log + +/** + * 音频录制前台服务 + * 用于在录制音频时保持应用在前台运行,防止系统杀死应用 + */ +class AudioRecordingForegroundService : Service() { + + companion object { + private const val TAG = "AudioRecordingService" + private const val NOTIFICATION_ID = 1001 + private const val CHANNEL_ID = "audio_recording_channel" + private const val CHANNEL_NAME = "音频录制服务" + + /** + * 启动前台服务 + * @param context 上下文 + */ + fun startService(context: Context) { + val intent = Intent(context, AudioRecordingForegroundService::class.java) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + context.startForegroundService(intent) + } else { + context.startService(intent) + } + Log.d(TAG, "音频录制前台服务已启动") + } + + /** + * 停止前台服务 + * @param context 上下文 + */ + fun stopService(context: Context) { + val intent = Intent(context, AudioRecordingForegroundService::class.java) + context.stopService(intent) + Log.d(TAG, "音频录制前台服务已停止") + } + } + + override fun onCreate() { + super.onCreate() + Log.d(TAG, "AudioRecordingForegroundService onCreate") + createNotificationChannel() + } + + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + Log.d(TAG, "AudioRecordingForegroundService onStartCommand") + + // 创建前台通知 + val notification = createNotification() + startForeground(NOTIFICATION_ID, notification) + + // 返回START_STICKY确保服务被系统杀死后会重启 + return START_STICKY + } + + override fun onBind(intent: Intent?): IBinder? { + return null + } + + override fun onDestroy() { + super.onDestroy() + Log.d(TAG, "AudioRecordingForegroundService onDestroy") + stopForeground(true) + } + + /** + * 创建通知渠道(Android 8.0及以上需要) + */ + private fun createNotificationChannel() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val channel = NotificationChannel( + CHANNEL_ID, + CHANNEL_NAME, + NotificationManager.IMPORTANCE_LOW + ).apply { + description = "用于音频录制时保持应用运行" + setShowBadge(false) + setSound(null, null) + enableVibration(false) + } + + val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + notificationManager.createNotificationChannel(channel) + } + } + + /** + * 创建前台服务通知 + * @return 通知对象 + */ + private fun createNotification(): Notification { + // 创建点击通知时的意图(可以打开应用主界面) + val intent = packageManager.getLaunchIntentForPackage(packageName) + val pendingIntent = PendingIntent.getActivity( + this, + 0, + intent, + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE + ) + + return NotificationCompat.Builder(this, CHANNEL_ID) + .setContentTitle("语音助手正在运行") + .setContentText("正在录制音频,点击返回应用") + .setSmallIcon(android.R.drawable.ic_btn_speak_now) // 使用系统默认麦克风图标 + .setContentIntent(pendingIntent) + .setOngoing(true) // 设置为持续通知,用户无法滑动删除 + .setPriority(NotificationCompat.PRIORITY_LOW) + .setCategory(NotificationCompat.CATEGORY_SERVICE) + .setShowWhen(false) + .build() + } +} \ No newline at end of file diff --git a/local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/tools/SimpleAudioReceiver.kt b/local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/tools/SimpleAudioReceiver.kt index 08e5ae668..ee4e9fed0 100644 --- a/local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/tools/SimpleAudioReceiver.kt +++ b/local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/tools/SimpleAudioReceiver.kt @@ -17,6 +17,7 @@ import android.media.AudioFocusRequest import android.media.AudioAttributes import com.yunqiinnovation.azure_speech.tools.RecordFile import android.os.Build // 新增:用于获取设备信息 +import com.yunqiinnovation.azure_speech.tools.AudioRecordingForegroundService /** * 简单音频接收器类,用于处理音频录制和流传输 @@ -208,6 +209,10 @@ fun startAudioRecord(audioSourceType: AudioSourceType, callback: AudioDataCallba Log.d("tag", "存下来的数据: ${writeQueue?.size}") } + // 启动前台服务 + AudioRecordingForegroundService.startService(context) + Log.d(TAG, "前台服务已启动,开始音频录制") + when (audioSourceType) { AudioSourceType.MICROPHONE -> runMicrophoneCapture() AudioSourceType.EXTERNAL -> runExternalCapture() @@ -218,10 +223,11 @@ fun startAudioRecord(audioSourceType: AudioSourceType, callback: AudioDataCallba pushAudioStream?.close() pushAudioStream = null isWriting.set(false) + // 停止前台服务 + AudioRecordingForegroundService.stopService(context) throw e // 重新抛出异常让调用者处理 } } -// ... existing code ... /** * 配置通话音频模式以优化回声消除 */ @@ -392,6 +398,9 @@ fun startAudioRecord(audioSourceType: AudioSourceType, callback: AudioDataCallba try { writeQueue.clear() isWriting.set(false) + // 停止前台服务 + AudioRecordingForegroundService.stopService(context) + // 停止录音 audioRecord?.stop() @@ -403,7 +412,6 @@ fun startAudioRecord(audioSourceType: AudioSourceType, callback: AudioDataCallba Log.e(TAG, "停止麦克风捕获失败: ${e.message}") } } - /** * 释放音频资源 */ @@ -411,7 +419,9 @@ fun startAudioRecord(audioSourceType: AudioSourceType, callback: AudioDataCallba try { isRunning.set(false) isWriting.set(false) - + // 停止前台服务 + AudioRecordingForegroundService.stopService(context) + Log.d(TAG, "前台服务已停止,音频资源已释放") // 中断并等待捕获线程结束 writeThread?.interrupt() writeThread?.join(300) // 最多等待300ms