Browse Source

Merge commit '1503669ac75c64450f966dbbdff3f8410be87ea5' into new_dev

newdev_shunjiawei
lxm 1 year ago
parent
commit
1f5bd3a9bc
  1. 12
      lib/data/services/ble_manager.dart
  2. 1
      lib/data/services/bluetooth_manager.dart
  3. 39
      lib/modules/devices/controllers/devices_controller.dart
  4. 84
      lib/modules/devices/views/devices_view.dart
  5. 2
      lib/modules/home/controllers/home_controller.dart
  6. 8
      lib/modules/pairing/controllers/pairing_controller.dart
  7. 17
      local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/tools/SimpleAudioReceiver.kt
  8. 10
      local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleConst.kt
  9. 63
      local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleService.kt
  10. 10
      local_plugins/ble_service/ios/ble_service/Sources/ble_service/BleService.swift
  11. 2
      local_plugins/classic_bluetooth/android/build.gradle.kts
  12. 24
      local_plugins/classic_bluetooth/android/src/main/kotlin/com/yunqiinnovation/classic_bluetooth/ClassicBluetoothPlugin.kt
  13. 8
      local_plugins/classic_bluetooth/ios/classic_bluetooth/Package.swift
  14. 34
      local_plugins/classic_bluetooth/ios/classic_bluetooth/Sources/classic_bluetooth/ClassicBluetoothHelper.swift
  15. 194
      local_plugins/qq_music/android/src/main/kotlin/com/yunqiinnovation/qq_music/QQMusicSingleton.kt

12
lib/data/services/ble_manager.dart

