app-starlock/lib/main/lockDetail/lockSet/speechLanguageSettings/speech_language_settings_logic.dart

495 lines
17 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_setVoicePackageFinalResult.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<Reply>? _replySubscription;
// 超时定时器(用于检测是否未收到回复)
Timer? _sendTimeoutTimer;
// 超时标志位(可选,防止重复处理)
bool _isTimeout = false;
@override
void onInit() async {
super.onInit();
_replySubscription =
EventBusManager().eventBus!.on<Reply>().listen((Reply reply) async {
if (reply is VoicePackageConfigureReply) {
// 语言包配置开始
_handlerStartVoicePackageConfigure(reply);
} else if (reply is VoicePackageConfigureProcessReply) {
_handlerVoicePackageConfigureProcess(reply);
} else if (reply is VoicePackageConfigureConfirmationReply) {
handleVoiceConfigureThrottled(reply);
} else if (reply is SetVoicePackageFinalResultReply) {
handleSetResult(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<String> parts = lang.split('_');
final indexOf = locales.indexOf(Locale(parts[0], parts[1]));
final passthroughItem = PassthroughItem(
lang: element.lang,
timbres: element.timbres,
langText: '简体中文'.tr + '(中国台湾)'.tr + 'Simplified Chinese TW',
name: element.name,
);
state.languages.add(passthroughItem);
} else {
List<String> 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;
// 传统 for 循环,直接通过索引访问
for (int i = 0; i < state.languages.length; i++) {
final language = state.languages[i]; // 当前元素
if (language.lang == lang) {
print('匹配到下标:$i,元素:$language');
final timbres = language.timbres;
for (int j = 0; j < timbres.length; j++) {
final item = timbres[j];
if (lang == language.lang && item.timbre == timbre) {
state.selectSoundTypeIndex.value = item.isFemale;
state.selectPassthroughListIndex.value = i;
break;
}
}
}
}
}
} 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<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 _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 _handleSendTimeout() {
_isTimeout = true; // 标记超时状态
dismissEasyLoading();
cancelBlueConnetctToastTimer();
showBlueConnetctToast();
// 重置状态,避免后续错误操作
state.voiceSubcontractingIndex = 0;
state.voiceSubcontractingCount = 0;
state.data = null;
state.progress.value = 0.0;
_isTimeout = false; // 标记超时状态
}
void _sendNextPackage() async {
// 若已超时,直接返回
if (_isTimeout) return;
// 取消上一次未完成的定时器(避免重复触发)
_sendTimeoutTimer?.cancel();
// 检查是否已完成所有分包发送
if (state.voiceSubcontractingIndex >= state.voiceSubcontractingCount) {
print('所有分包已发送完成');
state.progress.value = 1.0;
return;
}
// 启动 3 秒超时定时器
_sendTimeoutTimer = Timer(Duration(seconds: 3), () {
_handleSendTimeout(); // 触发超时处理
});
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) {
// 取消超时定时器(已收到回复,无需继续等待)
_sendTimeoutTimer?.cancel();
_isTimeout = false; // 重置超时标志
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();
}
bool _isThrottled = false;
void handleVoiceConfigureThrottled(
VoicePackageConfigureConfirmationReply reply,
) {
if (_isThrottled) return;
_isThrottled = true;
// 执行你的逻辑
_executeLogic(reply);
// 设置节流时间(比如 1 秒)
Future.delayed(Duration(seconds: 1), () {
_isThrottled = false;
});
}
Future<void> _executeLogic(
VoicePackageConfigureConfirmationReply reply) async {
await _handlerVoicePackageConfigureConfirmation(reply);
}
_handlerVoicePackageConfigureConfirmation(
VoicePackageConfigureConfirmationReply reply,
) async {
final int status = reply.data[2];
switch (status) {
case 0x00:
await BlueManage().blueSendData(BlueManage().connectDeviceName,
(BluetoothConnectionState deviceConnectionState) async {
if (deviceConnectionState == BluetoothConnectionState.connected) {
await BlueManage().writeCharacteristicWithResponse(
SetVoicePackageFinalResult(
lockID: BlueManage().connectDeviceName,
languageCode: state.tempLangStr.value,
).packageData(),
);
} else if (deviceConnectionState ==
BluetoothConnectionState.disconnected) {
dismissEasyLoading();
cancelBlueConnetctToastTimer();
showBlueConnetctToast();
}
});
break;
default:
showToast('设置'.tr + '失败'.tr);
break;
}
}
void handleSetResult(SetVoicePackageFinalResultReply 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;
}
}
}