import 'dart:async'; import 'dart:typed_data'; import 'package:crypto/crypto.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_blue_plus/flutter_blue_plus.dart'; import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:get/get.dart'; import 'package:star_lock/app_settings/app_settings.dart'; import 'package:star_lock/blue/blue_manage.dart'; import 'package:star_lock/blue/io_protocol/io_getDeviceModel.dart'; import 'package:star_lock/blue/io_protocol/io_otaUpgrade.dart'; import 'package:star_lock/blue/io_protocol/io_processOtaUpgrade.dart'; import 'package:star_lock/blue/io_protocol/io_voicePackageConfigure.dart'; import 'package:star_lock/blue/io_protocol/io_voicePackageConfigureProcess.dart'; import 'package:star_lock/blue/io_reply.dart'; import 'package:star_lock/blue/io_tool/io_tool.dart'; import 'package:star_lock/blue/io_tool/manager_event_bus.dart'; import 'package:star_lock/login/login/entity/LoginEntity.dart'; import 'package:star_lock/login/selectCountryRegion/common/index.dart'; import 'package:star_lock/main/lockDetail/lockDetail/passthrough_item.dart'; import 'package:star_lock/main/lockDetail/lockSet/speechLanguageSettings/speech_language_settings_state.dart'; import 'package:star_lock/network/api_repository.dart'; import 'package:star_lock/tools/baseGetXController.dart'; import 'package:http/http.dart' as http; import 'package:star_lock/tools/commonDataManage.dart'; import 'package:star_lock/tools/eventBusEventManage.dart'; import 'package:star_lock/tools/storage.dart'; import 'package:star_lock/translations/app_dept.dart'; class SpeechLanguageSettingsLogic extends BaseGetXController { final SpeechLanguageSettingsState state = SpeechLanguageSettingsState(); StreamSubscription? _replySubscription; @override void onInit() async { final findLocaleIndex = _findLocaleIndex(); state.selectPassthroughListIndex.value = findLocaleIndex; super.onInit(); _replySubscription = EventBusManager().eventBus!.on().listen((Reply reply) async { if (reply is GetDeviceModelReply) { // 获取设备型号 _handlerDeviceModelReply(reply); } if (reply is VoicePackageConfigureReply) { // 语言包配置开始 _handlerStartVoicePackageConfigure(reply); } else if (reply is VoicePackageConfigureProcessReply) { _handlerVoicePackageConfigureProcess(reply); } else if (reply is VoicePackageConfigureConfirmationReply) { _handlerVoicePackageConfigureConfirmation(reply); } }); state.deviceModel.value = await Storage.getString(deviceModel) ?? ''; debugPrint('设备型号:${state.deviceModel.value}'); if (state.deviceModel.value != null) { await initList(); } // await sendGetDeviceModelBleMessage(); } /// 获取列表 /// //{ "vendor": "XL", "model": "JL-BLE-01"} initList() async { showEasyLoading(); try { final vendor = state.lockSetInfoData.value.lockBasicInfo?.vendor; final model = state.lockSetInfoData.value.lockBasicInfo?.model; final PassthroughListResponse entity = await ApiRepository.to.getPassthroughList(data: { 'vendor': vendor!, 'model': model!, }); if (entity.errorCode!.codeIsSuccessful) { state.languages.value = entity.data!!; final lang = state .lockSetInfoData.value.lockSettingInfo?.currentVoiceTimbre?.lang; final timbre = state .lockSetInfoData.value.lockSettingInfo?.currentVoiceTimbre?.timbre; state.languages.value.forEach((element) { final timbres = element.timbres; timbres.forEach((item) { if (lang == element.lang && item.timbre == timbre) { state.selectSoundTypeIndex.value = item.isFemale; } }); }); } } catch (e) { debugPrint('获取语音包出现错误:$e'); } finally { dismissEasyLoading(); } } /// 查找 locales 中 code 等于 lang 的元素下标(不存在返回 -1) int _findLocaleIndex() { // 1. 获取 lang(可能为 null) final lang = state.lockSetInfoData.value.lockSettingInfo?.currentVoiceTimbre?.lang; // 2. 如果 lang 为 null,直接返回 -1 if (lang == null) return -1; // 3. 获取 locales 集合(假设非空,若可能为空需额外判空) final locales = appDept.deptSupportedLocales; // 4. 遍历查找符合条件的下标(使用 indexWhere 高效实现) return locales.indexWhere((element) { // 处理 countryCode 可能为 null 的情况(根据业务需求调整) if (element.countryCode == null) return false; // 构造当前 locale 的 code(格式:languageCode_countryCode) final currentCode = '${element.languageCode}_${element.countryCode}'; // 比较是否等于目标 lang return currentCode == lang; }); } void saveSpeechLanguageSettings() async { final language = state.appLocalLanguages[state.selectPassthroughListIndex.value]; // 从选中的语言中获取languageCode和countryCode final locales = appDept.deptSupportedLocales; locales.forEach((element) { final lanTitle = ExtensionLanguageType.fromLocale(element).lanTitle; if (lanTitle == language) { if (element.countryCode != null) { String code = element.languageCode + '_' + element.countryCode!; state.languages.forEach((item) async { if (item.lang == code) { item.timbres.forEach((timbre) async { if (timbre.isFemale == state.selectSoundTypeIndex.value) { state.tempLangStr.value = item.lang; state.tempTimbreStr.value = timbre.timbre; await downloadFile(timbre.timbrePackUrl); } }); } }); } } }); } void changeSelectIndex(int index) { state.selectLanguageIndex.value = index; } //下载语音包 Future downloadFile(String url) async { final http.Response response = await http.get(Uri.parse(url)); if (response.statusCode == 200) { state.data = response.bodyBytes; sendFileToDevice(response.bodyBytes, [0, 0, 0, 0]); } } sendFileToDevice(Uint8List data, List token) { showEasyLoading(); showBlueConnetctToastTimer(action: () { dismissEasyLoading(); }); BlueManage().blueSendData(BlueManage().connectDeviceName, (BluetoothConnectionState deviceConnectionState) async { if (deviceConnectionState == BluetoothConnectionState.connected) { final List? privateKey = await Storage.getStringList(saveBluePrivateKey); final List getPrivateKeyList = changeStringListToIntList(privateKey!); final List? signKey = await Storage.getStringList(saveBlueSignKey); final List signKeyDataList = changeStringListToIntList(signKey!); final String uid = await Storage.getUid() ?? ''; final String md5Str = md5.convert(data).toString().toUpperCase(); BlueManage().writeCharacteristicWithResponse( VoicePackageConfigure( lockID: BlueManage().connectDeviceName, userID: uid, keyID: BlueManage().connectDeviceName, platform: 0, product: 0, fwSize: data.length, fwMD5: md5Str, needAuthor: 1, token: token, signKey: signKeyDataList, privateKey: getPrivateKeyList) .packageData(), ); } else if (deviceConnectionState == BluetoothConnectionState.disconnected) { dismissEasyLoading(); cancelBlueConnetctToastTimer(); } }); } // 发送获取型号蓝牙命令 sendGetDeviceModelBleMessage() { showEasyLoading(); showBlueConnetctToastTimer(action: () { dismissEasyLoading(); }); BlueManage().blueSendData(BlueManage().connectDeviceName, (BluetoothConnectionState deviceConnectionState) async { if (deviceConnectionState == BluetoothConnectionState.connected) { BlueManage().writeCharacteristicWithResponse( GetDeviceModelCommand( lockID: BlueManage().connectDeviceName, ).packageData(), ); } else if (deviceConnectionState == BluetoothConnectionState.disconnected) { dismissEasyLoading(); cancelBlueConnetctToastTimer(); showBlueConnetctToast(); } }); } void _handlerDeviceModelReply(GetDeviceModelReply reply) async { final int status = reply.data[2]; switch (status) { case 0x00: //成功 cancelBlueConnetctToastTimer(); // 3. 解析DeviceModel(40字节,索引3~42) int startIndex = 3; int length = 40; List deviceModelBytes = reply.data.sublist(startIndex, startIndex + length); String rawData = String.fromCharCodes(deviceModelBytes); int firstNullIndex = rawData.indexOf('\u0000'); String deviceModel = rawData.substring(0, firstNullIndex); print(deviceModel); // 输出: 2403 print('获取到 DeviceModel (原始): $deviceModel'); state.deviceModel.value = deviceModel; await initList(); break; default: showToast('获取设备型号失败'.tr); break; } } // 开始配置语音包 void _handlerStartVoicePackageConfigure( VoicePackageConfigureReply reply) async { final int status = reply.data[6]; switch (status) { case 0x00: //成功 cancelBlueConnetctToastTimer(); _startSendLanguageFile(); break; case 0x06: //无权限 final List token = reply.data.sublist(2, 6); print('收到token:$token'); if (state.data != null) { sendFileToDevice(state.data!, token); } break; default: showToast('获取设备型号失败'.tr); break; } } void _startSendLanguageFile() { if (state.data == null) return; state.voiceSubcontractingIndex = 0; state.voiceSubcontractingCount = (state.data!.length + state.voiceSubcontractingSize - 1) ~/ state.voiceSubcontractingSize; state.progress.value = 0.0; // 开始前重置进度 _sendNextPackage(); } void _sendNextPackage() { if (state.voiceSubcontractingIndex >= state.voiceSubcontractingCount) { print('所有分包已发送完成'); state.progress.value = 1.0; // 发送完成 // 可在此处通知UI或做后续处理 return; } int start = state.voiceSubcontractingIndex * state.voiceSubcontractingSize; int end = start + state.voiceSubcontractingSize; if (end > state.data!.length) end = state.data!.length; Uint8List packageData = state.data!.sublist(start, end); // 更新分包进度 state.progress.value = (state.voiceSubcontractingIndex + 1) / state.voiceSubcontractingCount; EasyLoading.showProgress(state.progress.value, status: '正在发送数据 ${(state.progress.value * 100).toStringAsFixed(0)}%'); _sendLanguageFileBleMessage( index: state.voiceSubcontractingIndex, data: packageData, ); } _sendLanguageFileBleMessage({required int index, required Uint8List data}) { BlueManage().blueSendData(BlueManage().connectDeviceName, (BluetoothConnectionState deviceConnectionState) async { if (deviceConnectionState == BluetoothConnectionState.connected) { BlueManage().writeCharacteristicWithResponse( VoicePackageConfigureProcess( index: index, size: data.length, data: data, ).packageData(), ); } else if (deviceConnectionState == BluetoothConnectionState.disconnected) { dismissEasyLoading(); cancelBlueConnetctToastTimer(); showBlueConnetctToast(); } }); } void _handlerVoicePackageConfigureProcess( VoicePackageConfigureProcessReply reply) { final int status = reply.data[2]; switch (status) { case 0x00: cancelBlueConnetctToastTimer(); state.voiceSubcontractingIndex++; _sendNextPackage(); break; default: showToast('发送分包数据不成功'.tr); break; } } @override void dispose() async { await _replySubscription?.cancel(); _replySubscription = null; await BlueManage().disconnect(); dismissEasyLoading(); cancelBlueConnetctToastTimer(); EasyLoading.dismiss(); // 清理分包相关状态 state.progress.value = 0.0; state.voiceSubcontractingIndex = 0; state.voiceSubcontractingCount = 0; state.data = null; super.dispose(); } @override void onClose() async { await _replySubscription?.cancel(); _replySubscription = null; await BlueManage().disconnect(); dismissEasyLoading(); cancelBlueConnetctToastTimer(); EasyLoading.dismiss(); // 清理分包相关状态 state.progress.value = 0.0; state.voiceSubcontractingIndex = 0; state.voiceSubcontractingCount = 0; state.data = null; super.onClose(); } void _handlerVoicePackageConfigureConfirmation( VoicePackageConfigureConfirmationReply reply, ) async { final int status = reply.data[2]; switch (status) { case 0x00: cancelBlueConnetctToastTimer(); final LoginEntity entity = await ApiRepository.to.settingCurrentVoiceTimbre( data: { 'lang': state.tempLangStr.value, 'timbre': state.tempTimbreStr.value, }, lockId: state.lockSetInfoData.value.lockId!, ); if (entity.errorCode!.codeIsSuccessful) { showSuccess('设置成功'.tr, something: () { state.lockSetInfoData.value.lockSettingInfo?.currentVoiceTimbre ?.lang = state.tempLangStr.value; state.lockSetInfoData.value.lockSettingInfo?.currentVoiceTimbre ?.timbre = state.tempTimbreStr.value; eventBus.fire( PassCurrentLockInformationEvent(state.lockSetInfoData.value)); }); } dismissEasyLoading(); break; default: showToast('语音设置失败'.tr); break; } } }