moyoung_glasses_ble_plugin 1.2.8
moyoung_glasses_ble_plugin: ^1.2.8 copied to clipboard
A comprehensive Flutter plugin for MoYoung smart glasses with 50+ features including Bluetooth communication, camera control, audio recording, file management, OTA upgrades, and AI integration.
example/lib/main.dart
import 'dart:io' show Platform;
import 'package:flutter/foundation.dart' show kIsWeb;
import 'dart:async';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:moyoung_glasses_ble_plugin/moyoung_glasses_ble.dart';
import 'package:moyoung_glasses_ble_plugin/impl/moyoung_glasses_beans.dart';
import 'package:moyoung_glasses_ble_plugin/impl/channel_names.dart';
import 'package:moyoung_glasses_ble_plugin/models/ai_image_data.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'utils/toast_util.dart';
import 'event_manager.dart';
import 'glasses_scan_page.dart';
import 'mac_address_cache.dart';
import 'package:permission_handler/permission_handler.dart';
import 'l10n/app_strings.dart';
import 'utils/locale_manager.dart';
import 'pages/media_file_page.dart';
import 'pages/version_ota_page.dart';
import 'package:media_kit/media_kit.dart';
import 'dual_plugin_example.dart';
//region 数据类定义
//endregion
void main() {
WidgetsFlutterBinding.ensureInitialized();
MediaKit.ensureInitialized();
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final MoYoungGlassesBle _glassesPlugin = MoYoungGlassesBle();
final _streamSubscriptions = <StreamSubscription<dynamic>>[];
final GlobalKey<NavigatorState> _navigatorKey = GlobalKey<NavigatorState>();
//region 属性变量
// 语言管理
Locale _locale = const Locale('zh', 'CN');
String _permissionTxt = AppStrings.requestPermission;
String _connectionStatus = AppStrings.disconnected;
String _batteryLevel = AppStrings.unknown;
bool _isCharging = false;
String _wearCheckState = AppStrings.unknown;
String _deviceVersion = AppStrings.unknown;
// 存储实际值,用于语言切换时重新格式化
int _actualBatteryLevel = 0;
String _actualLanguageStatus = '';
String _actualDeviceUUIDStatus = '';
String _actualVoiceWakeupStatus = '';
String _actualAudioTalkState = '';
String _actualBluetoothState = '';
bool isQuit = false;
bool _isConnected = false;
String _audioDataStatus = AppStrings.noAudioData;
String _aiImageDataStatus = AppStrings.noAiImageData;
String _aiConversationStatus = AppStrings.aiStatusNotStarted; // AI对话状态
String _simultaneousInterpretationStatus = AppStrings.simultaneousInterpretationNotStarted; // 同声传译状态
int _selectedSimultaneousInterpretationAction = 3; // 选择的同声传译动作:3=开始,4=暂停,5=停止
String _translateAudioStatus = AppStrings.noTranslationAudio;
int _translateTotalBytes = 0; // 翻译音频数据累计字节数
// 拍照模式选择
int _selectedPhotoMode = 0;
// AI 回复状态选择
int _selectedAIReplyStatus = 0;
// 图片数据相关
bool _isImageReceiving = false; // 是否正在接收图片数据
Uint8List? _displayedImage; // 当前显示的图片数据
String _pcmAudioStatus = AppStrings.noPcmAudioData;
int _pcmTotalBytes = 0;
String _audioRecordStatus = AppStrings.clickToGet; // 录音状态
String _languageStatus = AppStrings.clickToGet; // 语言设置状态
String _getLanguageStatus = AppStrings.clickToGet; // 获取语言状态
String _deviceUUIDStatus = AppStrings.clickToGet; // 设备UUID状态
String _voiceWakeupStatus = AppStrings.clickToGet; // 语音唤醒状态
String _runningStatus = AppStrings.clickToGet; // 运行状态
String _sdkLogStatus = AppStrings.waitingSdkLog; // SDK日志状态
String? _cachedMacAddress; // 缓存的 MAC 地址
String? _cachedDeviceName; // 缓存的设备名称
//endregion
BuildContext? _materialContext; // MaterialApp 内部的 context
String _audioTalkState = AppStrings.notSet; // 音频控制状态
int _selectedAudioAction = 1; // 选择的音频动作类型
String _bluetoothState = AppStrings.unknown; // 蓝牙状态
int _selectedFrameRate = 0; // 选择的录像帧率
int _selectedMaxDuration = 60; // 选择的录像最大时长
int? _selectedVoiceWakeupAction; // 选择的语音唤醒操作:0=开启(TypeOn),1=关闭(TypeOff),null=未选择
int _currentLanguageSelection = 1; // 当前选择的语言:0=English,1=中文
//endregion
//region 生命周期
@override
void initState() {
super.initState();
try {
// 检测当前运行平台
if (kIsWeb) {
debugPrint('当前运行平台: Web');
} else if (Platform.isIOS) {
debugPrint('当前运行平台: iOS');
} else if (Platform.isAndroid) {
debugPrint('当前运行平台: Android');
} else {
debugPrint('当前运行平台: 其他平台');
}
_loadLocale();
_initializeGlassesPlugin();
_subscribeToStreams();
_checkInitialBluetoothState();
// 先等缓存地址加载完成,再检查连接状态
_loadCachedMacAddress().then((_) {
Future.delayed(Duration(milliseconds: 500), () {
_checkInitialConnectionState();
});
});
debugPrint('=== MyApp 初始化完成 ===');
} catch (e, stackTrace) {
debugPrint('=== MyApp 初始化失败 ===');
debugPrint('错误: $e');
debugPrint('堆栈: $stackTrace');
}
}
/// 显式初始化眼镜插件(Android 端会在这里开始 BLE 注册和自动重连)
Future<void> _initializeGlassesPlugin() async {
try {
final initialized = await _glassesPlugin.initialize();
debugPrint('MoYoungGlassesBle initialize result: $initialized');
} catch (e, stackTrace) {
debugPrint('MoYoungGlassesBle initialize failed: $e');
debugPrint('MoYoungGlassesBle initialize stack: $stackTrace');
}
}
/// 更新状态文本(语言切换时调用,只更新显示文本,不改变数据源)
void _updateStatusTexts() {
setState(() {
_permissionTxt = AppStrings.requestPermissionStatus;
// 只更新连接状态的显示文本,数据源 _isConnected 保持不变
_connectionStatus = _isConnected ? AppStrings.connected : AppStrings.disconnected;
// 只根据实际数据源重新格式化显示文本,不改变数据源
_batteryLevel = _actualBatteryLevel > 0
? AppStrings.batteryDisplay("$_actualBatteryLevel", _isCharging)
: AppStrings.unknownStatus;
_languageStatus = _actualLanguageStatus.isNotEmpty
? _actualLanguageStatus
: AppStrings.clickToGet;
_getLanguageStatus = _getLanguageStatus.isNotEmpty && _getLanguageStatus != AppStrings.clickToGet
? _getLanguageStatus
: AppStrings.clickToGet;
_deviceUUIDStatus = _actualDeviceUUIDStatus.isNotEmpty
? _actualDeviceUUIDStatus
: AppStrings.clickToGet;
_voiceWakeupStatus = _actualVoiceWakeupStatus.isNotEmpty
? _actualVoiceWakeupStatus
: AppStrings.clickToGet;
_audioTalkState = _actualAudioTalkState.isNotEmpty
? _actualAudioTalkState
: AppStrings.notSet;
_bluetoothState = _actualBluetoothState.isNotEmpty
? _actualBluetoothState
: AppStrings.unknownStatus;
// 这些状态文本根据当前语言重新设置
_audioDataStatus = AppStrings.noAudioData;
_aiImageDataStatus = AppStrings.noAiImageData;
_aiConversationStatus = AppStrings.aiStatusNotStarted;
_translateAudioStatus = AppStrings.noTranslationAudio;
_pcmAudioStatus = AppStrings.noPcmAudio;
_audioRecordStatus = AppStrings.clickToGet;
_sdkLogStatus = AppStrings.waitingSdkLog;
// 重置图片数据相关变量
_isImageReceiving = false;
_displayedImage = null;
});
}
/// 加载语言设置
Future<void> _loadLocale() async {
final savedLocale = LocaleManager.getSavedLocale();
setState(() {
_locale = savedLocale;
AppStrings.setLocale(savedLocale);
});
// 语言加载完成后,初始化显示文本
_initializeStatusTexts();
}
/// 初始化状态文本(只在语言加载后调用一次)
void _initializeStatusTexts() {
setState(() {
// 设置初始显示文本,这些会在数据更新时被替换
_permissionTxt = AppStrings.requestPermissionStatus;
_connectionStatus = AppStrings.disconnected;
_batteryLevel = AppStrings.unknownStatus;
_deviceVersion = AppStrings.unknownStatus;
_audioDataStatus = AppStrings.noAudioData;
_aiImageDataStatus = AppStrings.noAiImageData;
_aiConversationStatus = AppStrings.aiStatusNotStarted;
_translateAudioStatus = AppStrings.noTranslationAudio;
_pcmAudioStatus = AppStrings.noPcmAudio;
_audioRecordStatus = AppStrings.clickToGet;
_languageStatus = AppStrings.clickToGet;
_actualLanguageStatus = '';
_getLanguageStatus = AppStrings.clickToGet;
_deviceUUIDStatus = AppStrings.clickToGet;
_voiceWakeupStatus = AppStrings.clickToGet;
_runningStatus = AppStrings.clickToGet;
_sdkLogStatus = AppStrings.waitingSdkLog;
_audioTalkState = AppStrings.notSet;
_bluetoothState = AppStrings.unknownStatus;
// 重置累计字节数
_translateTotalBytes = 0;
_pcmTotalBytes = 0;
});
}
/// 切换语言
Future<void> _changeLanguage(Locale locale) async {
await LocaleManager.saveLocale(locale);
setState(() {
_locale = locale;
AppStrings.setLocale(locale);
});
// 更新所有状态文本
_updateStatusTexts();
}
/// 加载缓存的 MAC 地址
Future<void> _loadCachedMacAddress() async {
final mac = await MacAddressCache.getCachedMac();
final name = await MacAddressCache.getCachedDeviceName();
setState(() {
_cachedMacAddress = mac;
_cachedDeviceName = name;
});
if (mac != null && name != null) {
print(AppStrings.loadCachedDevice(name!, mac!));
}
}
void _subscribeToStreams() {
// 监听连接状态
_streamSubscriptions.add(
_glassesPlugin.connStateEveStm.listen((ConnectStateBean event) {
setState(() {
// connectState: 0=断开, 1=连接中, 2=已连接, 3=断开中
_isConnected = event.connectState == 2;
_connectionStatus = _getConnectionStateText(event.connectState);
});
// 连接成功时发送通知并更新信息
if (event.connectState == 2) {
EventManager().emit(EventNames.connectionSuccess, {
'isConnected': true,
});
// 连接成功后确保关闭 Wi-Fi 模式,防止设备停留在 Wi-Fi 模式导致无法操作
_glassesPlugin.disableWifi().catchError((e) {
debugPrint('连接成功后关闭 Wi-Fi 失败(可能本来就没开): $e');
});
// 连接成功后更新基本信息
// 延迟一下,避免与初始连接检查冲突
Timer(const Duration(milliseconds: 100), () async {
if (_isConnected) {
await _loadCachedMacAddress();
// 查询设备信息
_queryBattery();
_queryDeviceVersion();
}
});
}
// 断开连接时清除状态
else if (event.connectState == 0) {
setState(() {
_cachedMacAddress = null;
_cachedDeviceName = null;
});
}
}),
);
// 监听蓝牙状态
_streamSubscriptions.add(
_glassesPlugin.bluetoothStateEveStm.listen((int state) {
String stateText = AppStrings.unknownState;
switch (state) {
case 0:
stateText = AppStrings.unknownConnectionState;
break;
case 1:
stateText = AppStrings.bluetoothResetting;
break;
case 2:
stateText = AppStrings.bluetoothUnavailable;
break;
case 3:
stateText = AppStrings.bluetoothUnauthorized;
break;
case 4:
stateText = AppStrings.bluetoothAvailable;
break;
case 5:
stateText = AppStrings.bluetoothLimiting;
break;
case 6:
stateText = AppStrings.bluetoothTurningOn;
break;
case 7:
stateText = AppStrings.bluetoothOn;
break;
case 8:
stateText = AppStrings.bluetoothTurningOff;
break;
case 9:
stateText = AppStrings.bluetoothOff;
break;
}
setState(() {
_bluetoothState = stateText;
});
}),
);
// 监听音频数据
_streamSubscriptions.add(
_glassesPlugin.audioDataEveStm.listen((Map<String, dynamic> data) {
int frameIndex = data['frameIndex'] ?? 0;
int size = data['size'] ?? 0;
setState(() {
if (size > 0) {
_audioDataStatus = AppStrings.receivedAudioData(frameIndex, size);
} else {
_audioDataStatus = AppStrings.noAudioData;
}
});
}),
);
// 监听ACK错误
_streamSubscriptions.add(
_glassesPlugin.ackErrorEveStm.listen((Map<String, dynamic> error) {
int code = error['code'] ?? -1;
String message = error['message'] ?? AppStrings.unknownError;
bool shouldShowToast = true;
// 如果是语音唤醒相关的错误,更新状态显示
if (_voiceWakeupStatus == AppStrings.gettingStatusWithDots) {
setState(() {
_actualVoiceWakeupStatus = AppStrings.notSupportedOrErrorStatus;
_voiceWakeupStatus = AppStrings.notSupportedOrErrorStatus;
});
// 语音唤醒错误不显示Toast,避免重复提示
shouldShowToast = false;
}
// 只在非功能查询错误时显示Toast
if (shouldShowToast) {
_showToast(AppStrings.errorMessage(code.toString(), message));
}
}),
);
// 监听音频控制状态(SDK的receiveAudioState回调)
_streamSubscriptions.add(
_glassesPlugin.audioTalkStateEveStm.listen((int state) {
String stateText = AppStrings.unknownState;
String aiStatus = AppStrings.aiStatusNotStarted;
String siStatus = _simultaneousInterpretationStatus; // 保持当前同声传译状态
switch (state) {
case 0:
stateText = AppStrings.stopAudio;
aiStatus = AppStrings.aiStatusEnded; // 结束
break;
case 1:
stateText = AppStrings.startAudio;
aiStatus = AppStrings.aiStatusStarted; // 开始
break;
case 2:
stateText = AppStrings.cancelAudio;
aiStatus = AppStrings.aiStatusEnded; // 结束
break;
case 3:
stateText = AppStrings.dnsStreamStart;
siStatus = AppStrings.simultaneousInterpretationStarted; // 同声传译开始
break;
case 4:
stateText = AppStrings.dnsStreamPause;
siStatus = AppStrings.simultaneousInterpretationPaused; // 同声传译暂停
break;
case 5:
stateText = AppStrings.dnsStreamStop;
siStatus = AppStrings.simultaneousInterpretationStopped; // 同声传译停止
break;
case 6:
stateText = AppStrings.normalStreamStart;
break;
case 7:
stateText = AppStrings.normalStreamPause;
break;
case 8:
stateText = AppStrings.normalStreamStop;
break;
default:
stateText = AppStrings.unknownState;
}
setState(() {
_audioTalkState = stateText;
_actualAudioTalkState = stateText;
_aiConversationStatus = aiStatus;
_simultaneousInterpretationStatus = siStatus;
});
debugPrint('Audio control state: $stateText (value: $state)');
}),
);
// 监听音频状态
_streamSubscriptions.add(
_glassesPlugin.audioStateEveStm.listen((AudioStateBean event) {
debugPrint('Received audio state: state=${event.state}, frameIndex=${event.frameIndex}, dataSize=${event.dataSize}');
setState(() {
if (event.hasData == true && event.dataSize != null) {
// 收到音频数据
debugPrint('Received audio data: frame=${event.frameIndex}, size=${event.dataSize} bytes');
_audioDataStatus = AppStrings.receivedAudioData(event.frameIndex ?? 0, event.dataSize ?? 0);
} else {
// 只是状态变化
switch (event.state) {
case 0:
_audioDataStatus = AppStrings.audioIdle;
break;
case 1:
_audioDataStatus = AppStrings.recording;
break;
case 2:
_audioDataStatus = AppStrings.audioPaused;
break;
default:
_audioDataStatus = AppStrings.unknownAudioStatus;
}
}
});
}),
);
// 监听AI识别图片数据
_streamSubscriptions.add(
_glassesPlugin.aiImageDataEveStm.listen((AIImageData imageData) async {
debugPrint('Received AI image data: source=${imageData.source}, size=${imageData.size} bytes');
// 获取图片数据
final imageBytes = await imageData.getDisplayableData();
// 直接显示图片,无需累加
setState(() {
_displayedImage = imageBytes;
_aiImageDataStatus = AppStrings.receivedImageData(imageBytes.length);
_isImageReceiving = false; // 图片已接收完成
});
debugPrint('Image displayed, size: ${imageBytes.length} bytes');
}),
);
// 监听翻译音频数据
_streamSubscriptions.add(
_glassesPlugin.translateAudioEveStm.listen((Uint8List data) {
// data 现在是直接的二进制数据 (Uint8List)
debugPrint('收到翻译音频数据: ${data.length} bytes');
setState(() {
_translateTotalBytes += data.length;
_translateAudioStatus = AppStrings.receivedAudioData(0, _translateTotalBytes);
});
}),
);
// 监听PCM音频数据 - 支持iOS和Android平台
_streamSubscriptions.add(
_glassesPlugin.pcmAudioEveStm.listen((Uint8List data) {
// data 现在是直接的二进制数据 (Uint8List)
debugPrint('Received PCM audio data: size=${data.length} bytes');
setState(() {
_pcmTotalBytes += data.length;
_pcmAudioStatus = AppStrings.receivedAudioData(0, _pcmTotalBytes);
});
}),
);
// 监听SDK日志
debugPrint('开始监听 SDK 日志事件流...');
_streamSubscriptions.add(
_glassesPlugin.sdkLogEveStm.listen(
(String log) {
debugPrint('Received log message: $log');
// 更新状态显示,只显示最新的日志
setState(() {
// 截取日志,避免太长
String displayLog = log.length > 50 ? '${log.substring(0, 47)}...' : log;
_sdkLogStatus = displayLog;
});
// 重要日志仍然显示Toast
if (log.contains('ERROR') || log.contains('WARN')) {
_showToast(AppStrings.logMessage(log));
}
},
onError: (error) {
debugPrint('SDK 日志事件流错误: $error');
},
onDone: () {
debugPrint('SDK 日志事件流关闭');
},
),
);
// 监听运行状态
_streamSubscriptions.add(
_glassesPlugin.runningStatusEveStm.listen((Map<String, dynamic> status) {
debugPrint('Received running status event: $status');
// 将整个 status 对象转换为 JSON 字符串显示
try {
String statusJson = _mapToString(status);
setState(() {
_runningStatus = statusJson;
});
} catch (e) {
debugPrint('Failed to convert status to string: $e');
}
}),
);
// 监听电池信息
_streamSubscriptions.add(
_glassesPlugin.batteryEveStm.listen((Map<String, dynamic> batteryInfo) {
debugPrint('Received battery info event: $batteryInfo');
if (batteryInfo['error'] != null) {
debugPrint('Battery info error: ${batteryInfo['error']}');
return;
}
int batteryLevel = 0;
bool isCharging = false;
// 解析电池数据
if (batteryInfo['battery'] != null) {
batteryLevel = int.tryParse(batteryInfo['battery'].toString()) ?? 0;
}
if (batteryInfo['charging'] != null) {
isCharging = batteryInfo['charging'].toString().toLowerCase() == 'true';
}
// 只在电量变化时更新UI(避免频繁刷新)
if (_actualBatteryLevel != batteryLevel || _isCharging != isCharging) {
setState(() {
_actualBatteryLevel = batteryLevel;
_batteryLevel = AppStrings.batteryDisplay("$batteryLevel", isCharging);
_isCharging = isCharging;
});
debugPrint('Battery info updated: level=$batteryLevel%, charging=$isCharging');
}
}),
);
}
String _getConnectionStateText(int state) {
switch (state) {
case 2:
return AppStrings.connected;
case 1:
return AppStrings.connectingStatus;
case 0:
return AppStrings.disconnected;
case 3:
return AppStrings.disconnecting;
default:
return AppStrings.unknownConnectionState;
}
}
//endregion
@override
void dispose() {
for (final subscription in _streamSubscriptions) {
subscription.cancel();
}
super.dispose();
}
//endregion
//region 初始化和设置方法
/// 退出app并且断开连接
Future<bool> exitAppWithDisconnect() {
if (!isQuit) {
_showToast(AppStrings.pressAgainToExit);
isQuit = true;
return Future.value(false);
}
SystemChannels.platform.invokeMethod('SystemNavigator.pop');
return Future.value(true);
}
//region UI布局 - build方法
@override
Widget build(BuildContext context) {
return MaterialApp(
navigatorKey: _navigatorKey,
locale: _locale,
localizationsDelegates: const [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: const [
Locale('zh', 'CN'),
Locale('en', 'US'),
],
home: Builder(
builder: (context) {
_materialContext = context;
return Scaffold(
backgroundColor: Colors.grey[100],
appBar: AppBar(
title: Text(AppStrings.title,
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w600)),
backgroundColor: Colors.white,
foregroundColor: Colors.black,
elevation: 0,
systemOverlayStyle: SystemUiOverlayStyle.dark,
actions: [
PopupMenuButton<Locale>(
icon: const Icon(Icons.language),
onSelected: _changeLanguage,
itemBuilder: (context) => [
PopupMenuItem(
value: const Locale('zh', 'CN'),
child: Text(AppStrings.chinese),
),
PopupMenuItem(
value: const Locale('en', 'US'),
child: Text(AppStrings.english),
),
],
),
],
),
body: WillPopScope(
onWillPop: () => exitAppWithDisconnect(),
child: SafeArea(
child: ListView(
padding: const EdgeInsets.all(16),
children: [
// 状态卡片
_buildStatusCard(),
const SizedBox(height: 20),
// 双插件验证入口
_buildDualPluginEntrySection(),
const SizedBox(height: 20),
// 基础功能
_buildBasicFunctionSection(),
const SizedBox(height: 20),
// 佩戴检查
_buildWearCheckSection(),
// 眼镜功能
_buildGlassesSection(),
const SizedBox(height: 20),
// AI 对话监听
_buildAIConversationListenSection(),
const SizedBox(height: 20),
// 同声传译
_buildSimultaneousInterpretationSection(),
const SizedBox(height: 20),
// 媒体文件管理
_buildMediaFileSection(),
const SizedBox(height: 20),
// 录音功能
_buildRecordSection(),
const SizedBox(height: 20),
// 版本信息 & OTA 升级
_buildVersionOtaSection(),
const SizedBox(height: 20),
// SDK 日志显示
_buildSDKLogSection(),
const SizedBox(height: 20),
// 设备管理功能
_buildDeviceManagementSection(),
const SizedBox(height: 20),
],
),
),
),
);
},
),
);
}
Widget _buildStatusCard() {
return Card(
elevation: 2,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(Icons.info_outline, color: Colors.blue[600]),
const SizedBox(width: 8),
Text(
AppStrings.deviceStatus,
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w600),
),
],
),
const SizedBox(height: 12),
_buildStatusRow(AppStrings.connectionState, _connectionStatus,
_isConnected ? Colors.green : Colors.red),
_buildStatusRow(AppStrings.batteryLevel, _batteryLevel, Colors.orange),
_buildStatusRow(AppStrings.firmwareVersion, _deviceVersion, Colors.blue),
// 显示设备名称和 MAC 地址
if (_cachedDeviceName != null && _cachedMacAddress != null) ...[
const SizedBox(height: 8),
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.grey[100],
borderRadius: BorderRadius.circular(8),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(Icons.devices, size: 16, color: Colors.grey[600]),
const SizedBox(width: 4),
Text(
AppStrings.connectedDevice,
style: TextStyle(
fontSize: 12,
color: Colors.grey[600],
fontWeight: FontWeight.w500,
),
),
],
),
const SizedBox(height: 4),
Text(
_cachedDeviceName!,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
),
),
Text(
_cachedMacAddress!,
style: TextStyle(
fontSize: 12,
color: Colors.grey[600],
),
),
],
),
),
],
],
),
),
);
}
Widget _buildBasicFunctionSection() {
return _buildSectionCard(
title: AppStrings.basicFunctions,
icon: Icons.settings,
children: [
_buildApiButton(
AppStrings.bluetoothCheck,
Icons.security,
requestPermissions,
subtitle: _permissionTxt,
),
Builder(
builder: (context) => _buildApiButton(
AppStrings.scanDevice,
Icons.bluetooth_searching,
() => _navigateToScanPage(context),
subtitle: AppStrings.scanAndConnect,
),
),
_buildApiButton(
AppStrings.bluetoothStatus,
Icons.bluetooth,
() {},
subtitle: _bluetoothState,
enabled: false,
),
_buildApiButton(
AppStrings.connectionStatus,
Icons.link,
_updateConnectionState,
subtitle: _connectionStatus,
),
_buildApiButton(
AppStrings.getDeviceUUID,
Icons.numbers,
_getDeviceUUID,
subtitle: _deviceUUIDStatus,
),
_buildApiButton(
AppStrings.batteryLevel,
Icons.battery_full,
_queryBattery,
subtitle: _batteryLevel,
),
_buildApiButton(
AppStrings.firmwareVersion,
Icons.info,
_queryDeviceVersion,
subtitle: _deviceVersion,
),
// 语言设置
Row(
children: [
Expanded(
child: _buildApiButton(
AppStrings.setLanguage,
Icons.language,
() => _showLanguageDialog(),
subtitle: _languageStatus,
enabled: _isConnected,
),
),
const SizedBox(width: 12),
Expanded(
child: _buildApiButton(
AppStrings.getLanguage,
Icons.translate,
_getLanguage,
subtitle: _getLanguageStatus,
enabled: _isConnected,
),
),
],
),
],
);
}
Widget _buildDualPluginEntrySection() {
return _buildSectionCard(
title: AppStrings.dualPluginVerification,
icon: Icons.devices,
children: [
Builder(
builder: (BuildContext context) => _buildApiButton(
AppStrings.enterDualPluginPage,
Icons.open_in_new,
() {
Navigator.push(
context,
MaterialPageRoute(
builder: (BuildContext context) => const DualPluginExample(),
),
);
},
subtitle: AppStrings.verifyGlassesWatchTogether,
),
),
],
);
}
Widget _buildWearCheckSection() {
return _buildSectionCard(
title: AppStrings.wearCheck,
icon: Icons.watch,
children: [
_buildApiButton(
AppStrings.queryWearCheckState,
Icons.visibility,
_queryWearCheckState,
subtitle: _wearCheckState,
enabled: _isConnected,
),
_buildApiButton(
AppStrings.setWearCheckState,
Icons.settings,
_showWearCheckDialog,
subtitle: AppStrings.setWearCheckDesc,
enabled: _isConnected,
),
],
);
}
Widget _buildGlassesSection() {
return _buildSectionCard(
title: AppStrings.glassesFeatures,
icon: Icons.camera,
children: [
// 拍照模式选择
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.green.withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.green.withOpacity(0.3)),
),
child: Row(
children: [
Icon(Icons.photo_camera, color: Colors.green[600], size: 20),
const SizedBox(width: 8),
Text(
AppStrings.photoMode + ':',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Colors.green[600],
),
),
const SizedBox(width: 12),
Expanded(
child: DropdownButton<int>(
value: _selectedPhotoMode,
isExpanded: true,
items: [
DropdownMenuItem(value: 0, child: Text(AppStrings.normalPhoto)),
DropdownMenuItem(value: 1, child: Text(AppStrings.aiRecognitionPhoto)),
DropdownMenuItem(value: 2, child: Text(AppStrings.continuousPhoto)),
],
onChanged: (int? value) {
if (value != null) {
setState(() {
_selectedPhotoMode = value;
});
}
},
),
),
],
),
),
const SizedBox(height: 8),
_buildApiButton(
AppStrings.takePhoto,
Icons.camera_alt,
() => _takePhoto(photoMode: _selectedPhotoMode),
subtitle: AppStrings.controlPhoto,
enabled: _isConnected,
),
// 视频功能暂时禁用
/*
_buildApiButton(
AppStrings.startVideo,
Icons.videocam,
_startVideo,
subtitle: AppStrings.startVideoFunction,
enabled: _isConnected,
),
_buildApiButton(
AppStrings.stopVideo,
Icons.videocam_off,
_stopVideo,
subtitle: AppStrings.stopVideoFunction,
enabled: _isConnected,
),
*/
],
);
}
Widget _buildAIConversationListenSection() {
return _buildSectionCard(
title: AppStrings.aiConversationListen,
icon: Icons.hearing,
children: [
// AI对话状态显示
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.purple.withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.purple.withOpacity(0.3)),
),
child: Row(
children: [
Icon(Icons.psychology, color: Colors.purple[600], size: 20),
const SizedBox(width: 8),
Text(
AppStrings.aiConversationStatus,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Colors.purple[600],
),
),
const SizedBox(width: 16),
Expanded(
flex: 2,
child: Text(
_aiConversationStatus,
textAlign: TextAlign.right,
style: TextStyle(
fontSize: 13,
color: Colors.purple[700],
fontWeight: FontWeight.w500,
),
overflow: TextOverflow.ellipsis,
maxLines: 2,
),
),
],
),
),
const SizedBox(height: 8),
// PCM 音频数据状态显示
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.orange.withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.orange.withOpacity(0.3)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(Icons.mic, color: Colors.orange[600], size: 20),
const SizedBox(width: 8),
Text(
AppStrings.pcmAudioStatus,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Colors.orange[600],
),
),
],
),
const SizedBox(height: 6),
Text(
_pcmAudioStatus,
style: TextStyle(
fontSize: 13,
color: Colors.orange[700],
fontWeight: FontWeight.w500,
),
),
],
),
),
const SizedBox(height: 8),
// AI识别图片数据状态显示
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.blue.withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.blue.withOpacity(0.3)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(Icons.image, color: Colors.blue[600], size: 20),
const SizedBox(width: 8),
Text(
AppStrings.aiImageDataStatus,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Colors.blue[600],
),
),
],
),
const SizedBox(height: 6),
Text(
_aiImageDataStatus,
style: TextStyle(
fontSize: 13,
color: Colors.blue[700],
fontWeight: FontWeight.w500,
),
),
const SizedBox(height: 6),
// 显示接收到的图片
if (_displayedImage != null)
Container(
margin: const EdgeInsets.only(top: 8),
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.grey[50],
borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.grey[300]!),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(Icons.image, color: Colors.grey[600], size: 16),
const SizedBox(width: 4),
Text(
'AI识别图片',
style: TextStyle(
fontSize: 12,
color: Colors.grey[600],
fontWeight: FontWeight.w500,
),
),
const Spacer(),
GestureDetector(
onTap: () {
setState(() {
_displayedImage = null;
_aiImageDataStatus = AppStrings.noAiImageData;
});
},
child: Icon(
Icons.close,
size: 16,
color: Colors.grey[500],
),
),
],
),
const SizedBox(height: 8),
ClipRRect(
borderRadius: BorderRadius.circular(4),
child: Image.memory(
_displayedImage!,
fit: BoxFit.contain,
height: 200,
width: double.infinity,
errorBuilder: (context, error, stackTrace) {
return Container(
height: 200,
color: Colors.grey[200],
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.broken_image, color: Colors.grey[400], size: 40),
const SizedBox(height: 8),
Text(
'图片显示失败',
style: TextStyle(color: Colors.grey[500], fontSize: 12),
),
],
),
),
);
},
),
),
const SizedBox(height: 8),
Text(
'图片大小: ${_displayedImage!.length} bytes',
style: TextStyle(
fontSize: 10,
color: Colors.grey[500],
),
),
],
),
),
const SizedBox(height: 8),
// AI 回复状态控制
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.purple.withOpacity(0.05),
borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.purple.withOpacity(0.2)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(Icons.psychology_alt, color: Colors.purple[600], size: 20),
const SizedBox(width: 8),
Text(
AppStrings.aiReplyStatusSet(''),
style: TextStyle(fontSize: 13, fontWeight: FontWeight.w600, color: Colors.purple[600]),
),
],
),
const SizedBox(height: 8),
Row(
children: [
Expanded(
child: DropdownButton<int>(
value: _selectedAIReplyStatus,
isExpanded: true,
items: [
DropdownMenuItem(value: 0, child: Text(AppStrings.startAiReply)),
DropdownMenuItem(value: 1, child: Text(AppStrings.completeAiReply)),
DropdownMenuItem(value: 2, child: Text(AppStrings.interruptAiReply)),
],
onChanged: (int? value) {
if (value != null) {
setState(() {
_selectedAIReplyStatus = value;
});
}
},
),
),
const SizedBox(width: 12),
ElevatedButton.icon(
onPressed: _isConnected ? () => _setAIReplyStatus(_selectedAIReplyStatus) : null,
icon: const Icon(Icons.send, size: 18),
label: Text(AppStrings.setAIReplyStatusBtn),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.purple[600],
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
),
),
],
),
],
),
),
_buildApiButton(
AppStrings.exitVoice,
Icons.voice_over_off,
_exitAIReply,
enabled: _isConnected,
),
],
),
),
],
);
}
Widget _buildSimultaneousInterpretationSection() {
return _buildSectionCard(
title: AppStrings.simultaneousInterpretation,
icon: Icons.record_voice_over,
children: [
// 同声传译状态显示(参照AI对话状态样式)
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.teal.withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.teal.withOpacity(0.3)),
),
child: Row(
children: [
Icon(Icons.record_voice_over, color: Colors.teal[600], size: 20),
const SizedBox(width: 8),
Text(
AppStrings.simultaneousInterpretationStatus,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Colors.teal[600],
),
),
const SizedBox(width: 16),
Expanded(
flex: 2,
child: Text(
_simultaneousInterpretationStatus,
textAlign: TextAlign.right,
style: TextStyle(
fontSize: 13,
color: Colors.teal[700],
fontWeight: FontWeight.w500,
),
overflow: TextOverflow.ellipsis,
maxLines: 2,
),
),
],
),
),
const SizedBox(height: 16),
// 同声传译控制(参照AI回复的UI处理方式)
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.grey.withOpacity(0.05),
borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.grey.withOpacity(0.2)),
),
child: Row(
children: [
Icon(Icons.tune, color: Colors.grey[600], size: 20),
const SizedBox(width: 8),
Text(
AppStrings.simultaneousInterpretation + ':',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Colors.grey[600],
),
),
const SizedBox(width: 12),
Expanded(
child: DropdownButton<int>(
value: _selectedSimultaneousInterpretationAction,
isExpanded: true,
items: [
DropdownMenuItem(value: 3, child: Text(AppStrings.startSimultaneousInterpretation)),
DropdownMenuItem(value: 4, child: Text(AppStrings.pauseSimultaneousInterpretation)),
DropdownMenuItem(value: 5, child: Text(AppStrings.stopSimultaneousInterpretation)),
],
onChanged: _isConnected ? (value) {
setState(() {
_selectedSimultaneousInterpretationAction = value!;
});
} : null,
),
),
const SizedBox(width: 12),
ElevatedButton(
onPressed: _isConnected ? _setSimultaneousInterpretation : null,
child: Text(AppStrings.send),
),
],
),
),
const SizedBox(height: 16),
// 翻译音频数据状态显示
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.green.withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.green.withOpacity(0.3)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(Icons.translate, color: Colors.green[600], size: 20),
const SizedBox(width: 8),
Text(
AppStrings.translationAudioData,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Colors.green[600],
),
),
],
),
const SizedBox(height: 8),
Text(
_translateAudioStatus,
style: TextStyle(
fontSize: 13,
color: Colors.green[700],
fontWeight: FontWeight.w500,
),
overflow: TextOverflow.ellipsis,
maxLines: 2,
),
],
),
),
],
);
}
Widget _buildMediaFileSection() {
return _buildSectionCard(
title: AppStrings.mediaFileManagement,
icon: Icons.folder,
children: [
Builder(
builder: (context) => _buildApiButton(
AppStrings.openFileManager,
Icons.folder_open,
() => _navigateToMediaFilePage(context),
subtitle: AppStrings.manageDownloadFiles,
enabled: _isConnected,
),
),
],
);
}
Widget _buildRecordSection() {
return _buildSectionCard(
title: AppStrings.recordFunction,
icon: Icons.mic,
children: [
_buildApiButton(
AppStrings.setRecordControl,
Icons.fiber_manual_record,
_startAudioRecord,
subtitle: AppStrings.setRecordControl,
enabled: _isConnected,
),
_buildApiButton(
AppStrings.stopRecordControl,
Icons.stop,
_stopAudioRecord,
subtitle: AppStrings.stopRecordControl,
enabled: _isConnected,
),
_buildApiButton(
AppStrings.getRecordStatus,
Icons.info,
_queryAudioRecordState,
subtitle: _audioRecordStatus,
enabled: _isConnected,
),
],
);
}
Widget _buildVersionOtaSection() {
return _buildSectionCard(
title: AppStrings.versionInfo,
icon: Icons.info,
children: [
Builder(
builder: (context) => _buildApiButton(
AppStrings.versionAndOta,
Icons.system_update,
() => _navigateToVersionOtaPage(context),
subtitle: AppStrings.versionAndOtaSubtitle,
enabled: _isConnected,
),
),
],
);
}
Widget _buildSDKLogSection() {
return _buildSectionCard(
title: AppStrings.sdkLog,
icon: Icons.bug_report,
children: [
// SDK日志显示
Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
decoration: BoxDecoration(
color: Colors.grey.withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.grey.withOpacity(0.3)),
),
child: Row(
children: [
Icon(Icons.bug_report, size: 20, color: Colors.grey[700]),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
AppStrings.sdkLatestLog,
style: TextStyle(
fontSize: 12,
color: Colors.grey[600],
),
),
const SizedBox(height: 4),
Text(
_sdkLogStatus,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Colors.grey[700],
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
],
),
),
],
),
),
],
);
}
Widget _buildDeviceManagementSection() {
return _buildSectionCard(
title: AppStrings.deviceManagementFunction,
icon: Icons.settings_applications,
children: [
_buildApiButton(
AppStrings.disconnectDevice,
Icons.bluetooth_disabled,
_disconnectDevice,
subtitle: AppStrings.disconnectDevice,
enabled: _isConnected,
),
_buildApiButton(
AppStrings.clearPairInfo,
Icons.link_off,
_clearPairInfo,
subtitle: AppStrings.clearPairInfo,
enabled: _isConnected,
),
_buildApiButton(
AppStrings.getDeviceUUID,
Icons.fingerprint,
_getDeviceUUID,
subtitle: _deviceUUIDStatus,
enabled: _isConnected,
),
_buildApiButton(
AppStrings.getVoiceWakeupStatus,
Icons.voice_over_off,
_getVoiceWakeupState,
subtitle: _voiceWakeupStatus,
enabled: _isConnected,
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
AppStrings.setVoiceWakeup,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: Colors.grey[800],
),
),
const SizedBox(height: 12),
Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
AppStrings.operationType,
style: TextStyle(
fontSize: 12,
color: Colors.grey[600],
),
),
const SizedBox(height: 4),
DropdownButton<int>(
value: _selectedVoiceWakeupAction,
isExpanded: true,
hint: Text('请选择操作类型'),
items: [
DropdownMenuItem(value: 0, child: Text(AppStrings.enableVoiceWakeupTypeOn)),
DropdownMenuItem(value: 1, child: Text(AppStrings.disableVoiceWakeupTypeOff)),
],
onChanged: _isConnected ? (value) {
setState(() {
_selectedVoiceWakeupAction = value!;
});
} : null,
),
],
),
),
],
),
const SizedBox(height: 12),
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: (_isConnected && _selectedVoiceWakeupAction != null) ? _setVoiceWakeup : null,
child: Text(_selectedVoiceWakeupAction == 0 ? AppStrings.enableVoiceWakeup : (_selectedVoiceWakeupAction == 1 ? AppStrings.disableVoiceWakeup : AppStrings.setVoiceWakeup)),
),
),
],
),
),
_buildApiButton(
AppStrings.getRunningStatus,
Icons.info_outline,
_getRunningStatus,
subtitle: _runningStatus,
enabled: _isConnected,
),
_buildApiButton(
AppStrings.restartDevice,
Icons.refresh,
_restartDevice,
subtitle: AppStrings.restartDevice,
enabled: _isConnected,
),
Row(
children: [
Expanded(
child: _buildApiButton(
AppStrings.shutdownDevice,
Icons.power_off,
_shutdownDevice,
subtitle: AppStrings.shutdownDevice,
enabled: _isConnected,
),
),
const SizedBox(width: 8),
Expanded(
child: _buildApiButton(
AppStrings.factoryReset,
Icons.restore,
_resetDevice,
subtitle: AppStrings.factoryReset,
enabled: _isConnected,
),
),
],
),
],
);
}
//endregion
//region 媒体文件管理
/// 导航到媒体文件管理页面
void _navigateToMediaFilePage(BuildContext context) {
if (!mounted) return;
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => MediaFilePage(
glassesPlugin: _glassesPlugin,
),
),
);
}
/// 导航到版本信息 & OTA 升级页面
void _navigateToVersionOtaPage(BuildContext context) {
if (!mounted) return;
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => VersionOtaPage(
glassesPlugin: _glassesPlugin,
isConnected: _isConnected,
cachedMacAddress: _cachedMacAddress,
),
),
);
}
//endregion
//region UI构建辅助方法
// ==================== UI 构建方法 ====================
void _showToast(String message) {
ToastUtil.showToast(message);
}
Widget _buildStatusRow(String label, String value, Color color) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(label, style: const TextStyle(fontSize: 14, color: Colors.grey)),
Expanded(
child: Text(
value,
textAlign: TextAlign.right,
overflow: TextOverflow.ellipsis,
maxLines: 2,
style: TextStyle(fontSize: 14, fontWeight: FontWeight.w500, color: color),
),
),
],
),
);
}
Widget _buildSectionCard({
required String title,
required IconData icon,
required List<Widget> children,
}) {
return Card(
elevation: 2,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(icon, color: Colors.blue[600]),
const SizedBox(width: 8),
Text(
title,
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w600),
),
],
),
const SizedBox(height: 12),
...children,
],
),
),
);
}
Widget _buildApiButton(
String title,
IconData icon,
VoidCallback onPressed,
{
String? subtitle,
bool enabled = true,
}) {
return Container(
margin: const EdgeInsets.symmetric(vertical: 4),
child: Material(
color: enabled ? Colors.white : Colors.grey[100],
borderRadius: BorderRadius.circular(8),
child: InkWell(
borderRadius: BorderRadius.circular(8),
onTap: enabled ? onPressed : null,
child: Padding(
padding: const EdgeInsets.all(12),
child: Row(
children: [
Icon(
icon,
color: enabled ? Colors.blue[600] : Colors.grey,
size: 24,
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: enabled ? Colors.black87 : Colors.grey,
),
),
if (subtitle != null)
Text(
subtitle,
style: TextStyle(
fontSize: 12,
color: enabled ? Colors.grey[600] : Colors.grey,
),
),
],
),
),
Icon(
Icons.chevron_right,
color: enabled ? Colors.grey[400] : Colors.grey[300],
),
],
),
),
),
),
);
}
//endregion
//region API调用方法 - 设备连接和控制
// ==================== API 调用方法 ====================
void requestPermissions() {
[
Permission.location,
Permission.storage,
Permission.manageExternalStorage,
Permission.bluetoothConnect,
Permission.bluetoothScan,
Permission.bluetoothAdvertise
].request().then((value) => {
setState(() {
Map<Permission, PermissionStatus> statuses = value;
if (statuses[Permission.location] == PermissionStatus.denied) {
_permissionTxt = AppStrings.locationPermissionDenied;
return;
}
if (statuses[Permission.storage] == PermissionStatus.denied) {
_permissionTxt = AppStrings.storagePermissionDenied;
return;
}
_permissionTxt = AppStrings.permissionGranted;
})
});
}
void _navigateToScanPage(BuildContext context) async {
if (!mounted) return;
final result = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const GlassesScanPage(),
),
);
// 处理连接结果
if (result != null && result is Map<String, dynamic>) {
if (result['connected'] == true) {
final device = result['device'] as BleScanBean;
_showToast(AppStrings.connectedTo(device.name.isEmpty ? device.address : device.name));
// 连接成功后更新基本信息
// 注意:不要手动设置 _isConnected,让事件流来更新状态
_updateConnectionState();
}
}
}
/// 检查初始蓝牙状态
void _checkInitialBluetoothState() async {
try {
bool isEnabled = await _glassesPlugin.checkBluetoothEnable;
// 根据返回值设置初始状态
setState(() {
_bluetoothState = isEnabled ? AppStrings.bluetoothEnabled : AppStrings.bluetoothDisabled;
});
debugPrint('Initial Bluetooth state: $_bluetoothState');
} catch (e) {
debugPrint('Failed to check initial Bluetooth state: $e');
setState(() {
_bluetoothState = AppStrings.checkFailed;
});
}
}
// 标记是否已经完成初始连接检查
bool _hasCheckedInitialConnection = false;
/// 检查初始连接状态,解决时序问题
void _checkInitialConnectionState() async {
// 如果已经检查过且状态已同步,则跳过
if (_hasCheckedInitialConnection && _isConnected) {
debugPrint('Initial connection already checked and synced');
return;
}
try {
debugPrint('Checking initial connection state...');
// 检查是否有缓存的设备信息
if (_cachedMacAddress != null && _cachedMacAddress!.isNotEmpty) {
debugPrint('Found cached device, checking connection status...');
// 如果有缓存的设备信息,尝试查询电池来判断是否真的连接
try {
await _glassesPlugin.queryBattery();
debugPrint('Battery query successful, device is connected');
if (!_isConnected) {
setState(() {
_isConnected = true;
_connectionStatus = AppStrings.connected;
});
debugPrint('Updated UI state to connected');
_hasCheckedInitialConnection = true;
_queryBattery();
_queryDeviceVersion();
}
} catch (e) {
debugPrint('Battery query failed, device may not be connected: $e');
// 只有在第一次检查失败时才更新状态
if (!_hasCheckedInitialConnection) {
setState(() {
_isConnected = false;
_connectionStatus = AppStrings.disconnected;
});
}
}
} else {
debugPrint('No cached device found');
_hasCheckedInitialConnection = true;
}
} catch (e) {
debugPrint('Failed to check initial connection state: $e');
}
}
void _updateConnectionState() async {
// 更新 UI 状态显示,基于当前的连接状态
_showToast(_isConnected ? AppStrings.deviceConnected : AppStrings.deviceNotConnected);
// 如果已连接,加载缓存的设备信息并查询一些基本信息
if (_isConnected) {
// 重新加载缓存的设备信息(可能在扫描页面更新了)
await _loadCachedMacAddress();
_queryBattery();
_queryDeviceVersion();
}
}
void _reconnectDevice() async {
try {
debugPrint('Attempting to reconnect device...');
_showToast(AppStrings.reconnecting);
await _glassesPlugin.reconnect();
_showToast(AppStrings.reconnectCommandSent);
debugPrint('Reconnect command sent');
} catch (e) {
debugPrint('Reconnect failed: $e');
_showToast(AppStrings.reconnectFailed + ": $e");
}
}
void _disconnectDevice() async {
try {
await _glassesPlugin.disconnect();
_showToast(AppStrings.disconnectedSuccess);
} catch (e) {
_showToast(AppStrings.disconnectFailed + ": $e");
}
}
void _removeDevice() async {
try {
await _glassesPlugin.disconnect();
setState(() {
_isConnected = false;
_batteryLevel = AppStrings.unknown;
_isCharging = false;
_deviceVersion = AppStrings.unknown;
_cachedMacAddress = null; // 清除缓存的 MAC 地址
_cachedDeviceName = null; // 清除缓存的设备名称
});
// 清除 MAC 地址缓存
await MacAddressCache.clearCachedMac();
_showToast(AppStrings.deviceRemovedAndDisconnected);
} catch (e) {
_showToast(AppStrings.removeDeviceFailed + ": $e");
}
}
void _syncTime() async {
try {
await _glassesPlugin.syncTime();
_showToast(AppStrings.timeSyncSuccess);
} catch (e) {
_showToast(AppStrings.timeSyncFailed + ": $e");
}
}
void _queryDeviceVersion() async {
try {
String version = await _glassesPlugin.queryDeviceVersion(versionType: 1);
setState(() {
_deviceVersion = "${AppStrings.firmwareVersion}: $version";
});
_showToast(AppStrings.versionQuerySuccess + ": $version");
} catch (e) {
_showToast(AppStrings.versionQueryFailed + ": $e");
}
}
void _queryBattery() async {
try {
Map<String, dynamic> batteryInfo = await _glassesPlugin.queryBattery();
debugPrint('Battery query returned data: $batteryInfo');
int batteryLevel = 0;
bool isCharging = false;
// 处理返回的数据
if (batteryInfo['battery'] != null) {
batteryLevel = int.tryParse(batteryInfo['battery'].toString()) ?? 0;
}
if (batteryInfo['charging'] != null) {
isCharging = batteryInfo['charging'].toString().toLowerCase() == 'true';
}
setState(() {
_actualBatteryLevel = batteryLevel;
_batteryLevel = AppStrings.batteryDisplay("$batteryLevel", isCharging);
_isCharging = isCharging;
});
_showToast(AppStrings.batteryLevelToast("${batteryLevel}%", isCharging));
} catch (e) {
debugPrint('Battery query exception: $e');
_showToast(AppStrings.batteryQueryFailed + ": $e");
}
}
void _restartDevice() async {
try {
_showToast('正在重启设备...');
bool success = await _glassesPlugin.restart();
_showToast(success ? AppStrings.deviceRestartSuccess : AppStrings.deviceRestartFailed);
} catch (e) {
_showToast(AppStrings.deviceRestartFailed + ": $e");
}
}
void _takePhoto({int photoMode = 0}) async {
try {
await _glassesPlugin.takePhoto(photoMode: photoMode);
_showToast(AppStrings.photoCommandSent);
} catch (e) {
debugPrint('takePhoto failed: $e');
_showToast(AppStrings.photoFailed + ": $e");
}
}
// 暂时禁用
// void _startVideo() async {
// try {
// await _glassesPlugin.startVideo(fps: 30, maxDuration: 10);
// } catch (e) {
// _showToast(AppStrings.videoStartFailed + ": $e");
// }
// }
// 暂时禁用
// void _stopVideo() async {
// try {
// await _glassesPlugin.stopVideo();
// } catch (e) {
// _showToast(AppStrings.videoStopFailed + ": $e");
// }
// }
void _setVideoConfig() async {
try {
// 使用用户选择的录像参数
Map<String, dynamic> config = {
"frameRate": _selectedFrameRate,
"duration": _selectedMaxDuration, // 注意:iOS端使用 duration 而不是 maxDuration
};
// await _glassesPlugin.sendVideoConfig(config); // 暂时禁用
_showToast("视频配置功能暂时禁用");
String fpsText = _selectedFrameRate == 0 ? AppStrings.notSet : '$_selectedFrameRate fps';
_showToast(AppStrings.videoParamsSet(fpsText, _selectedMaxDuration));
} catch (e) {
_showToast(AppStrings.setVideoParamsFailed + ": $e");
}
}
void _sendLanguage() async {
try {
await _glassesPlugin.sendLanguage(1); // 1 = 中文
_showToast(AppStrings.languageSettingsSent);
} catch (e) {
_showToast(AppStrings.sendLanguageSettingsFailed + ": $e");
}
}
void _resetDevice() async {
try {
_showToast('正在重置设备...');
bool success = await _glassesPlugin.reset();
if (success) {
_showToast(AppStrings.deviceResetSuccess);
// 重置后断开连接
setState(() {
_isConnected = false;
_connectionStatus = AppStrings.disconnected;
});
} else {
_showToast(AppStrings.deviceResetFailed);
}
} catch (e) {
_showToast(AppStrings.deviceResetFailed + ": $e");
}
}
void _shutdownDevice() async {
try {
_showToast('正在关闭设备...');
bool success = await _glassesPlugin.shutdown();
if (success) {
_showToast(AppStrings.deviceShutdownSuccess);
// 关机后断开连接
setState(() {
_isConnected = false;
_connectionStatus = AppStrings.disconnected;
});
} else {
_showToast(AppStrings.deviceShutdownFailed);
}
} catch (e) {
_showToast(AppStrings.deviceShutdownFailed2 + ": $e");
}
}
/// 查询佩戴检查状态
void _queryWearCheckState() async {
debugPrint('Querying wear check state...');
try {
bool state = await _glassesPlugin.getWearCheckState();
debugPrint('Wear check state: $state');
setState(() {
_wearCheckState = state ? AppStrings.enabled : AppStrings.disabled;
});
_showToast(AppStrings.wearCheckStateToast(state ? AppStrings.enabled : AppStrings.disabled));
} catch (e) {
debugPrint('Query wear check failed: $e');
_showToast(AppStrings.queryWearCheckFailed + ": $e");
}
}
/// 显示佩戴检查设置对话框
void _showWearCheckDialog() {
final dialogContext = _materialContext ?? context;
showDialog(
context: dialogContext,
builder: (BuildContext context) {
// 根据当前状态设置初始值
bool enableWearCheck = _wearCheckState == AppStrings.enabled;
return AlertDialog(
title: Text(AppStrings.setWearCheckState),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(AppStrings.wearCheckDialogDesc),
const SizedBox(height: 16),
StatefulBuilder(
builder: (context, setState) {
return SwitchListTile(
title: Text(AppStrings.enableWearCheck),
value: enableWearCheck,
onChanged: (bool value) {
setState(() {
enableWearCheck = value;
});
},
);
},
),
],
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text('取消'),
),
ElevatedButton(
onPressed: () async {
Navigator.of(context).pop();
try {
await _glassesPlugin.setWearCheckState(enable: enableWearCheck);
setState(() {
_wearCheckState = enableWearCheck ? AppStrings.enabled : AppStrings.disabled;
});
_showToast(AppStrings.wearCheckStateToast(enableWearCheck ? AppStrings.enabled : AppStrings.disabled));
} catch (e) {
_showToast(AppStrings.setWearCheckFailed + ": $e");
}
},
child: Text(AppStrings.confirm),
),
],
);
},
);
}
//endregion
//region 音频控制方法
// ==================== 音频控制方法 ====================
/// 设置音频控制状态
/// 调用 SDK 的 setAudioCtrl 方法,传入用户选择的音频动作类型
void _startAudio() async {
try {
await _glassesPlugin.setAudioControl(actionType: _selectedAudioAction);
String actionText = '';
switch (_selectedAudioAction) {
case 1:
actionText = AppStrings.startAudio;
break;
case 2:
actionText = AppStrings.cancelAudio;
break;
case 3:
actionText = AppStrings.dnsStreamStart;
break;
case 4:
actionText = AppStrings.dnsStreamPause;
break;
case 5:
actionText = AppStrings.dnsStreamStop;
break;
case 6:
actionText = AppStrings.normalStreamStart;
break;
case 7:
actionText = AppStrings.normalStreamPause;
break;
case 8:
actionText = AppStrings.normalStreamStop;
break;
}
_showToast(AppStrings.audioControlSet(actionText));
} catch (e) {
_showToast(AppStrings.setAudioControlFailed + ": $e");
}
}
/// 停止音频
/// 调用 SDK 的 setAudioCtrl 方法,传入 audioStop
void _stopAudio() async {
try {
await _glassesPlugin.setAudioControl(actionType: 0); // 0 = audioStop
_showToast(AppStrings.audioStopped);
} catch (e) {
_showToast(AppStrings.stopAudioFailed + ": $e");
}
}
/// 设置 AI 回复状态
/// status: 0-开始, 1-完成, 2-中断
void _setAIReplyStatus(int status) async {
try {
bool success = await _glassesPlugin.setAIReplyStatus(status: status);
String statusText = '';
switch (status) {
case 0:
statusText = AppStrings.startAiReply;
break;
case 1:
statusText = AppStrings.completeAiReply;
break;
case 2:
statusText = AppStrings.interruptAiReply;
break;
}
if (success) {
_showToast(AppStrings.aiReplyStatusSet(statusText));
} else {
_showToast(AppStrings.setAiReplyStatusFailed);
}
} catch (e) {
_showToast(AppStrings.setAiReplyStatusFailed + ": $e");
}
}
/// 退出 AI 对话
void _exitAIReply() async {
try {
bool success = await _glassesPlugin.exitAIReply();
if (success) {
_showToast(AppStrings.exitVoice);
} else {
_showToast(AppStrings.exitVoiceFailed);
}
} catch (e) {
_showToast(AppStrings.exitVoiceFailed + ": $e");
}
}
/// 查询音频状态
/// 调用 SDK 的 queryAudioState 方法
void _queryAudioState() async {
try {
int state = await _glassesPlugin.queryAudioState();
String stateText = state == 1 ? AppStrings.inIntercom : AppStrings.notIntercom;
_showToast(AppStrings.audioStatus(stateText));
} catch (e) {
_showToast(AppStrings.queryAudioStateFailed + ": $e");
}
}
//endregion
//region AI功能方法
// ==================== 文件管理功能方法 ====================
/// 查询文件数量
/// 调用 SDK 的 getFileCount 方法
void _queryFileCount() async {
try {
await _glassesPlugin.getFileCount();
_showToast('已触发文件数量查询,请从 event_media_file_count 事件获取最新数量');
} catch (e) {
_showToast(AppStrings.queryFileCountFailed + ": $e");
}
}
/// 查询文件同步方式
/// 调用 SDK 的 getFileSyncType 方法
void _queryFileSyncType() async {
try {
int type = await _glassesPlugin.getFileSyncType();
String typeStr = type == 0 ? AppStrings.httpGetMode : AppStrings.otherMode;
_showToast(AppStrings.currentFileSyncMethod(typeStr));
} catch (e) {
_showToast(AppStrings.queryFileSyncMethodFailed + ": $e");
}
}
/// 删除文件(已废弃,下载完成后自动删除)
/// 调用 SDK 的 deleteFile 方法
// void _deleteMediaFile() async {
// try {
// // 示例:删除一个图片文件
// // 实际使用时应该从文件列表中选择
// bool success = await _glassesPlugin.deleteFile(
// fileType: 1, // 0-删除所有文件, 1-按名称删除, 2-按类型删除
// fileName: "example.jpg",
// );
// if (success) {
// _showToast(AppStrings.deleteFileSuccess);
// } else {
// _showToast(AppStrings.deleteFileFailed);
// }
// } catch (e) {
// _showToast(AppStrings.deleteMediaFileFailed + ": $e");
// }
// }
//endregion
//region 同声传译方法
/// 设置同声传译状态
/// 调用 SDK 的 setAudioCtrl 方法,传入用户选择的同声传译动作类型
void _setSimultaneousInterpretation() async {
try {
await _glassesPlugin.setAudioControl(actionType: _selectedSimultaneousInterpretationAction);
String actionText = '';
switch (_selectedSimultaneousInterpretationAction) {
case 3:
actionText = AppStrings.startSimultaneousInterpretation;
setState(() {
_simultaneousInterpretationStatus = AppStrings.simultaneousInterpretationStarted;
_translateTotalBytes = 0; // 重置翻译音频累计字节数
_translateAudioStatus = AppStrings.noTranslationAudio; // 重置状态显示
});
break;
case 4:
actionText = AppStrings.pauseSimultaneousInterpretation;
setState(() {
_simultaneousInterpretationStatus = AppStrings.simultaneousInterpretationPaused;
});
break;
case 5:
actionText = AppStrings.stopSimultaneousInterpretation;
setState(() {
_simultaneousInterpretationStatus = AppStrings.simultaneousInterpretationStopped;
});
break;
}
_showToast(AppStrings.simultaneousInterpretationStatusSet(actionText));
} catch (e) {
debugPrint('Set simultaneous interpretation failed: $e');
_showToast(AppStrings.setSimultaneousInterpretationFailed + ": $e");
}
}
//endregion
//region 音频录音功能方法
// ==================== 音频录音功能方法 ====================
/// 设置录音控制(开始录音)
/// 调用 SDK 的 setAudioRecord 方法,传入 Start
void _startAudioRecord() async {
try {
bool success = await _glassesPlugin.setAudioRecord(type: 0); // totalTime 默认 0(不限制)
if (success) {
_showToast(AppStrings.recordingStarted);
} else {
_showToast(AppStrings.startRecordingFailed2);
}
} catch (e) {
_showToast(AppStrings.startRecordingFailed + ": $e");
}
}
/// 设置录音控制(停止录音)
/// 调用 SDK 的 setAudioRecord 方法,传入 Stop
void _stopAudioRecord() async {
try {
bool success = await _glassesPlugin.setAudioRecord(type: 1); // 停止录音时 totalTime 无意义
if (success) {
_showToast(AppStrings.recordingStopped);
} else {
_showToast(AppStrings.stopRecordingFailed2);
}
} catch (e) {
_showToast(AppStrings.stopRecordingFailed + ": $e");
}
}
/// 获取录音状态
/// 调用 SDK 的 getAudioRecordState 方法
void _queryAudioRecordState() async {
// 设置初始状态为获取中
setState(() {
_audioRecordStatus = AppStrings.gettingStatusWithDots;
});
try {
// 使用 timeout 设置10秒超时
Map<String, int> state = await _glassesPlugin.getAudioRecordState()
.timeout(const Duration(seconds: 10), onTimeout: () {
throw TimeoutException(AppStrings.sdkTimeoutMessage, const Duration(seconds: 10));
});
int type = state['type'] ?? 0;
int totalTime = state['totalTime'] ?? 0;
String stateText = type == 1 ? AppStrings.recording : AppStrings.notRecording;
setState(() {
_audioRecordStatus = "$stateText, ${AppStrings.duration}: ${totalTime}${AppStrings.seconds}";
});
_showToast(AppStrings.recordingStatus(stateText, totalTime));
} on TimeoutException catch (e) {
setState(() {
_audioRecordStatus = AppStrings.sdkNotReturned;
});
_showToast(AppStrings.queryRecordStatusTimeout + ": $e");
} catch (e) {
// 显示SDK返回的错误信息
String errorMsg = e.toString();
// 检查多种错误模式
if (errorMsg.contains(AppStrings.undefinedCommand) ||
errorMsg.contains("Unknown") ||
errorMsg.contains("CRPACKError") ||
errorMsg.contains("rawValue: 1")) {
setState(() {
_audioRecordStatus = AppStrings.deviceNotSupported;
});
} else {
setState(() {
_audioRecordStatus = AppStrings.getFailed;
});
}
_showToast(AppStrings.getRecordStatusFailed + ": $e");
}
}
//endregion
//region 用户信息和设置方法
// ==================== 用户信息和设置方法 ====================
/// 设置闹钟
/// 调用 SDK 的 setAlarm 方法
void _setAlarm() async {
try {
// 示例闹钟设置:早上7:30,重复
Map<String, dynamic> alarmInfo = {
'enable': true, // 启用闹钟
'hour': 7, // 小时
'minute': 30, // 分钟
'repeat': 1, // 重复:0-单次,1-每天
};
// bool success = await _glassesPlugin.setAlarm(alarmInfo); // 已移除
_showToast("设置闹钟功能已移除");
/*
if (success) {
_showToast(AppStrings.alarmSetSuccess);
} else {
_showToast(AppStrings.alarmSetFailed);
}
*/
} catch (e) {
_showToast(AppStrings.setAlarmFailed + ": $e");
}
}
/// 获取语言设置
/// 调用 SDK 的 getLanguage 方法
void _getLanguage() async {
// 设置初始状态为获取中
setState(() {
_getLanguageStatus = AppStrings.gettingStatusWithDots;
});
try {
// 使用 timeout 设置10秒超时
String language = await _glassesPlugin.getLanguage()
.timeout(const Duration(seconds: 10), onTimeout: () {
throw TimeoutException(AppStrings.sdkTimeoutMessage, const Duration(seconds: 10));
});
setState(() {
_actualLanguageStatus = language;
_languageStatus = language;
_getLanguageStatus = language;
});
_showToast(AppStrings.currentLanguage(language));
} on TimeoutException catch (e) {
setState(() {
_getLanguageStatus = AppStrings.sdkNotReturned;
});
_showToast(AppStrings.getLanguageTimeout + ": $e");
} catch (e) {
// 显示SDK返回的错误信息
String errorMsg = e.toString();
if (errorMsg.contains('device not support')) {
setState(() {
_getLanguageStatus = AppStrings.deviceNotSupportedFeature;
});
_showToast(AppStrings.getFailed + ": $e");
} else {
setState(() {
_getLanguageStatus = AppStrings.getFailed;
});
_showToast(AppStrings.getLanguageFailed + ": $e");
}
}
}
/// 显示语言选择对话框
void _showLanguageDialog() {
// 根据当前语言状态设置初始选择
int initialSelection = 1;
if (_actualLanguageStatus == '中文') {
initialSelection = 1;
} else if (_actualLanguageStatus == 'English') {
initialSelection = 0;
}
// 使用 navigatorKey 来获取正确的 context
final context = _navigatorKey.currentContext;
if (context != null) {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(AppStrings.setLanguage),
content: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
ListTile(
title: Text('中文'),
leading: Radio<int>(
value: 1,
groupValue: initialSelection,
onChanged: (int? value) {
setState(() {
initialSelection = value!;
});
},
),
),
ListTile(
title: Text('English'),
leading: Radio<int>(
value: 0,
groupValue: initialSelection,
onChanged: (int? value) {
setState(() {
initialSelection = value!;
});
},
),
),
],
);
},
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text('取消'),
),
ElevatedButton(
onPressed: () {
Navigator.of(context).pop();
_setLanguage(initialSelection);
},
child: Text('确定'),
),
],
);
},
);
}
}
/// 设置语言
/// 调用 SDK 的 sendLanguage 方法
void _setLanguage(int language) async {
try {
setState(() {
_actualLanguageStatus = AppStrings.gettingStatusWithDots;
_languageStatus = AppStrings.gettingStatusWithDots;
});
await _glassesPlugin.sendLanguage(language);
setState(() {
_actualLanguageStatus = language == 1 ? '中文' : 'English';
_languageStatus = language == 1 ? '中文' : 'English';
_getLanguageStatus = language == 1 ? '中文' : 'English';
});
_showToast(AppStrings.setSuccess + ': ${language == 1 ? '中文' : 'English'}');
} catch (e) {
setState(() {
_actualLanguageStatus = AppStrings.setFailed;
_languageStatus = AppStrings.setFailed;
});
_showToast(AppStrings.setLanguageFailed + ": $e");
}
}
//endregion
//region 设备管理功能方法
/// 清除配对信息
/// 调用 SDK 的 clearPairInfo 方法
void _clearPairInfo() async {
try {
bool success = await _glassesPlugin.clearPairInfo();
if (success) {
// 清除 MAC 地址缓存
setState(() {
_cachedMacAddress = null;
_cachedDeviceName = null;
});
await MacAddressCache.clearCachedMac();
_showToast(AppStrings.clearedPairInfo);
} else {
_showToast(AppStrings.clearPairInfoFailed2);
}
} catch (e) {
_showToast(AppStrings.clearPairInfoFailed + ": $e");
}
}
/// 获取设备UUID
/// 调用 SDK 的 getDeviceUUID 方法
void _getDeviceUUID() async {
// 设置初始状态为获取中
setState(() {
_actualDeviceUUIDStatus = AppStrings.gettingStatus;
_deviceUUIDStatus = AppStrings.gettingStatus;
});
try {
// 使用 timeout 设置10秒超时
String uuid = await _glassesPlugin.getDeviceUUID()
.timeout(const Duration(seconds: 10), onTimeout: () {
throw TimeoutException(AppStrings.sdkTimeoutMessage, const Duration(seconds: 10));
});
setState(() {
_actualDeviceUUIDStatus = uuid;
_deviceUUIDStatus = uuid;
});
_showToast(AppStrings.deviceUuid(uuid));
} on TimeoutException catch (e) {
setState(() {
_actualDeviceUUIDStatus = AppStrings.sdkNotReturned;
_deviceUUIDStatus = AppStrings.sdkNotReturned;
});
_showToast(AppStrings.getDeviceUuidTimeout + ": $e");
} catch (e) {
// 显示SDK返回的错误信息
String errorMsg = e.toString();
if (errorMsg.contains('device not support')) {
setState(() {
_actualDeviceUUIDStatus = AppStrings.deviceNotSupported;
_deviceUUIDStatus = AppStrings.deviceNotSupported;
});
_showToast(AppStrings.getFailed + ": $e");
} else {
setState(() {
_actualDeviceUUIDStatus = AppStrings.getFailed;
_deviceUUIDStatus = AppStrings.getFailed;
});
_showToast(AppStrings.getDeviceUuidFailed + ": $e");
}
}
}
/// 获取语音唤醒状态
/// 调用 SDK 的 readVoiceWakeupState 方法
void _getVoiceWakeupState() async {
// 设置初始状态为获取中
setState(() {
_actualVoiceWakeupStatus = AppStrings.gettingStatusWithDots;
_voiceWakeupStatus = AppStrings.gettingStatusWithDots;
});
try {
// 使用 timeout 设置10秒超时
bool isEnabled = await _glassesPlugin.getVoiceWakeupState()
.timeout(const Duration(seconds: 10), onTimeout: () {
throw TimeoutException(AppStrings.sdkTimeoutMessage, const Duration(seconds: 10));
});
String statusText = isEnabled ? AppStrings.enabled : AppStrings.disabled;
setState(() {
_actualVoiceWakeupStatus = statusText;
_voiceWakeupStatus = statusText;
});
_showToast(AppStrings.voiceWakeupStatus(isEnabled ? AppStrings.enabled : AppStrings.disabled));
} catch (e) {
debugPrint('Voice wakeup state exception: $e');
// 显示SDK返回的错误信息
String errorMsg = e.toString();
// 检查多种错误模式
if (errorMsg.contains(AppStrings.undefinedCommand) ||
errorMsg.contains("Unknown") ||
errorMsg.contains("CRPACKError")) {
setState(() {
_voiceWakeupStatus = AppStrings.deviceNotSupportedFeature;
});
} else {
setState(() {
_voiceWakeupStatus = AppStrings.notSupportedOrError;
});
}
_showToast(AppStrings.voiceWakeupMayNotSupport + ": $e");
}
}
/// 设置语音唤醒
/// 调用 SDK 的 setVoiceWakeup 方法
void _setVoiceWakeup() async {
// SDK 定义:0=TypeOn(开启),1=TypeOff(关闭)
if (_selectedVoiceWakeupAction == null) return;
bool enable = _selectedVoiceWakeupAction == 0;
// 开启语音唤醒会导致设备重启,需用户确认
if (enable) {
final navContext = _navigatorKey.currentContext;
if (navContext == null) return;
bool? confirmed = await showDialog<bool>(
context: navContext,
builder: (ctx) => AlertDialog(
title: Text(AppStrings.setVoiceWakeup),
content: const Text('开启语音唤醒会导致设备重启,是否继续?'),
actions: [
TextButton(
onPressed: () => Navigator.of(ctx).pop(false),
child: Text(AppStrings.cancel),
),
TextButton(
onPressed: () => Navigator.of(ctx).pop(true),
child: Text(AppStrings.confirm),
),
],
),
);
if (confirmed != true) return;
}
try {
await _glassesPlugin.setVoiceWakeup(enable: enable);
_showToast(AppStrings.voiceWakeupSet(enable ? AppStrings.enabled : AppStrings.disabled));
// 设置完成后重新获取状态
_getVoiceWakeupState();
} catch (e) {
_showToast(AppStrings.setVoiceWakeupFailed + ": $e");
}
}
/// 获取运行状态
/// 调用 SDK 的 readRunningStatus 方法,状态通过事件流返回
void _getRunningStatus() async {
try {
// 只调用 SDK,状态通过 runningStatusEveStm 事件流返回
await _glassesPlugin.getRunningStatus();
debugPrint('已请求运行状态,等待事件流返回');
} catch (e) {
debugPrint('请求运行状态失败: $e');
_showToast(AppStrings.getFailed + ": $e");
}
}
/// 将 Map 转换为格式化的字符串
String _mapToString(Map<String, dynamic> map) {
final buffer = StringBuffer();
buffer.write('{');
bool first = true;
map.forEach((key, value) {
if (!first) buffer.write(', ');
first = false;
buffer.write('$key: $value');
});
buffer.write('}');
return buffer.toString();
}
//endregion
}