@ -113,7 +113,7 @@ class BleManager extends GetxService {
// 经典蓝牙实例 // 经典蓝牙实例
// 当前连接的设备信息 // 当前连接的设备信息
String? _currentDeviceAddress; String? currentDeviceAddress;
String? _currentDeviceName; String? _currentDeviceName;
// BLE连接状态 // BLE连接状态
@ -513,7 +513,7 @@ class BleManager extends GetxService {
(firmwareVersion != null || batteryLevel != null)) { (firmwareVersion != null || batteryLevel != null)) {
Logger.info('lxm--设备信息不存在,创建新的设备信息对象'); Logger.info('lxm--设备信息不存在,创建新的设备信息对象');
// 使用保存的设备信息创建新的DeviceInfo对象 // 使用保存的设备信息创建新的DeviceInfo对象
String deviceAddress = _currentDeviceAddress ?? ""; String deviceAddress = currentDeviceAddress ?? "";
String deviceName = _currentDeviceName ?? ""; String deviceName = _currentDeviceName ?? "";
Logger.info('lxm--创建新的设备信息对象: address=$deviceAddress, name=$deviceName'); Logger.info('lxm--创建新的设备信息对象: address=$deviceAddress, name=$deviceName');
@ -561,7 +561,7 @@ class BleManager extends GetxService {
void clearDeviceInfo() { void clearDeviceInfo() {
Logger.info('清除设备信息'); Logger.info('清除设备信息');
deviceInfo.value = null; deviceInfo.value = null;
_currentDeviceAddress = null; currentDeviceAddress = null;
_currentDeviceName = null; _currentDeviceName = null;
} }
@ -573,7 +573,7 @@ class BleManager extends GetxService {
// 确保设备信息对象存在 // 确保设备信息对象存在
if (deviceInfo.value == null && _isConnected.value) { if (deviceInfo.value == null && _isConnected.value) {
// 创建初始设备信息对象 // 创建初始设备信息对象
String deviceAddress = _currentDeviceAddress ?? ""; String deviceAddress = currentDeviceAddress ?? "";
String deviceName = _currentDeviceName ?? ""; String deviceName = _currentDeviceName ?? "";
Logger.info('初始化设备信息对象: address=$deviceAddress, name=$deviceName'); Logger.info('初始化设备信息对象: address=$deviceAddress, name=$deviceName');
@ -1036,12 +1036,12 @@ class BleManager extends GetxService {
try { try {
// 保存设备地址信息 // 保存设备地址信息
if (macAddress != null) { if (macAddress != null) {
_currentDeviceAddress = macAddress; currentDeviceAddress = macAddress;
// 从蓝牙管理器获取设备名称 // 从蓝牙管理器获取设备名称
final bluetoothManager = Get.find<BluetoothManager>(); final bluetoothManager = Get.find<BluetoothManager>();
_currentDeviceName = bluetoothManager.currentDevice?.name ?? ""; _currentDeviceName = bluetoothManager.currentDevice?.name ?? "";
Logger.info( Logger.info(
'保存设备信息: address=$_currentDeviceAddress, name=$_currentDeviceName'); '保存设备信息: address=$currentDeviceAddress, name=$_currentDeviceName');
} }
return await _bleService.registerBackgroundScan(macAddress: macAddress); return await _bleService.registerBackgroundScan(macAddress: macAddress);

1
lib/data/services/bluetooth_manager.dart

@ -46,6 +46,7 @@ class BluetoothManager extends GetxService {
/// 释放资源 /// 释放资源
@override @override
void onClose() { void onClose() {
print("蓝牙服务关闭");
_bluetoothSubscription?.cancel(); _bluetoothSubscription?.cancel();
super.onClose(); super.onClose();
} }

39
lib/modules/devices/controllers/devices_controller.dart

@ -24,29 +24,36 @@ class DevicesController extends GetxController {
@override @override
void onInit() { void onInit() {
super.onInit(); super.onInit();
print("初始化设备管理1");
_bleManager = Get.find<BleManager>(); _bleManager = Get.find<BleManager>();
print("初始化设备管理2");
// 在初始化时加载设备数据 // 在初始化时加载设备数据
_loadDevices(); _loadDevices();
} }
// 加载设备数据 // 加载设备数据
void _loadDevices() async { void _loadDevices() async {
devices.clear(); Logger.info("加载设备");
final response = await Api.getdevices(); final response = await Api.getdevices();
if (response != null) { if (response != null) {
var devices = (response['devices'] as List<dynamic>) if (response['devices'] != null) {
.map((e) => UserDevice.fromJson(e as Map<String, dynamic>)) var devices = (response['devices'] as List<dynamic>)
.toList(); .map((e) => UserDevice.fromJson(e as Map<String, dynamic>))
User.instance.devices = devices; .toList();
User.instance.devices = devices;
} else {
User.instance.devices = [];
}
Logger.info("加载设备 ${User.instance.devices.length}");
} }
devices.clear();
for (var device in User.instance.devices) { for (var device in User.instance.devices) {
if (!devices.containsKey(device.devicetype)) { if (!devices.containsKey(device.devicetype)) {
devices[device.devicetype] = []; devices[device.devicetype] = [];
} }
devices[device.devicetype]!.add(device); devices[device.devicetype]!.add(device);
} }
devices.refresh();
} }
@override @override
@ -59,12 +66,22 @@ class DevicesController extends GetxController {
final response = await Api.unbinddevice({ final response = await Api.unbinddevice({
"id": device.id, "id": device.id,
}); });
Logger.info("解绑设备 $response");
if (response != null) { if (response != null) {
// 响应式更新 // 响应式更新
devices[device.devicetype]?.remove(device); User.instance.devices.remove(device);
devices.refresh(); // 触发UI更新 devices.clear();
for (var device in User.instance.devices) {
if (device.devicemac == _bleManager.deviceInfo.value?.address) { if (!devices.containsKey(device.devicetype)) {
devices[device.devicetype] = [];
}
devices[device.devicetype]!.add(device);
}
devices.refresh();
Logger.info(
"解绑设备 ${device.devicemac} 当前使用设备:${_bleManager.currentDeviceAddress}");
if (device.devicemac == _bleManager.currentDeviceAddress) {
Logger.info("解绑设备 $device 重新匹配!");
User.instance.removeUserDevice(device.id); User.instance.removeUserDevice(device.id);
_bleManager.clearPairedDevices(); _bleManager.clearPairedDevices();
_bleManager.disconnect(); _bleManager.disconnect();

84
lib/modules/devices/views/devices_view.dart

@ -118,57 +118,51 @@ class DevicesView extends GetView<DevicesController> {
} }
Widget _buildDevicesList(bool isDarkMode) { Widget _buildDevicesList(bool isDarkMode) {
final devices = User.instance.devices; return Obx(() {
// 使用控制器的响应式数据,这会触发控制器的实例化
final groupedDevices = controller.devices;
if (devices.isEmpty) { if (groupedDevices.isEmpty) {
return _buildEmptyState(isDarkMode); return _buildEmptyState(isDarkMode);
}
// 按设备类型分组
final Map<DeviceType, List<UserDevice>> groupedDevices = {};
for (var device in devices) {
final deviceType = DeviceType.fromValue(device.devicetype);
if (!groupedDevices.containsKey(deviceType)) {
groupedDevices[deviceType] = [];
} }
groupedDevices[deviceType]!.add(device);
}
return ListView( return ListView(
padding: EdgeInsets.all(20.w), padding: EdgeInsets.all(20.w),
children: [ children: [
// 遍历每个设备类型 // 遍历每个设备类型
...groupedDevices.entries.map((entry) { ...groupedDevices.entries.map((entry) {
final deviceType = entry.key; final deviceTypeValue = entry.key;
final deviceList = entry.value; final deviceList = entry.value;
final deviceType = DeviceType.fromValue(deviceTypeValue);
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
// 设备类型标题 // 设备类型标题
Padding( Padding(
padding: EdgeInsets.only(bottom: 12.h), padding: EdgeInsets.only(bottom: 12.h),
child: Text( child: Text(
deviceType.displayName, deviceType.displayName,
style: TextStyle( style: TextStyle(
fontSize: 16.sp, fontSize: 16.sp,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: isDarkMode ? Colors.white : Colors.black87, color: isDarkMode ? Colors.white : Colors.black87,
),
), ),
), ),
), // 该类型下的设备列表
// 该类型下的设备列表 ...deviceList.map((device) => _buildDeviceCard(
...deviceList.map((device) => _buildDeviceCard( device: device,
device: device, deviceType: deviceType,
deviceType: deviceType, isDarkMode: isDarkMode,
isDarkMode: isDarkMode, )),
)), SizedBox(height: 20.h),
SizedBox(height: 20.h), ],
], );
); }).toList(),
}).toList(), ],
], );
); });
} }
Widget _buildEmptyState(bool isDarkMode) { Widget _buildEmptyState(bool isDarkMode) {

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

@ -171,7 +171,7 @@ class HomeController extends GetxController {
Future<void> _checkConnectionStatus() async { Future<void> _checkConnectionStatus() async {
try { try {
final currentBleState = await _bleManager.getConnectionState(); final currentBleState = await _bleManager.getConnectionState();
print('fdp--_checkConnectionStatus--currentBleState: $currentBleState');
bool hasClassicBluetooth = currentDevice != null; bool hasClassicBluetooth = currentDevice != null;
bool hasBleConnection = currentBleState == 2; bool hasBleConnection = currentBleState == 2;
bool wasConnected = _isFullyConnected.value; bool wasConnected = _isFullyConnected.value;

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

@ -922,6 +922,7 @@ class PairingController extends GetxController with WidgetsBindingObserver {
} }
List<int> macBytes = []; List<int> macBytes = [];
List<int> tempBytes = [];
if (Platform.isIOS) { if (Platform.isIOS) {
for (int i = 7; i <= 12; i++) { for (int i = 7; i <= 12; i++) {
// 每个字节异或0x5A // 每个字节异或0x5A
@ -930,6 +931,7 @@ class PairingController extends GetxController with WidgetsBindingObserver {
} }
} else if (Platform.isAndroid) { } else if (Platform.isAndroid) {
for (int i = 9; i <= 14; i++) { for (int i = 9; i <= 14; i++) {
tempBytes.add(bytes[i]);
// 每个字节异或0x5A // 每个字节异或0x5A
int decodedByte = bytes[i] ^ 0x5A; int decodedByte = bytes[i] ^ 0x5A;
macBytes.add(decodedByte); macBytes.add(decodedByte);
@ -940,13 +942,15 @@ class PairingController extends GetxController with WidgetsBindingObserver {
String extractedMac = macBytes String extractedMac = macBytes
.map((b) => b.toRadixString(16).padLeft(2, '0').toUpperCase()) .map((b) => b.toRadixString(16).padLeft(2, '0').toUpperCase())
.join(':'); .join(':');
String tempMac = tempBytes
.map((b) => b.toRadixString(16).padLeft(2, '0').toUpperCase())
.join(':');
// 格式化当前设备MAC地址(确保格式一致) // 格式化当前设备MAC地址(确保格式一致)
String formattedDeviceMac = deviceMac.replaceAll(':', '').toUpperCase(); String formattedDeviceMac = deviceMac.replaceAll(':', '').toUpperCase();
String formattedExtractedMac = String formattedExtractedMac =
extractedMac.replaceAll(':', '').toUpperCase(); extractedMac.replaceAll(':', '').toUpperCase();
print('设备MAC: $deviceMac, 提取的MAC: $extractedMac'); print('设备MAC: $deviceMac, 提取的MAC: $extractedMac tempMac: $tempMac');
// 对比MAC地址 // 对比MAC地址
if (formattedDeviceMac == formattedExtractedMac) { if (formattedDeviceMac == formattedExtractedMac) {

17
local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/tools/SimpleAudioReceiver.kt

@ -237,8 +237,21 @@ fun startAudioRecord(audioSourceType: AudioSourceType, callback: AudioDataCallba
// 设置通话模式 - 这是关键! // 设置通话模式 - 这是关键!
audioManager?.mode = AudioManager.MODE_IN_COMMUNICATION audioManager?.mode = AudioManager.MODE_IN_COMMUNICATION
// 启用扬声器(如果需要外放) // 检查是否有蓝牙音频设备连接
audioManager?.isSpeakerphoneOn = true val isBluetoothConnected = audioManager?.isBluetoothA2dpOn == true
// 只有在没有蓝牙设备连接时才启用扬声器
if (!isBluetoothConnected) {
audioManager?.isSpeakerphoneOn = true
Log.d("AudioMode", "未检测到蓝牙设备,启用扬声器")
} else {
audioManager?.isSpeakerphoneOn = false
// 启用蓝牙SCO连接
audioManager?.isBluetoothScoOn = true
audioManager?.startBluetoothSco()
Log.d("AudioMode", "检测到蓝牙设备,使用蓝牙音频")
}
// 请求音频焦点 // 请求音频焦点
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {

10
local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleConst.kt

@ -39,11 +39,11 @@ object BleConst {
const val FOREGROUND_ID = 0xBEE const val FOREGROUND_ID = 0xBEE
// ================== 连接状态 ================== // ================== 连接状态 ==================
const val STATE_DISCONNECTED = 0 const val STATE_DISCONNECTED = 0//未连接
const val STATE_CONNECTING = 1 const val STATE_CONNECTING = 1//已断开连接
const val STATE_CONNECTED = 2 const val STATE_CONNECTED = 2//已连接
const val STATE_DISCONNECTING = 3 const val STATE_DISCONNECTING = 3//连接中
const val STATE_ERROR = 4 const val STATE_ERROR = 4//连接错误
// ================== 广播厂商信息 ================== // ================== 广播厂商信息 ==================
/** 厂商ID - 文档中定义为0xABEF */ /** 厂商ID - 文档中定义为0xABEF */

63
local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleService.kt

@ -346,7 +346,7 @@ object BleService {
} }
val filter = ScanFilter.Builder() val filter = ScanFilter.Builder()
//.setServiceUuid(ParcelUuid(uuid)) .setServiceUuid(ParcelUuid(uuid))
.build() .build()
filters.add(filter) filters.add(filter)
Log.d(TAG, "添加扫描过滤器UUID: $uuid") Log.d(TAG, "添加扫描过滤器UUID: $uuid")
@ -475,16 +475,7 @@ object BleService {
ret.add(info) ret.add(info)
} }
} }
} else {
// 如果没找到匹配设备名的结果,返回所有扫描到的设备信息
for (result in scanResults) {
val info = printDeviceInfo(result)
if (info != null) {
ret.add(info)
}
}
} }
return ret return ret
} }
@ -595,6 +586,7 @@ object BleService {
* 断开当前连接 * 断开当前连接
*/ */
fun disconnect() { fun disconnect() {
Log.d(TAG, "主动断开当前连接")
bluetoothGatt?.disconnect() bluetoothGatt?.disconnect()
updateConnectionState(BleConst.STATE_DISCONNECTING) updateConnectionState(BleConst.STATE_DISCONNECTING)
} }
@ -625,6 +617,7 @@ object BleService {
* 更新连接状态并通知回调 * 更新连接状态并通知回调
*/ */
private fun updateConnectionState(state: Int) { private fun updateConnectionState(state: Int) {
Log.d(TAG, "fdp--------- 更新连接状态: $state")
connectionState.postValue(state) connectionState.postValue(state)
notifyConnectionStateChanged(state) notifyConnectionStateChanged(state)
} }
@ -657,17 +650,24 @@ object BleService {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
try { try {
// 首先尝试读取当前PHY状态 // 首先尝试读取当前PHY状态
g.readPhy() val readResult = g.readPhy()
Log.d(TAG, "读取PHY状态请求结果: $readResult")
// 然后请求设置2M PHY // 延迟一段时间后再设置PHY,确保readPhy完成
g.setPreferredPhy( Handler(Looper.getMainLooper()).postDelayed({
BluetoothDevice.PHY_LE_2M_MASK, // TX PHY: 2M try {
BluetoothDevice.PHY_LE_2M_MASK, // RX PHY: 2M val setResult = g.setPreferredPhy(
BluetoothDevice.PHY_OPTION_NO_PREFERRED BluetoothDevice.PHY_LE_2M_MASK, // TX PHY: 2M
) BluetoothDevice.PHY_LE_2M_MASK, // RX PHY: 2M
Log.d(TAG, "已请求设置PHY为2M") BluetoothDevice.PHY_OPTION_NO_PREFERRED
)
Log.d(TAG, "设置PHY为2M请求结果: $setResult")
} catch (e: Exception) {
Log.w(TAG, "设置PHY失败: ${e.message}")
}
}, 500) // 延迟500ms
} catch (e: Exception) { } catch (e: Exception) {
Log.w(TAG, "设置PHY失败: ${e.message},将使用默认1M PHY") Log.w(TAG, "读取PHY失败: ${e.message},将使用默认1M PHY")
} }
} else { } else {
Log.w(TAG, "当前Android版本不支持PHY设置(需要API 26+),使用默认1M PHY") Log.w(TAG, "当前Android版本不支持PHY设置(需要API 26+),使用默认1M PHY")
@ -678,11 +678,13 @@ object BleService {
newState == BluetoothProfile.STATE_DISCONNECTED -> { newState == BluetoothProfile.STATE_DISCONNECTED -> {
updateConnectionState(BleConst.STATE_DISCONNECTED) updateConnectionState(BleConst.STATE_DISCONNECTED)
Log.d(TAG, "监听到已断开连接")
disconnectGatt() disconnectGatt()
} }
else -> { else -> {
updateConnectionState(BleConst.STATE_ERROR) updateConnectionState(BleConst.STATE_ERROR)
Log.d(TAG, "监听到连接错误")
disconnectGatt() disconnectGatt()
} }
} }
@ -708,6 +710,7 @@ object BleService {
*/ */
override fun onPhyUpdate(gatt: BluetoothGatt, txPhy: Int, rxPhy: Int, status: Int) { override fun onPhyUpdate(gatt: BluetoothGatt, txPhy: Int, rxPhy: Int, status: Int) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Log.d(TAG, "PHY更新回调: status=$status, txPhy=$txPhy, rxPhy=$rxPhy")
when (status) { when (status) {
BluetoothGatt.GATT_SUCCESS -> { BluetoothGatt.GATT_SUCCESS -> {
val txPhyStr = getPhyString(txPhy) val txPhyStr = getPhyString(txPhy)
@ -721,7 +724,7 @@ object BleService {
Log.w(TAG, "PHY更新出现一般性错误,可能是设备兼容性问题") Log.w(TAG, "PHY更新出现一般性错误,可能是设备兼容性问题")
} }
else -> { else -> {
Log.w(TAG, "PHY更新失败: status=$status") Log.w(TAG, "PHY更新失败: status=$status,设备可能不支持2M PHY")
} }
} }
} }
@ -1506,39 +1509,39 @@ startBytesStatistics()
failureReason.append(" - 原因: BluetoothGatt为空") failureReason.append(" - 原因: BluetoothGatt为空")
} else { } else {
// 检查连接状态 // 检查连接状态
val connectionState = bluetoothManager.getConnectionState( val State = bluetoothManager.getConnectionState(
bluetoothGatt!!.device, bluetoothGatt!!.device,
BluetoothProfile.GATT BluetoothProfile.GATT
) )
when (connectionState) { when (State) {
BluetoothProfile.STATE_DISCONNECTED -> { BluetoothProfile.STATE_DISCONNECTED -> {
failureReason.append(" - 原因: 设备已断开连接") failureReason.append(" -发送失败 原因: 设备已断开连接")
} }
BluetoothProfile.STATE_CONNECTING -> { BluetoothProfile.STATE_CONNECTING -> {
failureReason.append(" - 原因: 设备正在连接中") failureReason.append(" -发送失败 原因: 设备正在连接中")
} }
BluetoothProfile.STATE_DISCONNECTING -> { BluetoothProfile.STATE_DISCONNECTING -> {
failureReason.append(" - 原因: 设备正在断开连接") failureReason.append(" -发送失败 原因: 设备正在断开连接")
} }
BluetoothProfile.STATE_CONNECTED -> { BluetoothProfile.STATE_CONNECTED -> {
// 连接正常,检查其他原因 // 连接正常,检查其他原因
if (callWriteChar == null) { if (callWriteChar == null) {
failureReason.append(" - 原因: 写入特征值为空") failureReason.append(" -发送失败 原因: 写入特征值为空")
} else { } else {
// 检查特征值属性 // 检查特征值属性
val properties = callWriteChar!!.properties val properties = callWriteChar!!.properties
if ((properties and BluetoothGattCharacteristic.PROPERTY_WRITE) == 0 && if ((properties and BluetoothGattCharacteristic.PROPERTY_WRITE) == 0 &&
(properties and BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) { (properties and BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) {
failureReason.append(" - 原因: 特征值不支持写入操作") failureReason.append(" -发送失败 原因: 特征值不支持写入操作")
} else if (chunk.size > 512) { // BLE MTU通常限制 } else if (chunk.size > 512) { // BLE MTU通常限制
failureReason.append(" - 原因: 数据块过大 (${chunk.size} > 512字节)") failureReason.append(" -发送失败 原因: 数据块过大 (${chunk.size} > 512字节)")
} else { } else {
failureReason.append(" - 原因: 未知错误,可能是设备忙碌或缓冲区满") failureReason.append(" -发送失败 原因: 未知错误,可能是设备忙碌或缓冲区满")
} }
} }
} }
else -> { else -> {
failureReason.append(" - 原因: 未知连接状态($connectionState)") failureReason.append(" -发送失败 原因: 未知连接状态($connectionState)")
} }
} }
} }

10
local_plugins/ble_service/ios/ble_service/Sources/ble_service/BleService.swift

@ -300,13 +300,13 @@ private var cmdReplyType: UInt8 = 0
} }
} }
// // 开始扫描,使用构建的服务UUID数组进行过滤 // 开始扫描,使用构建的服务UUID数组进行过滤
// manager.scanForPeripherals(withServices: serviceUUIDs.isEmpty ? nil : serviceUUIDs, options: [ manager.scanForPeripherals(withServices: serviceUUIDs.isEmpty ? nil : serviceUUIDs, options: [
// CBCentralManagerScanOptionAllowDuplicatesKey: true // 允许重复以获取更新的广播数据
// ])
manager.scanForPeripherals(withServices: nil , options: [
CBCentralManagerScanOptionAllowDuplicatesKey: true // 允许重复以获取更新的广播数据 CBCentralManagerScanOptionAllowDuplicatesKey: true // 允许重复以获取更新的广播数据
]) ])
// manager.scanForPeripherals(withServices: nil , options: [
// CBCentralManagerScanOptionAllowDuplicatesKey: true // 允许重复以获取更新的广播数据
// ])
let filterInfo = serviceUUIDs.isEmpty ? "无过滤" : "过滤器数量: \(serviceUUIDs.count)" let filterInfo = serviceUUIDs.isEmpty ? "无过滤" : "过滤器数量: \(serviceUUIDs.count)"
os_log("🔍 开始扫描BLE设备(%{public}@),超时时间: %.1f秒", log: logger, type: .info, filterInfo, timeout) os_log("🔍 开始扫描BLE设备(%{public}@),超时时间: %.1f秒", log: logger, type: .info, filterInfo, timeout)

2
local_plugins/classic_bluetooth/android/build.gradle.kts

@ -34,4 +34,6 @@ android {
dependencies { dependencies {
add("implementation", "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.6.10") add("implementation", "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.6.10")
// 添加对 ble_service 模块的依赖
add("compileOnly", project(":ble_service"))
} }

24
local_plugins/classic_bluetooth/android/src/main/kotlin/com/yunqiinnovation/classic_bluetooth/ClassicBluetoothPlugin.kt

@ -15,6 +15,8 @@ import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result import io.flutter.plugin.common.MethodChannel.Result
// 添加BLE服务导入
import com.yunqiinnovation.ble_service.BleService
/** ClassicBluetoothPlugin */ /** ClassicBluetoothPlugin */
class ClassicBluetoothPlugin: FlutterPlugin, MethodCallHandler { class ClassicBluetoothPlugin: FlutterPlugin, MethodCallHandler {
@ -135,7 +137,15 @@ class ClassicBluetoothPlugin: FlutterPlugin, MethodCallHandler {
ClassicBluetoothHelper.BluetoothState.TURNING_OFF -> "turningOff" ClassicBluetoothHelper.BluetoothState.TURNING_OFF -> "turningOff"
ClassicBluetoothHelper.BluetoothState.UNKNOWN -> "unknown" ClassicBluetoothHelper.BluetoothState.UNKNOWN -> "unknown"
} }
if(state == ClassicBluetoothHelper.BluetoothState.OFF){
try {
BleService.disconnect()
Log.d(TAG, "经典蓝牙设备断开,已触发BLE强制断开连接")
} catch (e: Exception) {
Log.e(TAG, "调用BLE强制断开连接失败: ${e.message}", e)
}
}
// 发送状态变化事件 // 发送状态变化事件
val eventMap = mapOf( val eventMap = mapOf(
"type" to "stateChanged", "type" to "stateChanged",
@ -156,11 +166,19 @@ class ClassicBluetoothPlugin: FlutterPlugin, MethodCallHandler {
), ),
"timestamp" to System.currentTimeMillis() "timestamp" to System.currentTimeMillis()
) )
Log.d(TAG, "经典蓝牙设备连接")
sendEvent(eventMap) sendEvent(eventMap)
} }
override fun onDeviceDisconnected(device: Map<String, String>) { override fun onDeviceDisconnected(device: Map<String, String>) {
// 经典蓝牙设备断开时,同时断开BLE连接
try {
BleService.disconnect()
Log.d(TAG, "经典蓝牙设备断开,已触发BLE强制断开连接")
} catch (e: Exception) {
Log.e(TAG, "调用BLE强制断开连接失败: ${e.message}", e)
}
// 发送设备断开事件 // 发送设备断开事件
val eventMap = mapOf( val eventMap = mapOf(
"type" to "deviceDisconnected", "type" to "deviceDisconnected",
@ -190,4 +208,4 @@ class ClassicBluetoothPlugin: FlutterPlugin, MethodCallHandler {
Log.e(TAG, "事件发送失败", e) Log.e(TAG, "事件发送失败", e)
} }
} }
} }

8
local_plugins/classic_bluetooth/ios/classic_bluetooth/Package.swift

@ -11,12 +11,16 @@ let package = Package(
) )
], ],
dependencies: [ dependencies: [
// 如果需要其他依赖,在这里添加 // 添加对ble_service的依赖
.package(path: "../../../ble_service/ios/ble_service")
], ],
targets: [ targets: [
.target( .target(
name: "classic_bluetooth", name: "classic_bluetooth",
dependencies: [], dependencies: [
// 添加ble_service产品依赖
.product(name: "ble-service", package: "ble_service")
],
path: "Sources" path: "Sources"
) )
] ]

34
local_plugins/classic_bluetooth/ios/classic_bluetooth/Sources/classic_bluetooth/ClassicBluetoothHelper.swift

@ -1,7 +1,7 @@
import Foundation import Foundation
import AVFoundation import AVFoundation
import CoreBluetooth import CoreBluetooth
import ble_service
/// 蓝牙事件类型 /// 蓝牙事件类型
public enum BluetoothEventType { public enum BluetoothEventType {
case stateChanged case stateChanged
@ -65,16 +65,35 @@ public class ClassicBluetoothHelper: NSObject, CBCentralManagerDelegate {
case .stateChanged: case .stateChanged:
if let state = data["state"] as? String { if let state = data["state"] as? String {
print("蓝牙状态变化: \(state)") print("蓝牙状态变化: \(state)")
// configureAudioRouteBasedOnBluetoothState()
} }
case .deviceConnected: case .deviceConnected:
print("蓝牙设备已连接") print("蓝牙设备已连接")
// 蓝牙设备连接时:末端在手机,输出端在耳机
// configureAudioForBluetoothConnected()
case .deviceDisconnected: case .deviceDisconnected:
print("蓝牙设备已断开") print("蓝牙设备已断开")
// 蓝牙设备断开时:末端和输出端都在手机 // 蓝牙设备断开时:调用BLE断开连接
/*configureAudioForBluetoothDisconnected*/() disconnectBleOnClassicBluetoothDisconnected()
}
}
/**
* 经典蓝牙断开时调用BLE断开连接
* 当检测到经典蓝牙设备断开时,主动断开BLE连接
*/
private func disconnectBleOnClassicBluetoothDisconnected() {
// 检查BLE服务是否可用
if #available(iOS 13.0, *) {
let bleService = BleService.shared
// 检查BLE是否已连接
if bleService.isConnected() {
print("检测到经典蓝牙断开,正在断开BLE连接...")
bleService.disconnect()
print("BLE连接已断开")
} else {
print("BLE未连接,无需断开")
}
} else {
print("iOS版本不支持BLE服务")
} }
} }
@ -290,6 +309,9 @@ public class ClassicBluetoothHelper: NSObject, CBCentralManagerDelegate {
"address": cleanDeviceAddress(output.uid) "address": cleanDeviceAddress(output.uid)
] ]
stateCallback?(.deviceDisconnected, ["device": device]) stateCallback?(.deviceDisconnected, ["device": device])
// 在这里调用BLE断开连接
disconnectBleOnClassicBluetoothDisconnected()
} }
// 检查当前是否还有蓝牙设备 // 检查当前是否还有蓝牙设备

194
local_plugins/qq_music/android/src/main/kotlin/com/yunqiinnovation/qq_music/QQMusicSingleton.kt

@ -423,52 +423,83 @@ class QQMusicSingleton private constructor() {
action: (QQMusicManager, QQMusicManager.SearchCallback) -> Unit action: (QQMusicManager, QQMusicManager.SearchCallback) -> Unit
): (callback: (Result<List<Map<String, Any?>>>) -> Unit) -> Unit { ): (callback: (Result<List<Map<String, Any?>>>) -> Unit) -> Unit {
return lambda@{ callback -> return lambda@{ callback ->
if (!isQQMusicInstalled()){ if (!isQQMusicInstalled()){
callback(Result.failure(Exception("未安装QQ音乐软件,请先安装QQ音乐软件"))) callback(Result.failure(Exception("未安装QQ音乐软件,请先安装QQ音乐软件")))
return@lambda return@lambda
} }
val currentActivity = context val currentActivity = context
if (currentActivity == null) { if (currentActivity == null) {
callback(Result.failure(Exception("Activity not available"))) callback(Result.failure(Exception("Activity not available")))
return@lambda return@lambda
} }
Log.d(TAG, "开始 ${actionName}")
ensureMusicManager(currentActivity,object: QQMusicManager. BindCallback{ // 添加登录状态检查
override fun onSuccess(){ if (!isLoggedIn()) {
action(musicManager!!, object : QQMusicManager.SearchCallback { Log.d(TAG, "用户未登录,开始自动登录")
override fun onSuccess(songs: List<Map<String, Any?>>) { login { loginResult ->
Log.d(TAG, "${actionName} 成功") if (loginResult.isSuccess) {
handler.post { Log.d(TAG, "自动登录成功,继续执行 ${actionName}")
callback(Result.success(songs)) // 登录成功后继续执行原操作
} executeSearchAction(actionName, curragain, allagain, action, callback)
} } else {
Log.e(TAG, "自动登录失败: ${loginResult.exceptionOrNull()?.message}")
override fun onError(errorCode: Int, errorMessage: String) { callback(Result.failure(Exception("用户未登录且自动登录失败: ${loginResult.exceptionOrNull()?.message}")))
Log.d(TAG, "失败 ${actionName} 成功") }
if (allagain > 0 && curragain < allagain){ //重试机制
executeSearchbackAction(actionName,curragain+1,allagain,action)
}else{
handler.post {
callback(Result.failure(Exception("$actionName 失败: $errorMessage")))
}
}
}
})
} }
override fun onError(errorCode: Int, errorMessage: String) { return@lambda
if (allagain > 0 && curragain < allagain){ //重试机制 }
executeSearchbackAction(actionName,curragain+1,allagain,action)
}else{ // 用户已登录,直接执行搜索操作
executeSearchAction(actionName, curragain, allagain, action, callback)
}
}
// 提取搜索执行逻辑为独立方法
private fun executeSearchAction(
actionName: String,
curragain: Int,
allagain: Int,
action: (QQMusicManager, QQMusicManager.SearchCallback) -> Unit,
callback: (Result<List<Map<String, Any?>>>) -> Unit
) {
Log.d(TAG, "开始 ${actionName}")
ensureMusicManager(context!!, object: QQMusicManager.BindCallback{
override fun onSuccess(){
action(musicManager!!, object : QQMusicManager.SearchCallback {
override fun onSuccess(songs: List<Map<String, Any?>>) {
Log.d(TAG, "${actionName} 成功")
handler.post { handler.post {
callback(Result.failure(Exception("$actionName 失败: $errorMessage"))) callback(Result.success(songs))
}
}
override fun onError(errorCode: Int, errorMessage: String) {
Log.d(TAG, "失败 ${actionName} errorCode: $errorCode errorMessage:$errorMessage 当前重试${curragain}/${allagain}")
if (allagain > 0 && curragain < allagain){ //重试机制
Log.d(TAG, "重试 ${actionName} 当前重试${curragain+1}/${allagain}")
executeSearchbackAction(actionName,curragain+1,allagain,action)(callback)
}else{
handler.post {
callback(Result.failure(Exception("$actionName 失败: $errorMessage")))
}
} }
} }
})
}
override fun onError(errorCode: Int, errorMessage: String) {
Log.d(TAG, "失败 ${actionName} errorCode: $errorCode errorMessage:$errorMessage 当前重试${curragain}/${allagain}")
if (allagain > 0 && curragain < allagain){ //重试机制
Log.d(TAG, "重试 ${actionName} 当前重试${curragain+1}/${allagain}")
executeSearchbackAction(actionName,curragain+1,allagain,action)(callback)
}else{
handler.post {
callback(Result.failure(Exception("$actionName 失败: $errorMessage")))
}
} }
}) }
})
}
} }
@ -491,49 +522,80 @@ class QQMusicSingleton private constructor() {
callback(Result.failure(Exception("Activity not available"))) callback(Result.failure(Exception("Activity not available")))
return@lambda return@lambda
} }
Log.d(TAG, "开始 ${actionName}")
ensureMusicManager(currentActivity,object: QQMusicManager. BindCallback{ // 添加登录状态检查
override fun onSuccess(){ if (!isLoggedIn()) {
action(musicManager!!, object : QQMusicManager.PlayCallback { Log.d(TAG, "用户未登录,开始自动登录")
override fun onSuccess() { login { loginResult ->
Log.d(TAG, "${actionName}成功") if (loginResult.isSuccess) {
handler.post { Log.d(TAG, "自动登录成功,继续执行 ${actionName}")
callback(Result.success(Unit)) // 登录成功后继续执行原操作
} executePlayAction(actionName, curragain, allagain, action, callback)
} else {
Log.e(TAG, "自动登录失败: ${loginResult.exceptionOrNull()?.message}")
callback(Result.failure(Exception("用户未登录且自动登录失败: ${loginResult.exceptionOrNull()?.message}")))
}
}
return@lambda
}
// 用户已登录,直接执行搜索操作
executePlayAction(actionName, curragain, allagain, action, callback)
}
}
private fun executePlayAction(
actionName: String,
curragain:Int,
allagain:Int,
action: (QQMusicManager, QQMusicManager.PlayCallback) -> Unit,
callback: (Result<Unit>) -> Unit
) {
Log.d(TAG, "开始 ${actionName}")
ensureMusicManager(context!!,object: QQMusicManager. BindCallback{
override fun onSuccess(){
action(musicManager!!, object : QQMusicManager.PlayCallback {
override fun onSuccess() {
Log.d(TAG, "${actionName}成功")
handler.post {
callback(Result.success(Unit))
} }
}
override fun onError(errorCode: Int, errorMessage: String) { override fun onError(errorCode: Int, errorMessage: String) {
if (allagain > 0 && curragain < allagain){ //重试机制 if (allagain > 0 && curragain < allagain){ //重试机制
executePlaybackAction(actionName,curragain+1,allagain,action) executePlaybackAction(actionName,curragain+1,allagain,action)(callback)
}else{ }else{
handler.post { handler.post {
Log.e(TAG, "${actionName}失败: $errorCode $errorMessage") Log.e(TAG, "${actionName}失败: $errorCode $errorMessage")
if (errorCode == -1){ if (errorCode == -1){
musicManager?.bindQQMusicApiService() musicManager?.bindQQMusicApiService()
callback(Result.failure(Exception("播放失败: $errorMessage"))) callback(Result.failure(Exception("播放失败: $errorMessage")))
} else if (errorCode == 1030) { } else if (errorCode == 1030) {
callback(Result.success(Unit)) callback(Result.success(Unit))
}else{ }else{
callback(Result.failure(Exception("播放失败: $errorMessage"))) callback(Result.failure(Exception("播放失败: $errorMessage")))
}
} }
} }
} }
})
}
override fun onError(errorCode: Int, errorMessage: String) {
Log.e(TAG, "${actionName}失败: $errorCode $errorMessage")
if (allagain > 0 && curragain < allagain){ //重试机制
executePlaybackAction(actionName,curragain+1,allagain,action)
}else{
callback(Result.failure(Exception("$actionName 失败: $errorMessage")))
} }
})
}
override fun onError(errorCode: Int, errorMessage: String) {
Log.e(TAG, "${actionName}失败: $errorCode $errorMessage")
if (allagain > 0 && curragain < allagain){ //重试机制
executePlaybackAction(actionName,curragain+1,allagain,action)(callback)
}else{
callback(Result.failure(Exception("$actionName 失败: $errorMessage")))
} }
}) }
})
}
} }
private fun executeInfoAction( private fun executeInfoAction(
actionName: String, actionName: String,
action: (QQMusicManager, QQMusicManager.InfoCallback) -> Unit action: (QQMusicManager, QQMusicManager.InfoCallback) -> Unit

Loading…
Cancel
Save