wolfplus 1 year ago
parent
commit
7238988517
  1. 5
      android/app/build.gradle.kts
  2. 6
      android/app/src/main/kotlin/com/yunqiinnovation/deepsound/MainActivity.kt
  3. 7
      android/app/src/profile/AndroidManifest.xml
  4. 2
      android/settings.gradle.kts
  5. 5
      lib/core/bindings/initial_binding.dart
  6. 243
      lib/data/services/classic_bluetooth_service.dart
  7. 106
      lib/modules/home/controllers/home_controller.dart
  8. 136
      lib/modules/pairing/controllers/pairing_controller.dart
  9. 37
      local_plugins/classic_bluetooth/android/build.gradle.kts
  10. 1
      local_plugins/classic_bluetooth/android/settings.gradle.kts
  11. 11
      local_plugins/classic_bluetooth/android/src/main/AndroidManifest.xml
  12. 23
      local_plugins/classic_bluetooth/android/src/main/kotlin/com/yunqiinnovation/classic_bluetooth/ClassicBluetoothHelper.kt
  13. 13
      local_plugins/classic_bluetooth/android/src/main/kotlin/com/yunqiinnovation/classic_bluetooth/ClassicBluetoothPlugin.kt
  14. 115
      local_plugins/classic_bluetooth/lib/classic_bluetooth.dart
  15. 20
      local_plugins/classic_bluetooth/pubspec.yaml
  16. 2
      pubspec.yaml

5
android/app/build.gradle.kts

