app-starlock/lib/main/lockDetail/lockSet/speechLanguageSettings/speech_language_settings_logic.dart
2025-07-25 14:59:54 +08:00

414 lines
14 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<Reply>? _replySubscription;
@override
void onInit() async {
final findLocaleIndex = _findLocaleIndex();
state.selectPassthroughListIndex.value = findLocaleIndex;
super.onInit();
_replySubscription =
EventBusManager().eventBus!.on<Reply>().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<void> 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, <int>[0, 0, 0, 0]);
}
}
sendFileToDevice(Uint8List data, List<int> token) {
showEasyLoading();
showBlueConnetctToastTimer(action: () {
dismissEasyLoading();
});
BlueManage().blueSendData(BlueManage().connectDeviceName,
(BluetoothConnectionState deviceConnectionState) async {
if (deviceConnectionState == BluetoothConnectionState.connected) {
final List<String>? privateKey =
await Storage.getStringList(saveBluePrivateKey);
final List<int> getPrivateKeyList =
changeStringListToIntList(privateKey!);
final List<String>? signKey =
await Storage.getStringList(saveBlueSignKey);
final List<int> 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. 解析DeviceModel40字节索引3~42
int startIndex = 3;
int length = 40;
List<int> 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<int> 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;
}
}
}