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'; import 'package:star_lock/translations/current_locale_tool.dart'; class SpeechLanguageSettingsLogic extends BaseGetXController { final SpeechLanguageSettingsState state = SpeechLanguageSettingsState(); StreamSubscription? _replySubscription; @override void onInit() async { super.onInit(); _replySubscription = EventBusManager().eventBus!.on().listen((Reply reply) async { if (reply is VoicePackageConfigureReply) { // 语言包配置开始 _handlerStartVoicePackageConfigure(reply); } else if (reply is VoicePackageConfigureProcessReply) { _handlerVoicePackageConfigureProcess(reply); } else if (reply is VoicePackageConfigureConfirmationReply) { _handlerVoicePackageConfigureConfirmation(reply); } }); await initList(); } /// 获取列表 /// //{ "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) { final data = entity.data; final locales = appDept.deptSupportedLocales; state.languages.clear(); state.languages.add( PassthroughItem( lang: 'system', timbres: [], langText: '跟随系统'.tr, name: '跟随系统'.tr, ), ); data?.forEach((element) { final lang = element.lang; if (lang == 'zh_TW') { // 如果是台湾的话应该显示未简体中文 List parts = lang.split('_'); final indexOf = locales.indexOf(Locale(parts[0], parts[1])); final passthroughItem = PassthroughItem( lang: element.lang, timbres: element.timbres, langText: '简体中文'.tr + '(中国台湾)'.tr, name: element.name, ); state.languages.add(passthroughItem); } else { List parts = lang.split('_'); final indexOf = locales.indexOf(Locale(parts[0], parts[1])); final passthroughItem = PassthroughItem( lang: element.lang, timbres: element.timbres, langText: ExtensionLanguageType.fromLocale(locales[indexOf]).lanTitle, name: element.name, ); state.languages.add(passthroughItem); } }); state.languages.refresh(); 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(); } } void saveSpeechLanguageSettings() async { var language = state.languages[state.selectPassthroughListIndex.value]; if (language.lang == 'system') { // 如果选择了跟随系统 // 系统层的语言 // print(CurrentLocaleTool.convertLocale(Get.deviceLocale!)); // APP层的语言 Locale? currentLocale = Get.locale; // 直接获取最新语言 if (currentLocale != null) { final indexWhere = state.languages .indexWhere((element) => element.lang == currentLocale.toString()); state.selectPassthroughListIndex.value = indexWhere; } } language = state.languages[state.selectPassthroughListIndex.value]; final value = state.selectSoundTypeIndex.value; state.tempLangStr.value = language.lang; AppLog.log('下载的语言是:${state.tempLangStr}'); language.timbres.forEach((element) async { if (element.isFemale == value) { await downloadFile(element.timbrePackUrl); state.tempTimbreStr.value = element.timbre; return; } }); } 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 _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() async { 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)}%'); await _sendLanguageFileBleMessage( index: state.voiceSubcontractingIndex, data: packageData, ); } _sendLanguageFileBleMessage( {required int index, required Uint8List data}) async { await BlueManage().blueSendData(BlueManage().connectDeviceName, (BluetoothConnectionState deviceConnectionState) async { if (deviceConnectionState == BluetoothConnectionState.connected) { await 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 + '失败'.tr); break; } } }