@ -124,12 +124,9 @@ dependencies {
// 添加本地插件模块依赖
implementation(project(":azure_speech"))
implementation(project(":open_ai_service"))
implementation(project(":chat_storage")) // 添加新创建的SQLite聊天存储插件
implementation(project(":jieli")) // 添加新创建的SQLite聊天存储插件
implementation(project(":spotify_sdk"))
implementation(project(":deepvoice_utils"))
implementation(project(":ble_service"))
// 添加OkHttp依赖
implementation("com.squareup.okhttp3:okhttp:4.9.3")

6
android/app/src/main/kotlin/com/yunqiinnovation/deepsound/MainActivity.kt

@ -52,7 +52,7 @@ class MainActivity: FlutterActivity() {
FileLogger.init(applicationContext)
// 初始化BLE后台唤醒功能
initBleBackgroundScan()
// initBleBackgroundScan()
FileLogger.d(TAG, "初始化完成")
}
@ -69,13 +69,9 @@ class MainActivity: FlutterActivity() {
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
GeneratedPluginRegistrant.registerWith(flutterEngine)
// 注册经典蓝牙插件
flutterEngine.plugins.add(ClassicBluetoothPlugin())
}
override fun onDestroy() {
super.onDestroy()
}

7
android/app/src/profile/AndroidManifest.xml

@ -1,7 +0,0 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>

2
android/settings.gradle.kts

@ -38,6 +38,7 @@ include(":jieli")
include(":spotify_sdk")
include(":ble_service")
include(":deepvoice_utils")
include(":classic_bluetooth")
// 设置azure_speech项目的路径
project(":azure_speech").projectDir = file("../local_plugins/azure_speech/android")
project(":open_ai_service").projectDir = file("../local_plugins/open_ai_service/android")
@ -46,3 +47,4 @@ project(":jieli").projectDir = file("../local_plugins/jieli/android")
project(":spotify_sdk").projectDir = file("../local_plugins/spotify_sdk/android")
project(":ble_service").projectDir = file("../local_plugins/ble_service/android")
project(":deepvoice_utils").projectDir = file("../local_plugins/deepvoice_utils/android")
project(":classic_bluetooth").projectDir = file("../local_plugins/classic_bluetooth/android")

5
lib/core/bindings/initial_binding.dart

@ -4,10 +4,7 @@ import '../../data/services/chat_history_service.dart';
import '../../data/services/volcano_translation_service.dart';
import '../../data/services/language_manager.dart';
import '../../data/services/volcano_ai_service.dart';
import '../../data/services/classic_bluetooth_service.dart';
import '../../data/services/bluetooth_media_button_service.dart';
import '../../core/utils/logger.dart';
import '../../data/services/voice_interaction_service.dart';
import '../../data/services/open_ai_service_adapter.dart';
/// 初始绑定,用于管理全局依赖
class InitialBinding extends Bindings {
@ -39,8 +36,6 @@ class InitialBinding extends Bindings {
// 火山AI服务
Get.lazyPut<VolcanoAIService>(() => VolcanoAIService(), fenix: true);
// 经典蓝牙服务 - 立即初始化以监听蓝牙状态
Get.lazyPut<ClassicBluetoothService>(() =>ClassicBluetoothService(), fenix: true);
// 蓝牙媒体按钮服务
// Get.lazyPut<BluetoothMediaButtonService>(

243
lib/data/services/classic_bluetooth_service.dart

@ -1,243 +0,0 @@
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:permission_handler/permission_handler.dart';
import '../../core/utils/logger.dart';
import 'dart:io';
/// 蓝牙设备信息
class BluetoothDevice {
final String name;
final String address;
final String type;
BluetoothDevice({
required this.name,
required this.address,
required this.type
});
factory BluetoothDevice.fromMap(Map<dynamic, dynamic> map) {
return BluetoothDevice(
name: map['name'] ?? '未知设备',
address: map['address'] ?? '',
type: map['type'] ?? 'unknown',
);
}
@override
String toString() => 'BluetoothDevice(name: $name, address: $address, type: $type)';
}
/// 经典蓝牙服务
/// 负责经典蓝牙设备的连接状态管理
class ClassicBluetoothService extends GetxService {
static ClassicBluetoothService get to => Get.find<ClassicBluetoothService>();
// 通道名称
final _methodChannel = const MethodChannel('com.yunqiinnovation.classic_bluetooth/methods');
final _eventChannel = const EventChannel('com.yunqiinnovation.classic_bluetooth/events');
/// 当前连接的设备
final currentDevice = Rx<BluetoothDevice?>(null);
/// 已连接耳机设备列表
final connectedDevices = <BluetoothDevice>[].obs;
/// 蓝牙事件监听
StreamSubscription? _eventSubscription;
/// 蓝牙开关状态
final bluetoothState = 'unknown'.obs;
/// 蓝牙是否可用
bool get isBluetoothEnabled => bluetoothState.value == 'on';
/// 服务是否已初始化
bool _isInitialized = false;
/// 初始化蓝牙服务
Future<bool> initialize() async {
if (_isInitialized) {
Logger.info('蓝牙服务已经初始化,跳过');
return true;
}
try {
Logger.info('正在初始化蓝牙服务...');
// 检查蓝牙是否启用
final bool isEnabled = await _methodChannel.invokeMethod<bool>('isBluetoothEnabled') ?? false;
bluetoothState.value = isEnabled ? 'on' : 'off';
Logger.info('初始蓝牙状态: ${bluetoothState.value}');
// 设置事件监听
_setupEventListener();
_isInitialized = true;
Logger.info('蓝牙服务初始化成功');
// 如果蓝牙已启用,自动获取已连接设备
if (isEnabled) {
await refreshConnectedDevices();
}
return true;
} catch (e) {
Logger.error('初始化蓝牙服务失败: $e');
return false;
}
}
/// 设置事件监听
void _setupEventListener() {
_eventSubscription?.cancel();
_eventSubscription = _eventChannel.receiveBroadcastStream().listen((event) {
if (event is Map) {
Logger.info('收到蓝牙事件: $event');
// 根据事件类型处理
final type = event['type'] as String?;
if (type == 'stateChanged') {
final state = event['state'] as String?;
if (state != null) {
_handleBluetoothStateChange(state);
}
} else if (type == 'deviceConnected') {
final deviceMap = event['device'] as Map?;
if (deviceMap != null) {
_handleDeviceConnected(deviceMap);
}
} else if (type == 'deviceDisconnected') {
final deviceMap = event['device'] as Map?;
if (deviceMap != null) {
_handleDeviceDisconnected(deviceMap);
}
}
}
}, onError: (e) {
Logger.error('蓝牙事件监听错误: $e');
});
}
/// 刷新已连接的蓝牙设备
Future<bool> refreshConnectedDevices() async {
// 确保服务已初始化
if (!_isInitialized && !(await initialize())) {
return false;
}
// 如果蓝牙未启用,无法获取设备
if (!isBluetoothEnabled) {
Logger.info('蓝牙未启用,无法获取设备');
return false;
}
try {
// 获取已连接的设备
final devices = await getConnectedDevices();
// 更新设备列表
connectedDevices.value = devices;
// 如果列表不为空且当前无选定设备,选择第一个
if (devices.isNotEmpty && currentDevice.value == null) {
currentDevice.value = devices.first;
}
return devices.isNotEmpty;
} catch (e) {
Logger.error('刷新已连接设备失败: $e');
return false;
}
}
/// 获取已连接的蓝牙设备
Future<List<BluetoothDevice>> getConnectedDevices() async {
try {
// 确保服务已初始化
if (!_isInitialized && !(await initialize())) {
return [];
}
final result = await _methodChannel.invokeMethod('getConnectedDevices');
if (result is List) {
return result.map((device) => BluetoothDevice.fromMap(device)).toList();
}
return [];
} catch (e) {
Logger.error('获取已连接设备失败: $e');
return [];
}
}
/// 检查蓝牙是否已开启
Future<bool> checkBluetoothEnabled() async {
try {
return await _methodChannel.invokeMethod<bool>('isBluetoothEnabled') ?? false;
} catch (e) {
Logger.error('检查蓝牙状态失败: $e');
return false;
}
}
/// 处理设备连接事件
void _handleDeviceConnected(Map deviceMap) {
final device = BluetoothDevice.fromMap(deviceMap);
// 更新设备列表
if (!connectedDevices.any((d) => d.address == device.address)) {
connectedDevices.add(device);
}
// 更新当前设备
currentDevice.value = device;
Logger.info('设备已连接: ${device.name}');
}
/// 处理设备断开事件
void _handleDeviceDisconnected(Map deviceMap) {
final address = deviceMap['address'] as String?;
if (address != null) {
// 更新设备列表
connectedDevices.removeWhere((d) => d.address == address);
// 如果是当前设备,清除当前设备
if (currentDevice.value?.address == address) {
currentDevice.value = connectedDevices.isEmpty ? null : connectedDevices.first;
Logger.info('当前设备已断开连接');
}
}
}
/// 处理蓝牙状态变化
void _handleBluetoothStateChange(String state) {
Logger.info('蓝牙状态变化: $state');
// 更新蓝牙状态
bluetoothState.value = state;
if (state == 'off') {
// 蓝牙关闭,清空设备信息
connectedDevices.clear();
currentDevice.value = null;
} else if (state == 'on') {
// 蓝牙重新开启,延迟一小段时间后刷新设备
Future.delayed(const Duration(milliseconds: 1000), () {
refreshConnectedDevices();
});
}
}
@override
void onClose() {
// 取消事件监听
_eventSubscription?.cancel();
super.onClose();
}
}

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

@ -1,10 +1,10 @@
import 'package:get/get.dart';
import 'dart:async';
import '../../../routes/app_routes.dart';
import '../../../data/services/classic_bluetooth_service.dart';
import '../../../core/utils/logger.dart';
import 'package:flutter/material.dart';
import '../../explore/controllers/explore_controller.dart';
import 'package:classic_bluetooth/classic_bluetooth.dart';
// 连接状态枚举
enum ConnectionStatus {
@ -18,9 +18,6 @@ class HomeController extends GetxController {
static HomeController get to => Get.find();
final ExploreController exploreController = Get.find<ExploreController>();
// 经典蓝牙服务
final ClassicBluetoothService _bluetoothService = Get.find<ClassicBluetoothService>();
// 连接状态
final _connectionStatus = ConnectionStatus.disconnected.obs;
ConnectionStatus get connectionStatus => _connectionStatus.value;
@ -36,13 +33,18 @@ class HomeController extends GetxController {
final _connectionErrorMsg = ''.obs;
String get connectionErrorMsg => _connectionErrorMsg.value;
// 从蓝牙服务中直接获取的属性
Rx<BluetoothDevice?> get currentDevice => _bluetoothService.currentDevice;
List<BluetoothDevice> get connectedDevices => _bluetoothService.connectedDevices;
RxString get bluetoothState => _bluetoothService.bluetoothState;
// 蓝牙状态和设备信息
final _bluetoothState = 'unknown'.obs;
String get bluetoothState => _bluetoothState.value;
final Rx<BluetoothDevice?> _currentDevice = Rx<BluetoothDevice?>(null);
BluetoothDevice? get currentDevice => _currentDevice.value;
// 蓝牙事件订阅
StreamSubscription<BluetoothEvent>? _bluetoothSubscription;
// 蓝牙是否已连接
bool get isConnected => currentDevice.value != null;
bool get isConnected => currentDevice != null;
@override
void onInit() {
@ -55,21 +57,32 @@ class HomeController extends GetxController {
_simulateBatteryUpdate();
}
@override
void onClose() {
_bluetoothSubscription?.cancel();
super.onClose();
}
// 初始化蓝牙
Future<void> _initBluetooth() async {
try {
// 确保蓝牙服务已初始化
if (!(await _bluetoothService.initialize())) {
_connectionStatus.value = ConnectionStatus.failed;
_connectionErrorMsg.value = '初始化蓝牙服务失败';
_connectionStatus.value = ConnectionStatus.connecting;
// 检查蓝牙状态
final isEnabled = await ClassicBluetooth.isBluetoothEnabled();
_bluetoothState.value = isEnabled ? 'on' : 'off';
if (!isEnabled) {
_connectionStatus.value = ConnectionStatus.disconnected;
_connectionErrorMsg.value = '蓝牙未开启';
return;
}
// 设置监听器
_setupBluetoothListeners();
// 设置蓝牙事件监听
_setupBluetoothListener();
// 更新初始状态
_updateConnectionStatus();
// 获取已连接设备
await refreshDeviceConnection();
} catch (e) {
Logger.error('初始化蓝牙失败: $e');
@ -78,29 +91,54 @@ class HomeController extends GetxController {
}
}
// 设置蓝牙状态监听
void _setupBluetoothListeners() {
// 监听当前设备变化
ever(currentDevice, (_) {
// 设置蓝牙事件监听
void _setupBluetoothListener() {
_bluetoothSubscription?.cancel();
_bluetoothSubscription = ClassicBluetooth.events.listen((event) {
if (event.type == 'stateChanged' && event.state != null) {
_bluetoothState.value = event.state!;
// 如果蓝牙关闭,清空设备信息
if (event.state == 'off') {
_currentDevice.value = null;
_connectionErrorMsg.value = '蓝牙未开启';
}
_updateConnectionStatus();
});
} else if (event.type == 'deviceConnected' && event.device != null) {
final newDevice = event.device!;
// 监听蓝牙状态变化
ever(bluetoothState, (_) {
// 更新当前设备
_currentDevice.value = newDevice;
_connectionErrorMsg.value = '';
_updateConnectionStatus();
} else if (event.type == 'deviceDisconnected' && event.device != null) {
final disconnectedDevice = event.device!;
// 如果断开的是当前设备,清除当前设备
if (_currentDevice.value?.address == disconnectedDevice.address) {
_currentDevice.value = null;
_connectionErrorMsg.value = '设备已断开连接';
}
_updateConnectionStatus();
}
}, onError: (error) {
Logger.error('蓝牙事件流错误: $error');
});
}
// 更新连接状态
void _updateConnectionStatus() {
if (bluetoothState.value == 'off') {
if (bluetoothState == 'off') {
_connectionStatus.value = ConnectionStatus.disconnected;
_deviceBattery.value = null;
update();
return;
}
if (currentDevice.value != null) {
if (currentDevice != null) {
_connectionStatus.value = ConnectionStatus.connected;
} else {
_connectionStatus.value = ConnectionStatus.disconnected;
@ -116,12 +154,14 @@ class HomeController extends GetxController {
_connectionStatus.value = ConnectionStatus.connecting;
update();
final success = await _bluetoothService.refreshConnectedDevices();
final devices = await ClassicBluetooth.getConnectedDevices();
if (!success) {
_connectionErrorMsg.value = '未找到已连接的设备';
} else {
if (devices.isNotEmpty) {
_currentDevice.value = devices.first;
_connectionErrorMsg.value = '';
} else {
_currentDevice.value = null;
_connectionErrorMsg.value = '未找到已连接的设备';
}
} catch (e) {
Logger.error('刷新设备连接失败: $e');
@ -184,7 +224,7 @@ class HomeController extends GetxController {
// 获取连接状态对应的文本
String getConnectionStatusText() {
// 如果蓝牙关闭,优先显示蓝牙状态
if (bluetoothState.value == 'off') {
if (bluetoothState == 'off') {
return 'bluetoothOff'.tr; // 蓝牙已关闭
}
@ -203,7 +243,7 @@ class HomeController extends GetxController {
// 获取设备名称
String getDeviceName() {
return currentDevice.value?.name ?? 'unknownEquipment'.tr; // 未知设备
return currentDevice?.name ?? 'unknownEquipment'.tr; // 未知设备
}
// 获取信号强度(模拟数据)
@ -226,6 +266,6 @@ class HomeController extends GetxController {
// 获取设备ID
String getDeviceId() {
return currentDevice.value?.address ?? 'unknown'.tr; // 未知
return currentDevice?.address ?? 'unknown'.tr; // 未知
}
}

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

@ -3,17 +3,14 @@ import 'package:get/get.dart';
import 'dart:async';
import 'package:permission_handler/permission_handler.dart';
import '../../../routes/app_routes.dart';
import '../../../data/services/classic_bluetooth_service.dart';
import '../../../core/utils/logger.dart';
import 'dart:io';
import 'package:classic_bluetooth/classic_bluetooth.dart';
/// 配对控制器,负责显示已连接经典蓝牙设备和处理基本配对流程
/// 配对控制器,负责检查蓝牙权限、状态,并处理配对流程
class PairingController extends GetxController {
static PairingController get to => Get.find();
// 经典蓝牙服务
final ClassicBluetoothService _bluetoothService = Get.find<ClassicBluetoothService>();
// 状态标志
final isLoading = false.obs;
@ -21,9 +18,9 @@ class PairingController extends GetxController {
final _hasPermissions = false.obs;
bool get hasPermissions => _hasPermissions.value;
// 蓝牙服务是否可用
final _isBluetoothServiceAvailable = true.obs;
bool get isBluetoothServiceAvailable => _isBluetoothServiceAvailable.value;
// 蓝牙状态
final _bluetoothState = 'unknown'.obs;
String get bluetoothState => _bluetoothState.value;
// 状态描述(用于UI显示)
final statusDescription = 'initializing'.tr.obs;
@ -31,10 +28,12 @@ class PairingController extends GetxController {
// 是否正在跳转到主页
bool _isNavigatingToHome = false;
// 当前连接设备和设备列表(直接从服务获取)
Rx<BluetoothDevice?> get currentDevice => _bluetoothService.currentDevice;
RxList<BluetoothDevice> get connectedDevices => _bluetoothService.connectedDevices;
RxString get bluetoothState => _bluetoothService.bluetoothState;
// 当前连接设备
final Rx<BluetoothDevice?> _currentDevice = Rx<BluetoothDevice?>(null);
BluetoothDevice? get currentDevice => _currentDevice.value;
// 蓝牙事件订阅
StreamSubscription<BluetoothEvent>? _bluetoothSubscription;
@override
void onInit() {
@ -42,6 +41,12 @@ class PairingController extends GetxController {
_initialize();
}
@override
void onClose() {
_bluetoothSubscription?.cancel();
super.onClose();
}
/// 初始化流程
Future<void> _initialize() async {
try {
@ -54,33 +59,30 @@ class PairingController extends GetxController {
return;
}
statusDescription.value = 'initializing'.tr;
// 监听蓝牙状态变化
_setupBluetoothListener();
// 检查蓝牙状态
statusDescription.value = 'checkingBluetoothStatus'.tr;
final isEnabled = await ClassicBluetooth.isBluetoothEnabled();
_bluetoothState.value = isEnabled ? 'on' : 'off';
// 初始化蓝牙服务
if (!await _bluetoothService.initialize()) {
Logger.error('蓝牙服务初始化失败');
statusDescription.value = 'bluetoothInitializationFailure'.tr;
_isBluetoothServiceAvailable.value = false;
if (!isEnabled) {
statusDescription.value = 'bluetoothTurnedOff'.tr;
return;
}
// 设置状态监听
_setupStatusListeners();
// 刷新已连接设备
statusDescription.value = 'searchingDevice'.tr;
await refreshConnectedDevices();
} catch (e) {
Logger.error('初始化配对控制器失败: $e');
statusDescription.value = 'initializationFailure'.tr;
_isBluetoothServiceAvailable.value = false;
} finally {
isLoading.value = false;
if (hasPermissions) {
_updateStatusDescription();
}
}
}
/// 检查并请求必要的蓝牙权限
Future<bool> _checkAndRequestPermissions() async {
@ -119,29 +121,55 @@ class PairingController extends GetxController {
if (Platform.isAndroid) {
// 这里需要原生代码支持来获取确切的SDK版本,
// 或者使用 device_info_plus 插件
// 暂时返回一个较高值以触发新权限请求,如果需要精确控制则需要引入插件
// 或者,如果你的minSdkVersion >= 31,可以直接请求新权限
// 假设minSdkVersion < 31,我们需要一种方式判断当前运行设备的SDK版本
// 为了简化,我们这里先假设需要请求新权限,如果你的目标SDK较低,需要调整
// 暂时返回一个较高值以触发新权限请求
return 31; // 假设运行在需要新权限的设备上
}
return 0;
}
/// 设置状态变化监听
void _setupStatusListeners() {
// 监听蓝牙状态变化
ever(bluetoothState, (_) => _updateStatusDescription());
/// 设置蓝牙状态监听
void _setupBluetoothListener() {
_bluetoothSubscription?.cancel();
try {
_bluetoothSubscription = ClassicBluetooth.events.listen(
(event) {
if (event.type == 'stateChanged' && event.state != null) {
_bluetoothState.value = event.state!;
_updateStatusDescription();
// 如果蓝牙关闭,清除设备
if (event.state == 'off') {
_currentDevice.value = null;
}
} else if (event.type == 'deviceConnected' && event.device != null) {
// 直接从事件中获取设备信息
final newDevice = event.device!;
// 监听当前设备变化
ever(currentDevice, (device) {
// 更新当前设备
_currentDevice.value = newDevice;
_updateStatusDescription();
// 当设备连接时自动跳转
if (device != null) {
// 设备连接时自动跳转到主页
_autoNavigateToHome();
} else if (event.type == 'deviceDisconnected' && event.device != null) {
// 直接从事件中获取设备信息
final disconnectedDevice = event.device!;
// 如果断开的是当前设备,清除当前设备
if (_currentDevice.value?.address == disconnectedDevice.address) {
_currentDevice.value = null;
}
_updateStatusDescription();
}
},
onError: (error) {
Logger.error('蓝牙事件流错误: $error');
},
);
} catch (e) {
Logger.error('设置蓝牙事件监听器失败: $e');
}
});
}
/// 更新状态描述
@ -151,18 +179,13 @@ class PairingController extends GetxController {
return;
}
if (bluetoothState.value == 'off') {
if (bluetoothState == 'off') {
statusDescription.value = 'bluetoothTurnedOff'.tr;
return;
}
if (currentDevice.value != null) {
statusDescription.value = '${'connectedDevice'.tr} ${currentDevice.value!.name}';
return;
}
if (connectedDevices.isNotEmpty) {
statusDescription.value = '${'findConnectedDevice'.tr} ${connectedDevices.first.name}';
if (currentDevice != null) {
statusDescription.value = '${'connectedDevice'.tr} ${currentDevice!.name}';
return;
}
@ -176,9 +199,7 @@ class PairingController extends GetxController {
/// 刷新已连接设备
Future<void> refreshConnectedDevices() async {
if (!_isBluetoothServiceAvailable.value) return;
if (!await _checkAndRequestPermissions()) {
if (!hasPermissions) {
statusDescription.value = 'permissionDenied'.tr;
return;
}
@ -187,24 +208,31 @@ class PairingController extends GetxController {
isLoading.value = true;
statusDescription.value = 'searchingDevice'.tr;
await _bluetoothService.refreshConnectedDevices();
// 获取已连接的蓝牙设备
final devices = await ClassicBluetooth.getConnectedDevices();
// 如果有设备则设置为当前设备
if (devices.isNotEmpty) {
_currentDevice.value = devices.first;
_autoNavigateToHome();
} else {
_currentDevice.value = null;
}
} catch (e) {
Logger.error('刷新设备失败: $e');
statusDescription.value = 'failedRefreshDevice'.tr;
} finally {
isLoading.value = false;
if (hasPermissions) {
_updateStatusDescription();
}
}
}
/// 自动跳转到主页
void _autoNavigateToHome() {
if (_isNavigatingToHome || currentDevice.value == null) return;
if (_isNavigatingToHome || currentDevice == null) return;
_isNavigatingToHome = true;
Logger.info('已找到连接的设备 ${currentDevice.value?.name},自动跳转到主页');
Logger.info('已找到连接的设备 ${currentDevice?.name},自动跳转到主页');
Future.delayed(const Duration(milliseconds: 500), () {
Get.offAllNamed(Routes.home);
@ -216,7 +244,7 @@ class PairingController extends GetxController {
try {
if (!hasPermissions) {
openAppSettings();
} else if (Platform.isAndroid && bluetoothState.value == 'off') {
} else if (Platform.isAndroid && bluetoothState == 'off') {
AppSettings.openAppSettings(type: AppSettingsType.bluetooth);
} else if (Platform.isIOS) {
openAppSettings();

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

@ -0,0 +1,37 @@
plugins {
// Android Library 插件
id("com.android.library")
// Kotlin Android 插件
id("org.jetbrains.kotlin.android")
id("com.github.johnrengelman.shadow") version "8.1.1"
}
android {
// 命名空间,对应你插件的包名(需与代码内包名保持一致)
namespace = "com.yunqiinnovation.classic_bluetooth"
// 目标 SDK 版本
compileSdk = 33
defaultConfig {
// 最低 SDK 版本
minSdk = 21
targetSdk = 33
}
// Java 语言级别兼容配置
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
// Kotlin 语言级别
kotlinOptions {
jvmTarget = "11"
}
}
dependencies {
add("implementation", "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.6.10")
}

1
local_plugins/classic_bluetooth/android/settings.gradle.kts

@ -0,0 +1 @@
rootProject.name = "classic_bluetooth"

11
local_plugins/classic_bluetooth/android/src/main/AndroidManifest.xml

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.yunqiinnovation.classic_bluetooth">
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<!-- For Android 12+ -->
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
</manifest>

23
android/app/src/main/kotlin/com/yunqiinnovation/deepsound/ClassicBluetoothHelper.kt → local_plugins/classic_bluetooth/android/src/main/kotlin/com/yunqiinnovation/classic_bluetooth/ClassicBluetoothHelper.kt

@ -1,4 +1,4 @@
package com.yunqiinnovation.deepsound
package com.yunqiinnovation.classic_bluetooth
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
@ -79,7 +79,6 @@ class ClassicBluetoothHelper(private val context: Context) {
return bluetoothAdapter.isEnabled
} catch (e: Exception) {
Log.e(TAG, "检查蓝牙状态失败", e)
return false
}
}
@ -187,24 +186,6 @@ class ClassicBluetoothHelper(private val context: Context) {
callback.onSuccess(devicesList)
}
/**
* 检查是否有蓝牙权限
*/
fun hasBluetoothPermission(): Boolean {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
ContextCompat.checkSelfPermission(
context,
Manifest.permission.BLUETOOTH_CONNECT
) == PackageManager.PERMISSION_GRANTED
} else {
ContextCompat.checkSelfPermission(
context,
Manifest.permission.BLUETOOTH
) == PackageManager.PERMISSION_GRANTED
}
}
/**
* 开始监听蓝牙状态变化
* @param callback 蓝牙状态回调接口
@ -314,7 +295,7 @@ class ClassicBluetoothHelper(private val context: Context) {
try {
context.unregisterReceiver(bluetoothReceiver)
} catch (e: Exception) {
Log.e(TAG, "注销蓝牙接收器失败", e)
// 忽略注销失败异常
}
bluetoothReceiver = null
}

13
android/app/src/main/kotlin/com/yunqiinnovation/deepsound/ClassicBluetoothPlugin.kt → local_plugins/classic_bluetooth/android/src/main/kotlin/com/yunqiinnovation/classic_bluetooth/ClassicBluetoothPlugin.kt

@ -1,4 +1,4 @@
package com.yunqiinnovation.deepsound
package com.yunqiinnovation.classic_bluetooth
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
@ -58,9 +58,6 @@ class ClassicBluetoothPlugin: FlutterPlugin, MethodCallHandler {
"isBluetoothEnabled" -> {
result.success(bluetoothHelper.isBluetoothEnabled())
}
"hasBluetoothPermission" -> {
result.success(bluetoothHelper.hasBluetoothPermission())
}
else -> {
result.notImplemented()
}
@ -151,8 +148,6 @@ class ClassicBluetoothPlugin: FlutterPlugin, MethodCallHandler {
}
override fun onDeviceConnected(device: Map<String, String>) {
FileLogger.d(TAG, "设备连接: ${device["name"]} (${device["address"]})")
// 发送设备连接事件
val eventMap = mapOf(
"type" to "deviceConnected",
@ -167,8 +162,6 @@ class ClassicBluetoothPlugin: FlutterPlugin, MethodCallHandler {
}
override fun onDeviceDisconnected(device: Map<String, String>) {
FileLogger.d(TAG, "设备断开: ${device["name"]} (${device["address"]})")
// 发送设备断开事件
val eventMap = mapOf(
"type" to "deviceDisconnected",
@ -189,6 +182,10 @@ class ClassicBluetoothPlugin: FlutterPlugin, MethodCallHandler {
*/
private fun sendEvent(event: Map<String, Any>) {
try {
if (eventSink == null) {
return
}
eventSink?.success(event)
} catch (e: Exception) {
FileLogger.e(TAG, "事件发送失败", e)

115
local_plugins/classic_bluetooth/lib/classic_bluetooth.dart

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

20
local_plugins/classic_bluetooth/pubspec.yaml

@ -0,0 +1,20 @@
name: classic_bluetooth
description: 经典蓝牙设备连接和状态监控插件
version: 0.0.1
environment:
sdk: ">=2.17.0 <3.0.0"
flutter: ">=2.5.0"
dependencies:
flutter:
sdk: flutter
deepvoice_utils:
path: ../deepvoice_utils
flutter:
plugin:
platforms:
android:
package: com.yunqiinnovation.classic_bluetooth
pluginClass: ClassicBluetoothPlugin

2
pubspec.yaml

@ -82,6 +82,8 @@ dependencies:
path: local_plugins/ble_service
deepvoice_utils:
path: local_plugins/deepvoice_utils
classic_bluetooth:
path: local_plugins/classic_bluetooth
dev_dependencies:

Loading…
Cancel
Save