Browse Source

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

weicu
lxm 1 year ago
parent
commit
bf288ef298
  1. 62
      lib/data/services/bluetooth_manager.dart
  2. 52
      lib/modules/pairing/controllers/pairing_controller.dart
  3. 2
      local_plugins/chat_api/ios/chat_api/Sources/chat_api/MCPClient.swift
  4. 57
      local_plugins/classic_bluetooth/android/src/main/kotlin/com/yunqiinnovation/classic_bluetooth/ClassicBluetoothHelper.kt
  5. 3
      local_plugins/classic_bluetooth/ios/Classes/ClassicBluetoothHelper.swift
  6. 59
      local_plugins/classic_bluetooth/lib/classic_bluetooth.dart
  7. 6
      macos/Podfile.lock
  8. 2
      pubspec.yaml

62
lib/data/services/bluetooth_manager.dart

@ -9,59 +9,58 @@ export 'package:classic_bluetooth/classic_bluetooth.dart' show BluetoothDevice;
/// 蓝牙服务 - 管理经典蓝牙服务 /// 蓝牙服务 - 管理经典蓝牙服务
class BluetoothManager extends GetxService { class BluetoothManager extends GetxService {
static BluetoothManager get to => Get.find<BluetoothManager>(); static BluetoothManager get to => Get.find<BluetoothManager>();
// 蓝牙状态 // 蓝牙状态
final _bluetoothState = 'unknown'.obs; final _bluetoothState = 'unknown'.obs;
String get bluetoothState => _bluetoothState.value; String get bluetoothState => _bluetoothState.value;
RxString get bluetoothStateRx => _bluetoothState; RxString get bluetoothStateRx => _bluetoothState;
// 当前经典蓝牙设备 // 当前经典蓝牙设备
final Rx<BluetoothDevice?> currentDeviceRx = Rx<BluetoothDevice?>(null); final Rx<BluetoothDevice?> currentDeviceRx = Rx<BluetoothDevice?>(null);
BluetoothDevice? get currentDevice => currentDeviceRx.value; BluetoothDevice? get currentDevice => currentDeviceRx.value;
// 订阅 // 订阅
StreamSubscription<BluetoothEvent>? _bluetoothSubscription; StreamSubscription<BluetoothEvent>? _bluetoothSubscription;
/// 初始化 /// 初始化
@override @override
void onInit() { void onInit() {
super.onInit(); super.onInit();
Logger.info('蓝牙服务初始化'); Logger.info('蓝牙服务初始化');
// 设置蓝牙事件监听 // 设置蓝牙事件监听
_setupBluetoothListener(); _setupBluetoothListener();
// 主动检查当前已连接的设备和蓝牙状态 // 主动检查当前已连接的设备和蓝牙状态
Future.delayed(Duration.zero, () async { Future.delayed(Duration.zero, () async {
Logger.info('检查初始蓝牙连接状态'); Logger.info('检查初始蓝牙连接状态');
// 先检查蓝牙是否开启 // 先检查蓝牙是否开启
await isBluetoothEnabled(); await isBluetoothEnabled();
// 然后刷新设备连接 // 然后刷新设备连接
await refreshDeviceConnection(); await refreshDeviceConnection();
}); });
} }
/// 释放资源 /// 释放资源
@override @override
void onClose() { void onClose() {
_bluetoothSubscription?.cancel(); _bluetoothSubscription?.cancel();
super.onClose(); super.onClose();
} }
/// 设置经典蓝牙事件监听 /// 设置经典蓝牙事件监听
void _setupBluetoothListener() { void _setupBluetoothListener() {
_bluetoothSubscription?.cancel(); _bluetoothSubscription?.cancel();
try { try {
_bluetoothSubscription = ClassicBluetooth.events.listen( _bluetoothSubscription = ClassicBluetooth.events.listen(
(event) { (event) {
if (event.type == 'stateChanged' && event.state != null) { if (event.type == 'stateChanged' && event.state != null) {
_bluetoothState.value = event.state!; _bluetoothState.value = event.state!;
Logger.info('蓝牙状态变更: ${event.state}'); Logger.info('蓝牙状态变更: ${event.state}');
// 如果蓝牙关闭,清除设备 // 如果蓝牙关闭,清除设备
if (event.state == 'off') { if (event.state == 'off') {
currentDeviceRx.value = null; currentDeviceRx.value = null;
@ -69,17 +68,19 @@ class BluetoothManager extends GetxService {
} else if (event.type == 'deviceConnected' && event.device != null) { } else if (event.type == 'deviceConnected' && event.device != null) {
// 直接从事件中获取设备信息 // 直接从事件中获取设备信息
final newDevice = event.device!; final newDevice = event.device!;
// 更新当前设备 // 更新当前设备
currentDeviceRx.value = newDevice; currentDeviceRx.value = newDevice;
Logger.info('经典蓝牙设备已连接: ${newDevice.name} (${newDevice.address})'); Logger.info('经典蓝牙设备已连接: ${newDevice.name} (${newDevice.address})');
} else if (event.type == 'deviceDisconnected' && event.device != null) { } else if (event.type == 'deviceDisconnected' &&
event.device != null) {
// 直接从事件中获取设备信息 // 直接从事件中获取设备信息
final disconnectedDevice = event.device!; final disconnectedDevice = event.device!;
Logger.info('经典蓝牙设备已断开: ${disconnectedDevice.name} (${disconnectedDevice.address})'); Logger.info(
'经典蓝牙设备已断开: ${disconnectedDevice.name} (${disconnectedDevice.address})');
// 如果断开的是当前设备,清除当前设备 // 如果断开的是当前设备,清除当前设备
if (currentDeviceRx.value?.address == disconnectedDevice.address) { if (currentDeviceRx.value?.address == disconnectedDevice.address) {
currentDeviceRx.value = null; currentDeviceRx.value = null;
@ -95,34 +96,36 @@ class BluetoothManager extends GetxService {
Logger.error('设置蓝牙事件监听器失败: ${e.toString()}'); Logger.error('设置蓝牙事件监听器失败: ${e.toString()}');
} }
} }
/// 刷新设备连接状态 /// 刷新设备连接状态
Future<void> refreshDeviceConnection() async { Future<void> refreshDeviceConnection() async {
try { try {
// 如果BLE未连接,则检查经典蓝牙连接 // 如果BLE未连接,则检查经典蓝牙连接
final devices = await ClassicBluetooth.getConnectedDevices(); final devices = await ClassicBluetooth.getConnectedDevices();
if (devices.isNotEmpty) { if (devices.isNotEmpty) {
for (var device in devices) {
if (device.isActive) {
// 如果有活动设备,直接使用它
currentDeviceRx.value = device;
Logger.info('已连接经典蓝牙设备: ${device.name} (${device.address})');
return;
}
}
final newDevice = devices.first; final newDevice = devices.first;
// 更新当前设备
currentDeviceRx.value = newDevice; currentDeviceRx.value = newDevice;
Logger.info('已连接经典蓝牙设备: ${newDevice.name} (${newDevice.address})'); Logger.info('已连接经典蓝牙设备: ${newDevice.name} (${newDevice.address})');
return; return;
} }
// 没有任何连接 // 没有任何连接
currentDeviceRx.value = null; currentDeviceRx.value = null;
Logger.info('未找到已连接的蓝牙设备'); Logger.info('未找到已连接的蓝牙设备');
} catch (e) { } catch (e) {
Logger.error('刷新设备连接失败: ${e.toString()}'); Logger.error('刷新设备连接失败: ${e.toString()}');
} }
} }
/// 检查蓝牙是否已启用 /// 检查蓝牙是否已启用
Future<bool> isBluetoothEnabled() async { Future<bool> isBluetoothEnabled() async {
try { try {
@ -134,5 +137,4 @@ class BluetoothManager extends GetxService {
return false; return false;
} }
} }
}
}

52
lib/modules/pairing/controllers/pairing_controller.dart

@ -490,9 +490,11 @@ class PairingController extends GetxController {
} else { } else {
final authcode_open = AppConfig.env("APP_AUTHCODE_OPEN") ?? ""; final authcode_open = AppConfig.env("APP_AUTHCODE_OPEN") ?? "";
if (authcode_open == "true") { if (authcode_open == "true") {
Logger.i(_tag, '进入手动验证流: ${device.name} (${device.address})');
//验证失败 进入手动授权 //验证失败 进入手动授权
_transition(PairingState.manualbinding); _transition(PairingState.manualbinding);
} else { } else {
Logger.i(_tag, '设备不支持: ${device.name} (${device.address})');
_setError(ErrorType.deviceNotMatch, _setError(ErrorType.deviceNotMatch,
'pleaseConnectDeepSoundDevice'.tr); //请连接DeepSound设备 'pleaseConnectDeepSoundDevice'.tr); //请连接DeepSound设备
} }
@ -610,34 +612,42 @@ class PairingController extends GetxController {
Future<int> authbindingDevice(BluetoothDevice device) async { Future<int> authbindingDevice(BluetoothDevice device) async {
// 检查设备名称是否包含 DeepSound 或 DeapSound // 检查设备名称是否包含 DeepSound 或 DeapSound
final deviceName = device.name; final deviceName = device.name;
DBProduct? product;
// 检查是否已有BLE绑定的设备 // 检查是否已有BLE绑定的设备
final product = AppConfig.config.products /// 检查是否已有BLE绑定的设备
.firstWhere((d) => d.devicename == device.name); if (AppConfig.config.products.isNotEmpty) {
final match = AppConfig.config.products.where(
(d) => d.devicename == device.name,
);
product = match.isNotEmpty ? match.first : null;
}
final names = AppConfig.allproductNames(); final names = AppConfig.allproductNames();
// 检查是否已有绑定的设备 // 检查是否已有绑定的设备
print("device.address${device.address}"); print("device.address${device.address}");
var existingDevice = User.instance.devices.firstWhere( if (User.instance.devices.isNotEmpty) {
(d) => final existingDevice = User.instance.devices.firstWhere(
d.devicetype == 1 && (d) =>
d.devicename == device.name && d.devicetype == 1 &&
d.devicemac == device.address, d.devicename == device.name &&
orElse: () => UserDevice( d.devicemac == device.address,
id: -1, orElse: () => UserDevice(
uid: '', id: -1,
devicetype: -1, uid: '',
devicename: '', devicetype: -1,
devicemac: '', devicename: '',
license: ''), devicemac: '',
); license: ''),
if (existingDevice.id != -1) { );
// 已经绑定 if (existingDevice.id != -1) {
return 9; // 已经绑定
return 9;
}
} }
if (Platform.isAndroid) { if (Platform.isAndroid) {
// 检查是否已有BLE绑定的设备 // 检查是否已有BLE绑定的设备
existingDevice = User.instance.devices.firstWhere( final existingDevice = User.instance.devices.firstWhere(
(d) => (d) =>
d.devicetype == 2 && d.devicetype == 2 &&
d.devicename == device.name && d.devicename == device.name &&
@ -751,7 +761,7 @@ class PairingController extends GetxController {
} }
await Future.delayed(Duration(seconds: 1)); await Future.delayed(Duration(seconds: 1));
} }
if (product.devicetype == 2) { if (product != null && product.devicetype == 2) {
//ble设备 必须要扫描到结果 //ble设备 必须要扫描到结果
return 0; return 0;
} else { } else {

2
local_plugins/chat_api/ios/chat_api/Sources/chat_api/MCPClient.swift

@ -294,7 +294,7 @@ public class MCPSubClient {
isConnectedFlag = false isConnectedFlag = false
// 启动重连逻辑 // 启动重连逻辑
startReconnection() // startReconnection()
} }
} }
} }

57
local_plugins/classic_bluetooth/android/src/main/kotlin/com/yunqiinnovation/classic_bluetooth/ClassicBluetoothHelper.kt

@ -174,11 +174,16 @@ class ClassicBluetoothHelper(private val context: Context) {
// 获取已连接设备 // 获取已连接设备
val connectedDevices = proxy.connectedDevices val connectedDevices = proxy.connectedDevices
// 获取当前活跃设备
val activeDevice = getActiveDevice(proxy, profileId)
// 转换为设备信息列表 // 转换为设备信息列表
val devicesList = connectedDevices.map { device -> val devicesList = connectedDevices.map { device ->
val isActive = activeDevice?.address == device.address
mapOf( mapOf(
"name" to (device.name ?: "未知设备"), "name" to (device.name ?: "未知设备"),
"address" to device.address "address" to device.address,
"isActive" to isActive.toString()
) )
} }
@ -186,6 +191,54 @@ class ClassicBluetoothHelper(private val context: Context) {
callback.onSuccess(devicesList) callback.onSuccess(devicesList)
} }
/**
* 获取当前活跃的蓝牙设备
*/
private fun getActiveDevice(proxy: BluetoothProfile, profileId: Int): BluetoothDevice? {
return try {
when (profileId) {
BluetoothProfile.A2DP -> {
// 使用反射调用 getActiveDevice 方法
val method = proxy.javaClass.getDeclaredMethod("getActiveDevice")
method.isAccessible = true
method.invoke(proxy) as? BluetoothDevice
}
BluetoothProfile.HEADSET -> {
// 对于 HEADSET 配置文件,也尝试获取活跃设备
val method = proxy.javaClass.getDeclaredMethod("getActiveDevice")
method.isAccessible = true
method.invoke(proxy) as? BluetoothDevice
}
else -> null
}
} catch (e: Exception) {
Log.w(TAG, "无法获取活跃设备: ${e.message}")
// 如果无法获取活跃设备,可以尝试其他方法
// 比如检查连接状态或音频焦点
getActiveDeviceFallback(proxy)
}
}
/**
* 获取活跃设备的备用方法
*/
private fun getActiveDeviceFallback(proxy: BluetoothProfile): BluetoothDevice? {
return try {
val connectedDevices = proxy.connectedDevices
// 如果只有一个连接的设备,则认为它是活跃的
if (connectedDevices.size == 1) {
connectedDevices.first()
} else {
// 如果有多个设备,可以通过其他方式判断
// 这里简单返回第一个,实际应用中可能需要更复杂的逻辑
connectedDevices.firstOrNull()
}
} catch (e: Exception) {
Log.w(TAG, "备用方法获取活跃设备失败: ${e.message}")
null
}
}
/** /**
* 开始监听蓝牙状态变化 * 开始监听蓝牙状态变化
* @param callback 蓝牙状态回调接口 * @param callback 蓝牙状态回调接口
@ -361,4 +414,4 @@ class ClassicBluetoothHelper(private val context: Context) {
TURNING_ON, TURNING_ON,
TURNING_OFF TURNING_OFF
} }
} }

3
local_plugins/classic_bluetooth/ios/Classes/ClassicBluetoothHelper.swift

@ -75,7 +75,8 @@ class ClassicBluetoothHelper: NSObject {
for output in outputs where isBluetoothOutput(output.portType) { for output in outputs where isBluetoothOutput(output.portType) {
let device: [String: String] = [ let device: [String: String] = [
"name": output.portName, "name": output.portName,
"address": cleanDeviceAddress(output.uid) "address": cleanDeviceAddress(output.uid),
"isActive": "true" // 在 currentRoute.outputs 中的蓝牙设备都是活跃的
] ]
result.append(device) result.append(device)
} }

59
local_plugins/classic_bluetooth/lib/classic_bluetooth.dart

@ -3,34 +3,35 @@ import 'package:flutter/services.dart';
/// 经典蓝牙插件 /// 经典蓝牙插件
class ClassicBluetooth { class ClassicBluetooth {
static const MethodChannel _methodChannel = static const MethodChannel _methodChannel =
MethodChannel('com.yunqiinnovation.classic_bluetooth/methods'); MethodChannel('com.yunqiinnovation.classic_bluetooth/methods');
static const EventChannel _eventChannel = static const EventChannel _eventChannel =
EventChannel('com.yunqiinnovation.classic_bluetooth/events'); EventChannel('com.yunqiinnovation.classic_bluetooth/events');
static Stream<BluetoothEvent>? _eventStream; static Stream<BluetoothEvent>? _eventStream;
/// 获取已连接的蓝牙设备 /// 获取已连接的蓝牙设备
static Future<List<BluetoothDevice>> getConnectedDevices() async { static Future<List<BluetoothDevice>> getConnectedDevices() async {
final List<dynamic> devices = await _methodChannel.invokeMethod('getConnectedDevices'); final List<dynamic> devices =
await _methodChannel.invokeMethod('getConnectedDevices');
return devices.map((device) => BluetoothDevice.fromJson(device)).toList(); return devices.map((device) => BluetoothDevice.fromJson(device)).toList();
} }
/// 检查蓝牙是否已启用 /// 检查蓝牙是否已启用
static Future<bool> isBluetoothEnabled() async { static Future<bool> isBluetoothEnabled() async {
return await _methodChannel.invokeMethod('isBluetoothEnabled'); return await _methodChannel.invokeMethod('isBluetoothEnabled');
} }
/// 监听蓝牙事件(状态变化、设备连接/断开) /// 监听蓝牙事件(状态变化、设备连接/断开)
static Stream<BluetoothEvent> get events { static Stream<BluetoothEvent> get events {
_eventStream ??= _eventChannel _eventStream ??= _eventChannel
.receiveBroadcastStream() .receiveBroadcastStream()
.map((dynamic event) => BluetoothEvent.fromJson(event)) .map((dynamic event) => BluetoothEvent.fromJson(event))
.handleError((error) { .handleError((error) {
print('[ClassicBluetooth] 事件流错误: $error'); print('[ClassicBluetooth] 事件流错误: $error');
}); });
return _eventStream!; return _eventStream!;
} }
} }
@ -39,61 +40,67 @@ class ClassicBluetooth {
class BluetoothDevice { class BluetoothDevice {
final String name; final String name;
final String address; final String address;
final bool isActive;
BluetoothDevice({ BluetoothDevice({
required this.name, required this.name,
required this.address, required this.address,
this.isActive = false,
}); });
factory BluetoothDevice.fromJson(Map<dynamic, dynamic> json) { factory BluetoothDevice.fromJson(Map<dynamic, dynamic> json) {
return BluetoothDevice( return BluetoothDevice(
name: json['name'] as String, name: json['name'] as String,
address: json['address'] as String, address: json['address'] as String,
isActive: json['isActive'] == 'true',
); );
} }
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
return { return {
'name': name, 'name': name,
'address': address, 'address': address,
'isActive': isActive,
}; };
} }
@override @override
String toString() => 'BluetoothDevice(name: $name, address: $address)'; String toString() =>
'BluetoothDevice(name: $name, address: $address, isActive: $isActive)';
} }
/// 简化的蓝牙事件类 /// 简化的蓝牙事件类
class BluetoothEvent { class BluetoothEvent {
/// 事件类型:stateChanged, deviceConnected, deviceDisconnected /// 事件类型:stateChanged, deviceConnected, deviceDisconnected
final String type; final String type;
/// 蓝牙状态: on, off, turningOn, turningOff, unknown /// 蓝牙状态: on, off, turningOn, turningOff, unknown
final String? state; final String? state;
/// 相关的蓝牙设备 /// 相关的蓝牙设备
final BluetoothDevice? device; final BluetoothDevice? device;
/// 事件发生时间 /// 事件发生时间
final DateTime timestamp; final DateTime timestamp;
BluetoothEvent({ BluetoothEvent({
required this.type, required this.type,
this.state, this.state,
this.device, this.device,
required this.timestamp, required this.timestamp,
}); });
factory BluetoothEvent.fromJson(Map<dynamic, dynamic> json) { factory BluetoothEvent.fromJson(Map<dynamic, dynamic> json) {
final String type = json['type'] as String; final String type = json['type'] as String;
final timestamp = DateTime.fromMillisecondsSinceEpoch(json['timestamp'] as int); final timestamp =
DateTime.fromMillisecondsSinceEpoch(json['timestamp'] as int);
// 处理设备相关事件 // 处理设备相关事件
BluetoothDevice? device; BluetoothDevice? device;
if (json.containsKey('device')) { if (json.containsKey('device')) {
device = BluetoothDevice.fromJson(json['device']); device = BluetoothDevice.fromJson(json['device']);
} }
return BluetoothEvent( return BluetoothEvent(
type: type, type: type,
state: json['state'] as String?, state: json['state'] as String?,
@ -101,7 +108,7 @@ class BluetoothEvent {
timestamp: timestamp, timestamp: timestamp,
); );
} }
@override @override
String toString() { String toString() {
if (type == 'stateChanged') { if (type == 'stateChanged') {
@ -112,4 +119,4 @@ class BluetoothEvent {
return 'BluetoothEvent(type: $type)'; return 'BluetoothEvent(type: $type)';
} }
} }
} }

6
macos/Podfile.lock

@ -14,6 +14,8 @@ PODS:
- FlutterMacOS (1.0.0) - FlutterMacOS (1.0.0)
- record_darwin (1.0.0): - record_darwin (1.0.0):
- FlutterMacOS - FlutterMacOS
- sign_in_with_apple (0.0.1):
- FlutterMacOS
- window_size (0.0.2): - window_size (0.0.2):
- FlutterMacOS - FlutterMacOS
@ -26,6 +28,7 @@ DEPENDENCIES:
- flutter_tts (from `Flutter/ephemeral/.symlinks/plugins/flutter_tts/macos`) - flutter_tts (from `Flutter/ephemeral/.symlinks/plugins/flutter_tts/macos`)
- FlutterMacOS (from `Flutter/ephemeral`) - FlutterMacOS (from `Flutter/ephemeral`)
- record_darwin (from `Flutter/ephemeral/.symlinks/plugins/record_darwin/macos`) - record_darwin (from `Flutter/ephemeral/.symlinks/plugins/record_darwin/macos`)
- sign_in_with_apple (from `Flutter/ephemeral/.symlinks/plugins/sign_in_with_apple/macos`)
- window_size (from `Flutter/ephemeral/.symlinks/plugins/window_size/macos`) - window_size (from `Flutter/ephemeral/.symlinks/plugins/window_size/macos`)
EXTERNAL SOURCES: EXTERNAL SOURCES:
@ -45,6 +48,8 @@ EXTERNAL SOURCES:
:path: Flutter/ephemeral :path: Flutter/ephemeral
record_darwin: record_darwin:
:path: Flutter/ephemeral/.symlinks/plugins/record_darwin/macos :path: Flutter/ephemeral/.symlinks/plugins/record_darwin/macos
sign_in_with_apple:
:path: Flutter/ephemeral/.symlinks/plugins/sign_in_with_apple/macos
window_size: window_size:
:path: Flutter/ephemeral/.symlinks/plugins/window_size/macos :path: Flutter/ephemeral/.symlinks/plugins/window_size/macos
@ -57,6 +62,7 @@ SPEC CHECKSUMS:
flutter_tts: ae915565cc6948444b513acc8ee021993281e027 flutter_tts: ae915565cc6948444b513acc8ee021993281e027
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
record_darwin: 30509266ae213af8afdb09a8ae7467cb64c1377e record_darwin: 30509266ae213af8afdb09a8ae7467cb64c1377e
sign_in_with_apple: 6673c03c9e3643f6c8d33601943fbfa9ae99f94e
window_size: 4bd15034e6e3d0720fd77928a7c42e5492cfece9 window_size: 4bd15034e6e3d0720fd77928a7c42e5492cfece9
PODFILE CHECKSUM: 8c4442624fc136b0afb378a2502958e01c05d073 PODFILE CHECKSUM: 8c4442624fc136b0afb378a2502958e01c05d073

2
pubspec.yaml

@ -1,7 +1,7 @@
name: voitrans name: voitrans
description: "Voitrans - AI Voice Assistant." description: "Voitrans - AI Voice Assistant."
publish_to: "none" publish_to: "none"
version: 1.0.3+9 version: 1.0.3+11
environment: environment:
sdk: ">=3.3.0 <4.0.0" sdk: ">=3.3.0 <4.0.0"

Loading…
Cancel
Save