Merge branch 'develop_sky_liyi' into 'develop_sky'

Develop sky liyi

See merge request StarlockTeam/app-starlock!213
This commit is contained in:
李仪 2025-07-23 01:55:25 +00:00
commit 768f7fd38f
41 changed files with 2613 additions and 160 deletions

View File

@ -1159,5 +1159,6 @@
"请确认后再继续": "Please confirm before continuing",
"需要相机权限": "Camera permission required",
"此功能的开启和关闭只能在锁附近通过手机蓝牙进行": "The activation and deactivation of this feature can only be done through Bluetooth on the phone near the lock",
"网关添加成功": "Gateway added successfully"
"网关添加成功": "Gateway added successfully",
"语音包设置": "Voice packet settings"
}

View File

@ -1101,7 +1101,7 @@
"支持的国家": "支持的国家",
"支持的国家值": "美国、加拿大、英国、澳大利亚、印度、德国、法国、意大利、西班牙、日本",
"操作流程": "操作流程",
"操作流程值":"1 用智能锁APP添加锁和网关\n\n2 在APP里开启锁的远程开锁功能这个功能默认是关闭的。如果没有这个选项则锁不支持Alexa \n\n3 在Alexa中添加Skill并用智能锁APP的账号和密码进行授权。授权成功后就可以发现账号下的设备\n\n4 在Alexa app里找到锁开启语音开锁的功能并设置语言密码\n\n5 可以通过Alexa操作锁了",
"操作流程值": "1 用智能锁APP添加锁和网关\n\n2 在APP里开启锁的远程开锁功能这个功能默认是关闭的。如果没有这个选项则锁不支持Alexa \n\n3 在Alexa中添加Skill并用智能锁APP的账号和密码进行授权。授权成功后就可以发现账号下的设备\n\n4 在Alexa app里找到锁开启语音开锁的功能并设置语言密码\n\n5 可以通过Alexa操作锁了",
"Google Home": "Google Home",
"Action name": "Action name",
"ScienerSmart": "ScienerSmart",
@ -1164,5 +1164,6 @@
"请确认后再继续": "请确认后再继续",
"需要相机权限": "需要相机权限",
"此功能的开启和关闭只能在锁附近通过手机蓝牙进行": "此功能的开启和关闭只能在锁附近通过手机蓝牙进行",
"网关添加成功": "网关添加成功"
"网关添加成功": "网关添加成功",
"语音包设置": "语音包设置",
}

View File

@ -1166,5 +1166,6 @@
"请确认后再继续": "请确认后再继续",
"需要相机权限": "需要相机权限",
"一键登录": "一键登录",
"网关添加成功": "网关添加成功"
"网关添加成功": "网关添加成功",
"语音包设置": "语音包设置"
}

View File

@ -26,6 +26,7 @@ import 'package:star_lock/main/lockDetail/lockSet/faceUnlock/faceUnlock_page.dar
import 'package:star_lock/main/lockDetail/lockSet/liveVideo/liveVideo_page.dart';
import 'package:star_lock/main/lockDetail/lockSet/motorPower/motorPower_page.dart';
import 'package:star_lock/main/lockDetail/lockSet/openDoorDirection/openDoorDirection_page.dart';
import 'package:star_lock/main/lockDetail/lockSet/speechLanguageSettings/speech_language_settings_page.dart';
import 'package:star_lock/main/lockDetail/messageWarn/addFamily/addFamily_page.dart';
import 'package:star_lock/main/lockDetail/messageWarn/lockUser/lockUser_page.dart';
import 'package:star_lock/main/lockDetail/messageWarn/msgNotification/coerceOpenDoor/coerceFingerprint/coerceFingerprint_page.dart';
@ -41,6 +42,7 @@ import 'package:star_lock/main/lockDetail/palm/palmList/palmList_page.dart';
import 'package:star_lock/main/lockDetail/passwordKey/passwordKeyDetailChangeDate/passwordKeyDetailChangeDate_page.dart';
import 'package:star_lock/main/lockMian/lockMain/xhj/lockMain_xhj_page.dart';
import 'package:star_lock/mine/about/webviewShow_page.dart';
import 'package:star_lock/mine/addLock/lock_voice_setting/lock_voice_setting_page.dart';
import 'package:star_lock/mine/mine/safeVerify/safeVerify_page.dart';
import 'package:star_lock/mine/minePersonInfo/minePersonInfoEmail/mineBindPhoneOrEmail_page.dart';
import 'package:star_lock/mine/minePersonInfo/minePersonInfoPage/minePersonInfo_entity.dart';
@ -59,6 +61,7 @@ import 'package:star_lock/mine/mineSet/transferSmartLock/selectBranch/selectBran
import 'package:star_lock/mine/mineSet/transferSmartLock/transferSmartLockList/transferSmartLock_page.dart';
import 'package:star_lock/mine/valueAddedServices/advancedFeaturesWeb/advancedFeaturesWeb_page.dart';
import 'package:star_lock/mine/valueAddedServices/advancedFunctionRecord/advancedFunctionRecord_page.dart';
import 'package:star_lock/mine/valueAddedServices/cloudStorage/cloud_storage_page.dart';
import 'package:star_lock/mine/valueAddedServices/valueAddedServicesRecord/value_added_services_record_page.dart';
import 'package:star_lock/talk/starChart/views/guide/permission_guidance_page.dart';
import 'package:star_lock/talk/starChart/views/native/talk_view_native_decode_page.dart';
@ -368,6 +371,8 @@ abstract class Routers {
'/valueAddedServicesRecordPage'; // -
static const String valueAddedServicesHighFunctionPage =
'/ValueAddedServicesHighFunctionPage'; // -
static const String valueAddedCloudStoragePage =
'/valueAddedCloudStoragePage'; // -
static const String valueAddedServicesBuyPage =
'/ValueAddedServicesBuyPage'; // -
static const String customSMSTemplateListPage =
@ -436,6 +441,8 @@ abstract class Routers {
'/AddLockSelectCountryPage'; //
static const String faceUnlockPage = '/faceUnlockPage'; //
static const String motorPowerPage = '/motorPowerPage'; //
static const String speechLanguageSettingsPage =
'/speechLanguageSettingsPage'; //
static const String openDoorDirectionPage = '/openDoorDirectionPage'; //
static const String catEyeWorkModePage = '/catEyeWorkModePage'; //
static const String msgNotificationPage = '/msgNotificationPage'; //
@ -521,6 +528,8 @@ abstract class Routers {
'/imageTransmissionView'; //()
static const String permissionGuidancePage =
'/permissionGuidancePage'; //
static const String lockVoiceSettingPage =
'/lockVoiceSetting'; //
}
abstract class AppRouters {
@ -904,6 +913,10 @@ abstract class AppRouters {
name: Routers.valueAddedServicesHighFunctionPage,
page: () => const ValueAddedServicesHighFunctionPage(),
),
GetPage<dynamic>(
name: Routers.valueAddedCloudStoragePage,
page: () => const CloudStoragePage(),
),
GetPage<dynamic>(
name: Routers.customSMSTemplateListPage,
page: () => const CustomSMSTemplateListPage(),
@ -1024,6 +1037,9 @@ abstract class AppRouters {
name: Routers.faceUnlockPage, page: () => const FaceUnlockPage()),
GetPage<dynamic>(
name: Routers.motorPowerPage, page: () => const MotorPowerPage()),
GetPage<dynamic>(
name: Routers.speechLanguageSettingsPage,
page: () => const SpeechLanguageSettingsPage()),
GetPage<dynamic>(
name: Routers.openDoorDirectionPage,
page: () => const OpenDoorDirectionPage()),
@ -1201,6 +1217,9 @@ abstract class AppRouters {
GetPage<dynamic>(
name: Routers.permissionGuidancePage,
page: () => PermissionGuidancePage()),
GetPage<dynamic>(
name: Routers.lockVoiceSettingPage,
page: () => LockVoiceSetting()),
//
// GetPage<dynamic>(name: Routers.h264View, page: () => H264WebView()), // webview播放页面
];

View File

@ -0,0 +1,48 @@
import 'dart:convert';
import '../io_reply.dart';
import '../io_sender.dart';
import '../io_tool/io_tool.dart';
import '../io_type.dart';
class GetDeviceModelCommand extends SenderProtocol {
GetDeviceModelCommand({
this.lockID,
}) : super(CommandType.getDeviceModel);
String? lockID;
@override
String toString() {
return 'GetDeviceModelCommand{lockID: $lockID}';
}
@override
List<int> messageDetail() {
List<int> data = [];
//
final int type = commandType!.typeValue;
final double typeDouble = type / 256;
final int type1 = typeDouble.toInt();
final int type2 = type % 256;
data.add(type1);
data.add(type2);
final int length = utf8.encode(lockID!).length;
data.addAll(utf8.encode(lockID!));
data = getFixedLengthList(data, 40 - length);
printLog(data);
return data;
}
}
class GetDeviceModelReply extends Reply {
GetDeviceModelReply.parseData(CommandType commandType, List<int> dataDetail)
: super.parseData(commandType, dataDetail) {
status = dataDetail[2];
data = dataDetail;
}
}

View File

@ -0,0 +1,163 @@
import 'dart:convert';
import 'dart:typed_data';
import 'package:crypto/crypto.dart' as crypto;
import '../io_reply.dart';
import '../io_sender.dart';
import '../io_tool/io_tool.dart';
import '../io_type.dart';
import '../sm4Encipher/sm4.dart';
//oat升级
class VoicePackageConfigure extends SenderProtocol {
VoicePackageConfigure(
{this.lockID,
this.userID,
this.keyID,
this.platform,
this.product,
this.fwSize,
this.fwMD5,
this.needAuthor,
this.signKey,
this.privateKey,
this.token,
this.encrypt = true})
: super(CommandType.startVoicePackageConfigure);
String? lockID;
String? userID;
String? keyID;
int? platform;
int? product;
int? fwSize;
String? fwMD5;
int? needAuthor;
List<int>? signKey;
List<int>? privateKey;
List<int>? token;
bool encrypt;
@override
String toString() {
return 'VoicePackageConfigure{lockID: $lockID, userID: $userID, '
'keyID: $keyID, platform: $platform, product: $product, '
' fwSize: $fwSize, '
'fwMD5: $fwMD5, needAuthor: $needAuthor, signKey: $signKey, '
'privateKey: $privateKey, token: $token}';
}
@override
int identifierValue() {
if (encrypt) {
return super.identifierValue();
} else {
return 0x20;
}
}
@override
List<int> messageDetail() {
List<int> data = <int>[];
List<int> ebcData = <int>[];
//
final int type = commandType!.typeValue;
final double typeDouble = type / 256;
final int type1 = typeDouble.toInt();
final int type2 = type % 256;
data.add(type1);
data.add(type2);
// id 40
final int lockIDLength = utf8.encode(lockID!).length;
data.addAll(utf8.encode(lockID!));
data = getFixedLengthList(data, 40 - lockIDLength);
//userID 20
final int userIDLength = utf8.encode(userID!).length;
data.addAll(utf8.encode(userID!));
data = getFixedLengthList(data, 20 - userIDLength);
//platform 2
final int platform0 = (platform! & 0xFF00) >> 8;
final int platform1 = platform! & 0xFF;
data.add(platform0);
data.add(platform1);
//product 2
data.addAll(<int>[0, 1]); // 01
//fwSize 4
final ByteData bytes = ByteData(4); // 4
bytes.setInt32(0, fwSize!);
final List<int> byteList = bytes.buffer.asUint8List();
data.addAll(byteList);
// 16
final Uint8List result = Uint8List(16);
// 4
for (int i = 0; i < fwMD5!.length; i += 2) {
final String hex = fwMD5!.substring(i, i + 2);
final int byteValue = int.parse(hex, radix: 16);
result[i ~/ 2] = byteValue;
}
data.addAll(result);
// token 4 Token 0 token失效或者第一次发送的时候token为0
data.addAll(token!);
if (needAuthor == 0) {
//AuthCodeLen 1
data.add(0);
} else {
final List<int> authCodeData = <int>[];
//KeyID
authCodeData.addAll(utf8.encode(keyID!));
//UserID
authCodeData.addAll(utf8.encode(userID!));
//token 4 Token 0
authCodeData.addAll(token ?? <int>[]);
authCodeData.addAll(signKey ?? <int>[]);
// KeyIDauthUserIDmd5加密之后就是authCode
final crypto.Digest authCode = crypto.md5.convert(authCodeData);
data.add(authCode.bytes.length);
data.addAll(authCode.bytes);
}
if ((data.length % 16) != 0) {
final int add = 16 - data.length % 16;
for (int i = 0; i < add; i++) {
data.add(0);
}
}
printLog(data);
if (encrypt) {
// LockId进行SM4 ECB加密 key:544d485f633335373034383064613864
ebcData = SM4.encrypt(data, key: privateKey, mode: SM4CryptoMode.ECB);
return ebcData;
} else {
data.add(0);
return data;
}
}
}
class VoicePackageConfigureReply extends Reply {
VoicePackageConfigureReply.parseData(
CommandType commandType, List<int> dataDetail)
: super.parseData(commandType, dataDetail) {
data = dataDetail;
token = data.sublist(2, 6);
status = data[6];
errorWithStstus(status);
}
List<int> token = <int>[];
}

View File

@ -0,0 +1,73 @@
import 'dart:typed_data';
import '../io_reply.dart';
import '../io_sender.dart';
import '../io_type.dart';
//oat升级
class VoicePackageConfigureProcess extends SenderProtocol {
VoicePackageConfigureProcess({
this.index,
this.size,
this.data,
}) : super(CommandType.voicePackageConfigureProcess);
int? index;
int? size;
List<int>? data;
@override
String toString() {
return 'VoicePackageConfigureProcess{index: $index, size: $size, data: $data}';
}
@override
List<int> messageDetail() {
final List<int> data = [];
//
final int type = commandType!.typeValue;
final double typeDouble = type / 256;
final int type1 = typeDouble.toInt();
final int type2 = type % 256;
data.add(type1);
data.add(type2);
//index 2
final ByteData indexBytes = ByteData(2); // 4
indexBytes.setInt16(0, index!);
final List<int> indexList = indexBytes.buffer.asUint8List();
data.addAll(indexList);
//size 2
final ByteData bytes = ByteData(2); // 4
bytes.setInt16(0, size!);
final List<int> byteList = bytes.buffer.asUint8List();
data.addAll(byteList);
data.addAll(this.data!);
printLog(data);
//
return data;
}
}
class VoicePackageConfigureProcessReply extends Reply {
VoicePackageConfigureProcessReply.parseData(
CommandType commandType, List<int> dataDetail)
: super.parseData(commandType, dataDetail) {
data = dataDetail;
status = data[2];
errorWithStstus(status);
}
}
class VoicePackageConfigureConfirmationReply extends Reply {
VoicePackageConfigureConfirmationReply.parseData(
CommandType commandType, List<int> dataDetail)
: super.parseData(commandType, dataDetail) {
data = dataDetail;
status = data[2];
errorWithStstus(status);
}
}

View File

@ -288,16 +288,27 @@ String asciiString(List<int> codeUnits) {
}
String utf8String(List<int> codeUnits) {
codeUnits.reversed;
final List<int> uniqueList = <int>[];
for (int i = 0; i < codeUnits.length; i++) {
if (codeUnits[i] != 0) {
uniqueList.add(codeUnits[i]);
}
try {
// 0
int realLen = codeUnits.indexWhere((b) => b == 0);
List<int> validBytes = (realLen == -1) ? codeUnits : codeUnits.sublist(0, realLen);
return utf8.decode(validBytes);
} catch (e) {
//
return '';
}
uniqueList.reversed;
return utf8.decode(uniqueList).toString();
}
// String utf8String(List<int> codeUnits) {
// codeUnits.reversed;
// final List<int> uniqueList = <int>[];
// for (int i = 0; i < codeUnits.length; i++) {
// if (codeUnits[i] != 0) {
// uniqueList.add(codeUnits[i]);
// }
// }
// uniqueList.reversed;
// return utf8.decode(uniqueList).toString();
// }
bool compareTwoList({List<int>? list1, List<int>? list2}) {
if (list1!.length != list2!.length) {

View File

@ -41,6 +41,10 @@ enum CommandType {
startOATUpgrade, //OTA升级开始 0x30E0
processOTAUpgrade, //OTA升级过程 0x30E1
confirmationOTAUpgrade, //OTA升级确认 0x30E2
startVoicePackageConfigure, // 0x30A1
voicePackageConfigureProcess, // 0x30A2
voicePackageConfigureConfirmation, // 0x30A3
getDeviceModel, // 0x30A4
gatewayConfiguringWifi, // 0x30F4
gatewayConfiguringWifiResult, // 0x30F5
@ -189,6 +193,26 @@ extension ExtensionCommandType on CommandType {
type = CommandType.confirmationOTAUpgrade;
}
break;
case 0x30A1:
{
type = CommandType.startVoicePackageConfigure;
}
break;
case 0x30A2:
{
type = CommandType.voicePackageConfigureProcess;
}
break;
case 0x30A3:
{
type = CommandType.voicePackageConfigureConfirmation;
}
break;
case 0x30A4:
{
type = CommandType.getDeviceModel;
}
break;
case 0x30F4:
{
type = CommandType.gatewayConfiguringWifi;
@ -307,6 +331,18 @@ extension ExtensionCommandType on CommandType {
case CommandType.gatewayGetStatus:
type = 0x30F8;
break;
case CommandType.startVoicePackageConfigure:
type = 0x30A1;
break;
case CommandType.voicePackageConfigureProcess:
type = 0x30A2;
break;
case CommandType.voicePackageConfigureConfirmation:
type = 0x30A3;
break;
case CommandType.getDeviceModel:
type = 0x30A4;
break;
default:
type = 0x300A;
break;
@ -322,9 +358,11 @@ extension ExtensionCommandType on CommandType {
switch (this) {
case CommandType.getLockPublicKey:
case CommandType.processOTAUpgrade:
case CommandType.voicePackageConfigureProcess:
case CommandType.gatewayGetWifiList:
case CommandType.gatewayConfiguringWifi:
case CommandType.gatewayGetStatus:
case CommandType.getDeviceModel:
//
type = 0x20;
break;
@ -428,6 +466,18 @@ extension ExtensionCommandType on CommandType {
case 0x30F8:
t = '获取网关状态';
break;
case 0x30A1:
t = '语音包配置开始';
break;
case 0x30A2:
t = '语音包配置过程';
break;
case 0x30A3:
t = '语音包配置确认';
break;
case 0x30A4:
t = '获取设备型号';
break;
default:
t = '读星锁状态信息';
break;

View File

@ -12,6 +12,7 @@ import 'package:star_lock/blue/io_protocol/io_cleanUpUsers.dart';
import 'package:star_lock/blue/io_protocol/io_deletUser.dart';
import 'package:star_lock/blue/io_protocol/io_editUser.dart';
import 'package:star_lock/blue/io_protocol/io_factoryDataReset.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_readAdminPassword.dart';
@ -21,6 +22,8 @@ import 'package:star_lock/blue/io_protocol/io_referEventRecordTime.dart';
import 'package:star_lock/blue/io_protocol/io_setSupportFunctionsNoParameters.dart';
import 'package:star_lock/blue/io_protocol/io_setSupportFunctionsWithParameters.dart';
import 'package:star_lock/blue/io_protocol/io_timing.dart';
import 'package:star_lock/blue/io_protocol/io_voicePackageConfigure.dart';
import 'package:star_lock/blue/io_protocol/io_voicePackageConfigureProcess.dart';
import '../tools/storage.dart';
import 'io_gateway/io_gateway_getStatus.dart';
@ -292,6 +295,28 @@ class CommandReciverManager {
reply = GatewayGetStatusReply.parseData(commandType, data);
}
break;
case CommandType.getDeviceModel:
{
reply = GetDeviceModelReply.parseData(commandType, data);
}
break;
case CommandType.startVoicePackageConfigure:
{
reply = VoicePackageConfigureReply.parseData(commandType, data);
}
break;
case CommandType.voicePackageConfigureProcess:
{
reply =
VoicePackageConfigureProcessReply.parseData(commandType, data);
}
break;
case CommandType.voicePackageConfigureConfirmation:
{
reply = VoicePackageConfigureConfirmationReply.parseData(
commandType, data);
}
break;
case CommandType.generalExtendedCommond:
{
//

View File

@ -90,6 +90,7 @@ class XSConstantMacro {
static int webBuyTypeVip = 3; //VIP购买
static int webBuyTypeAuth = 4; //
static int webBuyTypeShop = 5; //
static int webBuyTypeCloudStorage = 6; //
//
Future<Map<String, dynamic>> getDeviceInfoData() async {

View File

@ -783,7 +783,7 @@ class LockDetailLogic extends BaseGetXController {
// }
///
void sendMonitorMessage() {
void sendMonitorMessage() async {
final catEyeConfig = state.keyInfos.value.lockSetting?.catEyeConfig ?? [];
final network = state.keyInfos.value.network;
if (catEyeConfig.isNotEmpty &&
@ -840,24 +840,48 @@ class LockDetailLogic extends BaseGetXController {
state.DetailLockInfo = eventBus
.on<PassCurrentLockInformationEvent>()
.listen((PassCurrentLockInformationEvent event) {
if (event.lockSetInfoData.lockSettingInfo != null &&
event.lockSetInfoData.lockSettingInfo!.catEyeConfig != null &&
event.lockSetInfoData.lockSettingInfo!.catEyeConfig!.length > 0) {
if (state.keyInfos.value.lockSetting!.catEyeConfig == null ||
state.keyInfos.value.lockSetting!.catEyeConfig!.length == 0) {
state.keyInfos.value.lockSetting!.catEyeConfig!.add(CatEyeConfig(
//
final lockSettingInfo = event.lockSetInfoData.lockSettingInfo;
final lockBasicInfo = event.lockSetInfoData.lockBasicInfo;
final networkInfo = lockBasicInfo?.networkInfo;
final targetCatEyeConfig = lockSettingInfo?.catEyeConfig;
if (lockBasicInfo != null && networkInfo != null) {
state.keyInfos.value.network?.peerId = networkInfo.peerId;
state.keyInfos.value.network?.wifiName = networkInfo.wifiName;
}
// catEyeConfig
if (lockSettingInfo != null &&
targetCatEyeConfig != null &&
targetCatEyeConfig.isNotEmpty) {
// catEyeConfig
final stateCatEyeConfig =
state.keyInfos.value.lockSetting?.catEyeConfig;
// catEyeConfig
if (stateCatEyeConfig == null || stateCatEyeConfig.isEmpty) {
state.keyInfos.value.lockSetting?.catEyeConfig ??= [];
state.keyInfos.value.lockSetting!.catEyeConfig!.add(
CatEyeConfig(
catEyeMode: 0,
catEyeModeConfig: CatEyeModeConfig(
realTimeMode: 0,
recordEndTime: 0,
recordMode: 0,
recordStartTime: 0,
recordTime: '0',
detectionDistance: '0')));
realTimeMode: 0,
recordEndTime: 0,
recordMode: 0,
recordStartTime: 0,
recordTime: '0',
detectionDistance: '0',
),
),
);
}
state.keyInfos.value.lockSetting!.catEyeConfig![0].catEyeMode = event
.lockSetInfoData.lockSettingInfo!.catEyeConfig![0].catEyeMode ??
1;
// catEyeMode使 1
state.keyInfos.value.lockSetting!.catEyeConfig![0].catEyeMode =
targetCatEyeConfig[0].catEyeMode ?? 1;
//
state.keyInfos.refresh();
}
});

View File

@ -0,0 +1,281 @@
//
import 'package:get/get.dart';
class TimbreItem {
final String timbre;
final String name;
final String timbrePackUrl;
TimbreItem({
required this.timbre,
required this.name,
required this.timbrePackUrl,
});
factory TimbreItem.fromJson(Map<String, dynamic> json) {
return TimbreItem(
timbre: json['timbre'] ?? '',
name: json['name'] ?? '',
timbrePackUrl: json['timbrePackUrl'] ?? '',
);
}
Map<String, dynamic> toJson() {
return {
'timbre': timbre,
'name': name,
'timbrePackUrl': timbrePackUrl,
};
}
@override
String toString() {
return 'TimbreItem{timbre: $timbre, name: $name, timbrePackUrl: $timbrePackUrl}';
}
}
//
class PassthroughItem {
final String lang;
final List<TimbreItem> timbres;
PassthroughItem({
required this.lang,
required this.timbres,
});
factory PassthroughItem.fromJson(Map<String, dynamic> json) {
var timbresJson = json['timbres'] as List<dynamic>?;
List<TimbreItem> timbresList = timbresJson != null
? timbresJson
.map((e) => TimbreItem.fromJson(e as Map<String, dynamic>))
.toList()
: <TimbreItem>[];
return PassthroughItem(
lang: json['lang'] ?? '',
timbres: timbresList,
);
}
Map<String, dynamic> toJson() {
return {
'lang': lang,
'timbres': timbres.map((e) => e.toJson()).toList(),
};
}
@override
String toString() {
return 'PassthroughItem{lang: $lang, timbres: $timbres}';
}
}
class PassthroughListResponse {
PassthroughListResponse({
this.description,
this.errorCode,
this.data,
this.errorMsg,
});
String? description;
int? errorCode;
List<PassthroughItem>? data;
String? errorMsg;
PassthroughListResponse.fromJson(dynamic json) {
description = json['description'];
errorCode = json['errorCode'];
if (json['data'] != null && json['data'] is List) {
data = (json['data'] as List)
.map((e) => PassthroughItem.fromJson(e as Map<String, dynamic>))
.toList();
} else {
data = null;
}
errorMsg = json['errorMsg'];
}
Map<String, dynamic> toJson() {
final map = <String, dynamic>{};
map['description'] = description;
map['errorCode'] = errorCode;
if (data != null) {
map['data'] = data!.map((e) => e.toJson()).toList();
}
map['errorMsg'] = errorMsg;
return map;
}
}
///
/// 'zh_CN' -> '简体中文'.tr'en_US' -> '英文'.tr
class PassthroughLangHelper {
static String getLangText(String langCode) {
switch (langCode.toLowerCase()) {
case 'zh_cn':
return '简体中文'.tr;
case 'zh_tw':
return '繁体中文(中国台湾)'.tr;
case 'zh_hk':
return '繁体中文(中国香港)'.tr;
case 'en_us':
return '英文'.tr;
case 'fr_fr':
return '法语'.tr;
case 'ru_ru':
return '俄语'.tr;
case 'de_de':
return '德语'.tr;
case 'ja_jp':
return '日语'.tr;
case 'ko_kr':
return '韩语'.tr;
case 'it_it':
return '意大利语'.tr;
case 'pt_pt':
return '葡萄牙语'.tr;
case 'es_es':
return '西班牙语'.tr;
case 'ar_sa':
return '阿拉伯语'.tr;
case 'vi_vn':
return '越南语'.tr;
case 'ms_my':
return '马来语'.tr;
case 'nl_nl':
return '荷兰语'.tr;
case 'ro_ro':
return '罗马尼亚语'.tr;
case 'lt_lt':
return '立陶宛语'.tr;
case 'sv_se':
return '瑞典语'.tr;
case 'et_ee':
return '爱沙尼亚语'.tr;
case 'pl_pl':
return '波兰语'.tr;
case 'sk_sk':
return '斯洛伐克语'.tr;
case 'cs_cz':
return '捷克语'.tr;
case 'el_gr':
return '希腊语'.tr;
case 'he_il':
return '希伯来语'.tr;
case 'sr_rs':
return '塞尔维亚语'.tr;
case 'tr_tr':
return '土耳其语'.tr;
case 'hu_hu':
return '匈牙利语'.tr;
case 'bg_bg':
return '保加利亚语'.tr;
case 'kk_kz':
return '哈萨克斯坦语'.tr;
case 'bn_bd':
return '孟加拉语'.tr;
case 'hr_hr':
return '克罗地亚语'.tr;
case 'th_th':
return '泰语'.tr;
case 'id_id':
return '印度尼西亚语'.tr;
case 'fi_fi':
return '芬兰语'.tr;
case 'da_dk':
return '丹麦语'.tr;
case 'uk_ua':
return '乌克兰语'.tr;
case 'hi_in':
return '印地语'.tr;
case 'ur_pk':
return '乌尔都语'.tr;
case 'hy_am':
return '亚美尼亚语'.tr;
case 'ka_ge':
return '格鲁吉亚语'.tr;
default:
return '未知语言'.tr;
}
}
}
class ValidityPeriodResponse {
ValidityPeriodResponse({
this.description,
this.errorCode,
this.data,
this.errorMsg,
});
String? description;
int? errorCode;
ValidityPeriod? data;
String? errorMsg;
ValidityPeriodResponse.fromJson(dynamic json) {
description = json['description'];
errorCode = json['errorCode'];
if (json['data'] != null) {
data = ValidityPeriod.fromJson(json['data']);
} else {
data = null;
}
errorMsg = json['errorMsg'];
}
Map<String, dynamic> toJson() {
final map = <String, dynamic>{};
map['description'] = description;
map['errorCode'] = errorCode;
if (data != null) {
map['data'] = data!.toJson();
}
map['errorMsg'] = errorMsg;
return map;
}
}
class ValidityPeriod {
final int lockId;
final int status;
final String validityPeriodStart;
final String validityPeriodEnd;
final int rollingStorageDays;
final int remainingDays;
ValidityPeriod({
required this.lockId,
required this.status,
required this.validityPeriodStart,
required this.validityPeriodEnd,
required this.rollingStorageDays,
required this.remainingDays,
});
//
factory ValidityPeriod.fromJson(Map<String, dynamic> json) {
return ValidityPeriod(
lockId: json['lockId'] as int? ?? 0,
// 0
status: json['status'] as int? ?? 0,
validityPeriodStart: json['validityPeriodStart'] as String? ?? '',
validityPeriodEnd: json['validityPeriodEnd'] as String? ?? '',
rollingStorageDays: json['rollingStorageDays'] as int? ?? 0,
remainingDays: json['remainingDays'] as int? ?? 0,
);
}
//
Map<String, dynamic> toJson() {
return {
'lockId': lockId,
'status': status,
'validityPeriodStart': validityPeriodStart,
'validityPeriodEnd': validityPeriodEnd,
'rollingStorageDays': rollingStorageDays,
'remainingDays': remainingDays,
};
}
}

View File

@ -161,11 +161,12 @@ class ConfiguringWifiLogic extends BaseGetXController {
// - : sureBtnState updateNetworkInfo
final info = await updateNetworkInfo(
peerId: peerId,
wifiName: wifiName ?? '',
secretKey: secretKey,
deviceMac: deviceMac ?? '',
networkMac: networkMac ?? '');
peerId: peerId,
wifiName: wifiName ?? '',
secretKey: secretKey,
deviceMac: deviceMac ?? '',
networkMac: networkMac ?? '',
);
if (info.errorCode!.codeIsSuccessful) {
// peerID
StartChartManage().lockNetworkInfo = DeviceNetworkInfo(
@ -175,6 +176,11 @@ class ConfiguringWifiLogic extends BaseGetXController {
peerId: peerId,
);
state.lockSetInfoData.value?.lockBasicInfo?.networkInfo?.peerId =
peerId;
state.lockSetInfoData.value?.lockBasicInfo?.networkInfo?.wifiName =
wifiName;
/// ,peerId
StartChartManage().lockPeerId = peerId;
@ -189,7 +195,9 @@ class ConfiguringWifiLogic extends BaseGetXController {
Get.offAllNamed(Routers.starLockMain);
}
eventBus.fire(SuccessfulDistributionNetwork());
eventBus.fire(RefreshLockListInfoDataEvent(clearScanDevices: true,isUnShowLoading: true));
eventBus.fire(RefreshLockListInfoDataEvent(
clearScanDevices: true, isUnShowLoading: true));
eventBus.fire(RefreshLockDetailInfoDataEvent());
});
//
@ -589,7 +597,6 @@ class ConfiguringWifiLogic extends BaseGetXController {
recordType: recordType,
records: records,
isUnShowLoading: true);
if (entity.errorCode!.codeIsSuccessful) {
eventBus
.fire(PassCurrentLockInformationEvent(state.lockSetInfoData.value));

View File

@ -158,6 +158,7 @@ class LockFeature {
this.isElectronicAntiLock,
this.isVisualDoorBellCode,
this.isDoubleLockLinkage,
this.languageSpeech,
});
LockFeature.fromJson(Map<String, dynamic> json) {
@ -218,6 +219,7 @@ class LockFeature {
isElectronicAntiLock = json['isElectronicAntiLock'];
isVisualDoorBellCode = json['isVisualDoorBellCode'];
isDoubleLockLinkage = json['isDoubleLockLinkage'];
languageSpeech = json['languageSpeech'];
}
int? password;
@ -277,6 +279,7 @@ class LockFeature {
int? isElectronicAntiLock;
int? isVisualDoorBellCode;
int? isDoubleLockLinkage;
int? languageSpeech;
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
@ -337,6 +340,7 @@ class LockFeature {
data['isElectronicAntiLock'] = isElectronicAntiLock;
data['isVisualDoorBellCode'] = isVisualDoorBellCode;
data['isDoubleLockLinkage'] = isDoubleLockLinkage;
data['languageSpeech'] = languageSpeech;
return data;
}
}
@ -348,6 +352,8 @@ class LockBasicInfo {
this.keyId,
this.model,
this.electricQuantity,
this.hwVersion,
this.fwVersion,
this.electricQuantityStandby,
this.indate,
this.isLockOwner,
@ -376,6 +382,8 @@ class LockBasicInfo {
keyId = json['keyId'];
model = json['model'];
electricQuantity = json['electricQuantity'];
hwVersion = json['hwVersion'];
fwVersion = json['fwVersion'];
electricQuantityStandby = json['electricQuantityStandby'];
indate = json['indate'];
isLockOwner = json['isLockOwner'];
@ -411,6 +419,8 @@ class LockBasicInfo {
int? keyId;
String? model;
int? electricQuantity;
String? fwVersion;
String? hwVersion;
int? electricQuantityStandby;
int? indate;
int? isLockOwner;
@ -440,6 +450,8 @@ class LockBasicInfo {
data['keyId'] = keyId;
data['model'] = model;
data['electricQuantity'] = electricQuantity;
data['hwVersion'] = hwVersion;
data['fwVersion'] = fwVersion;
data['electricQuantityStandby'] = electricQuantityStandby;
data['indate'] = indate;
data['isLockOwner'] = isLockOwner;
@ -523,6 +535,7 @@ class LockSettingInfo {
this.autoLightScreen,
this.autoLightScreenTime,
this.faceEnErrUnlock,
this.currentVoiceTimbre,
});
LockSettingInfo.fromJson(Map<String, dynamic> json) {
@ -570,6 +583,10 @@ class LockSettingInfo {
autoLightScreen = json['autoLightScreen'];
autoLightScreenTime = json['autoLightScreenTime'];
faceEnErrUnlock = json['faceEnErrUnlock'];
if (json['currentVoiceTimbre'] != null) {
currentVoiceTimbre =
CurrentVoiceTimbre.fromJson(json['currentVoiceTimbre']);
}
}
int? remoteUnlock;
@ -605,6 +622,7 @@ class LockSettingInfo {
int? autoLightScreen; //- 0: 1:
int? autoLightScreenTime; //-
int? faceEnErrUnlock;
CurrentVoiceTimbre? currentVoiceTimbre;
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
@ -645,7 +663,9 @@ class LockSettingInfo {
data['autoLightScreen'] = autoLightScreen;
data['autoLightScreenTime'] = autoLightScreenTime;
data['faceEnErrUnlock'] = faceEnErrUnlock;
if (currentVoiceTimbre != null) {
data['currentVoiceTimbre'] = currentVoiceTimbre!.toJson();
}
return data;
}
}
@ -757,3 +777,29 @@ class CatEyeModeConfig {
return data;
}
}
class CurrentVoiceTimbre {
String? lang;
String? timbre;
CurrentVoiceTimbre({
this.lang,
this.timbre,
});
// JSON
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['lang'] = lang;
data['timbre'] = timbre;
return data;
}
// JSON
factory CurrentVoiceTimbre.fromJson(Map<String, dynamic> json) {
return CurrentVoiceTimbre(
timbre: json['timbre'],
lang: json['lang'],
);
}
}

View File

@ -4,6 +4,7 @@ import 'package:flutter/scheduler.dart';
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
import 'package:get/get.dart';
import 'package:star_lock/apm/apm_helper.dart';
import 'package:star_lock/blue/io_protocol/io_getDeviceModel.dart';
import 'package:star_lock/login/login/entity/LoginEntity.dart';
import 'package:star_lock/main/lockMian/entity/lockListInfo_entity.dart';
@ -66,6 +67,11 @@ class LockSetLogic extends BaseGetXController {
(state.ifCurrentScreen.value == true)) {
_replyUpdataLockSetReply(reply);
}
if (reply is GetDeviceModelReply) {
//
_handlerDeviceModelReply(reply);
}
});
}
@ -728,6 +734,7 @@ class LockSetLogic extends BaseGetXController {
getUpdataLockSet();
_initReplySubscription();
sendGetDeviceModelBleMessage();
// _scanListDiscoveredDeviceSubscriptionAction();
}
@ -737,4 +744,52 @@ class LockSetLogic extends BaseGetXController {
_passCurrentLockInformationEvent!.cancel();
// _scanListDiscoveredDeviceSubscription.cancel();
}
//
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. DeviceModel403~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 deviceModelValue = rawData.substring(0, firstNullIndex);
print(deviceModelValue); // : 2403
print('获取到 DeviceModel (原始): $deviceModelValue');
await Storage.setString(deviceModel, deviceModelValue);
break;
default:
showToast('获取设备型号失败'.tr);
break;
}
}
}

View File

@ -526,6 +526,20 @@ class _LockSetPageState extends State<LockSetPage>
'lockSetInfoData': state.lockSetInfoData.value
});
})),
//
Visibility(
visible: state.lockFeature.value.languageSpeech == 1,
child: CommonItem(
leftTitel: '语音包设置'.tr,
rightTitle: '',
isHaveLine: true,
isHaveDirection: true,
action: () {
Get.toNamed(Routers.speechLanguageSettingsPage,
arguments: <String, LockSetInfoData>{
'lockSetInfoData': state.lockSetInfoData.value
});
})),
// 广使
/* 2024-01-12 广 by DaisyWu
Obx(() => Visibility(

View File

@ -0,0 +1,366 @@
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';
class SpeechLanguageSettingsLogic extends BaseGetXController {
final SpeechLanguageSettingsState state = SpeechLanguageSettingsState();
StreamSubscription<Reply>? _replySubscription;
@override
void onInit() async {
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) {
final PassthroughItem item =
state.languages[state.selectPassthroughListIndex.value];
final timbre = item.timbres[state.selectLanguageIndex.value];
final LoginEntity entity =
await ApiRepository.to.settingCurrentVoiceTimbre(
data: {
'lang': item.lang,
'timbre': timbre.timbre,
},
lockId: state.lockSetInfoData.value.lockId!,
);
if (entity.errorCode!.codeIsSuccessful) {
showSuccess('设置成功'.tr, something: () {
state.lockSetInfoData.value.lockSettingInfo?.currentVoiceTimbre
?.lang = item.lang;
state.lockSetInfoData.value.lockSettingInfo?.currentVoiceTimbre
?.timbre = timbre.timbre;
eventBus.fire(
PassCurrentLockInformationEvent(state.lockSetInfoData.value));
});
}
dismissEasyLoading();
}
});
state.deviceModel.value = await Storage.getString(deviceModel) ?? '';
debugPrint('设备型号:${state.deviceModel.value}');
if (state.deviceModel.value != null) {
await initList();
}
// await sendGetDeviceModelBleMessage();
}
///
initList() async {
showEasyLoading();
try {
final PassthroughListResponse entity = await ApiRepository.to
.getPassthroughList(data: {'deviceType': state.deviceModel.value});
if (entity.errorCode!.codeIsSuccessful) {
state.languages.value = entity.data!!;
final oldTimbre = state
.lockSetInfoData.value.lockSettingInfo?.currentVoiceTimbre?.timbre;
final oldLang = state
.lockSetInfoData.value.lockSettingInfo?.currentVoiceTimbre?.lang;
for (int index = 0; index < state.languages.value.length; index++) {
final element = state.languages.value[index];
final timbres = element.timbres;
for (int i = 0; i < timbres.length; i++) {
final timbre = timbres[i].timbre;
if ((oldLang != null && oldLang == element.lang) &&
(oldTimbre != null && oldTimbre == timbre)) {
state.selectPassthroughListIndex.value = index;
state.selectLanguageIndex.value = i;
}
}
}
}
} catch (e) {
debugPrint('获取语音包出现错误:$e');
} finally {
dismissEasyLoading();
}
}
void saveSpeechLanguageSettings() async {
//
if (state.progress.value > 0) {
return;
}
final oldTimbre =
state.lockSetInfoData.value.lockSettingInfo?.currentVoiceTimbre?.timbre;
final oldLang =
state.lockSetInfoData.value.lockSettingInfo?.currentVoiceTimbre?.lang;
EasyLoading.showProgress(state.progress.value, status: '正在发送数据');
final PassthroughItem item =
state.languages[state.selectPassthroughListIndex.value];
final timbre = item.timbres[state.selectLanguageIndex.value];
debugPrint('选中的语音是:${timbre}');
if ((oldLang != null && oldLang == item.lang) &&
(oldTimbre != null && oldTimbre == timbre.timbre)) {
showToast('已设置为当前选择的语音包'.tr);
}
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. DeviceModel403~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();
}
}

View File

@ -0,0 +1,169 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:star_lock/app_settings/app_colors.dart';
import 'package:star_lock/main/lockDetail/lockDetail/passthrough_item.dart';
import 'package:star_lock/main/lockDetail/lockSet/speechLanguageSettings/speech_language_settings_logic.dart';
import 'package:star_lock/main/lockDetail/lockSet/speechLanguageSettings/speech_language_settings_state.dart';
import 'package:star_lock/tools/EasyRefreshTool.dart';
import 'package:star_lock/tools/titleAppBar.dart';
class SpeechLanguageSettingsPage extends StatefulWidget {
const SpeechLanguageSettingsPage();
@override
State<SpeechLanguageSettingsPage> createState() =>
_SpeechLanguageSettingsPageState();
}
class _SpeechLanguageSettingsPageState
extends State<SpeechLanguageSettingsPage> {
final SpeechLanguageSettingsLogic logic =
Get.put(SpeechLanguageSettingsLogic());
final SpeechLanguageSettingsState state =
Get.find<SpeechLanguageSettingsLogic>().state;
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColors.mainBackgroundColor,
appBar: TitleAppBar(
barTitle: '锁语音包设置'.tr,
haveBack: true,
backgroundColor: AppColors.mainColor,
actionsList: [
TextButton(
onPressed: logic.saveSpeechLanguageSettings,
child: Text(
'保存'.tr,
style: TextStyle(
color: Colors.white,
fontSize: 24.sp,
fontWeight: FontWeight.w500,
),
),
),
],
),
body: EasyRefreshTool(
child: _buildBody(),
onRefresh: () {
logic.sendGetDeviceModelBleMessage();
},
),
);
}
Widget _buildBody() {
return Obx(
() => SingleChildScrollView(
child: Column(
children: [
// Container(
// width: 1.sw,
// decoration: BoxDecoration(color: Colors.white),
// child: Column(
// children: [
// RadioListTile(
// title: Text("男声".tr),
// value: 1,
// groupValue: state.selectedValue.value,
// onChanged: (value) {
// state.selectedValue.value = int.parse(value.toString());
// },
// ),
// RadioListTile(
// title: Text("女声".tr),
// value: 2,
// groupValue: state.selectedValue.value,
// onChanged: (value) {
// state.selectedValue.value = int.parse(value.toString());
// },
// )
// ],
// ),
// ),
SizedBox(
height: 8.h,
),
Column(
children: _buildList(),
),
],
),
),
);
}
List<Widget> _buildList() {
final languages = state.languages;
return List.generate(
languages.length,
(index) => _buildItem(languages[index], index),
);
}
_buildItem(PassthroughItem language, int index) {
final timbres = language.timbres;
final isSelected = state.selectPassthroughListIndex == index;
return ExpansionTile(
title: Text(
PassthroughLangHelper.getLangText(language.lang),
style: TextStyle(
fontSize: 24.sp,
fontWeight: isSelected ? FontWeight.bold : null,
),
),
onExpansionChanged: (bool expanded) {},
initiallyExpanded: false,
backgroundColor: Colors.white,
collapsedBackgroundColor: Colors.white,
expandedCrossAxisAlignment: CrossAxisAlignment.center,
expandedAlignment: Alignment.center,
shape: InputBorder.none,
maintainState: true,
//
collapsedShape: InputBorder.none,
//
childrenPadding: EdgeInsets.only(left: 12.w),
children: List.generate(
timbres.length,
(int languageIndex) => ListTile(
title: Text(
timbres[languageIndex].name,
style: TextStyle(
fontSize: 22.sp,
fontWeight:
state.selectLanguageIndex == languageIndex && isSelected
? FontWeight.bold
: null,
),
),
trailing: state.selectLanguageIndex == languageIndex && isSelected
? Icon(
Icons.check_circle,
color: AppColors.mainColor,
) //
: null, //
onTap: () {
//
state.selectLanguageIndex.value = languageIndex;
//
state.selectPassthroughListIndex.value = index;
}, // , //
),
),
);
}
@override
void dispose() {
// TODO: implement dispose
super.dispose();
if (EasyLoading.isShow) {
EasyLoading.dismiss(animation: true);
}
}
}

View File

@ -0,0 +1,46 @@
import 'dart:typed_data';
import 'package:flutter/widgets.dart';
import 'package:get/get.dart';
import 'package:star_lock/main/lockDetail/lockDetail/passthrough_item.dart';
import 'package:star_lock/main/lockDetail/lockSet/lockSet/lockSetInfo_entity.dart';
class SpeechLanguageSettingsState {
SpeechLanguageSettingsState() {
final map = Get.arguments;
lockSetInfoData.value = map['lockSetInfoData'];
}
Rx<LockSetInfoData> lockSetInfoData = LockSetInfoData().obs;
//
RxInt selectPassthroughListIndex = 0.obs;
//
RxInt selectLanguageIndex = 0.obs;
final RxList<PassthroughItem> languages = <PassthroughItem>[].obs;
Map<int, String> languageSpeechDeviceTypeMapping = {0: '2403'};
RxBool otaUpdateIng = false.obs;
RxDouble otaProgress = 0.00.obs;
RxString deviceModel = '2403'.obs;
Uint8List? data;
//
int voiceSubcontractingSize = 256;
//
int voiceSubcontractingCount = 0;
//
int voiceSubcontractingIndex = 0;
// 0.0~1.0
RxDouble progress = 0.0.obs;
RxInt selectedValue = 1.obs;
}

View File

@ -2,8 +2,12 @@ import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:get/get.dart';
import 'package:path_provider/path_provider.dart';
import 'package:star_lock/appRouters.dart';
import 'package:star_lock/common/XSConstantMacro/XSConstantMacro.dart';
import 'package:star_lock/main/lockDetail/videoLog/videoLog/videoLog_entity.dart';
import 'package:star_lock/mine/valueAddedServices/advancedFeaturesWeb/advancedFeaturesWeb_entity.dart';
import 'package:star_lock/network/api_repository.dart';
import 'package:star_lock/tools/baseGetXController.dart';
@ -142,10 +146,45 @@ class VideoLogLogic extends BaseGetXController {
}
}
@override
onReady() {
super.onReady();
getLockPassthroughInfo() async {
showEasyLoading();
try {
var entity = await ApiRepository.to.getValidityPeriodInfo(
lockId: state.getLockId.value,
);
if (entity.errorCode!.codeIsSuccessful) {
state.validityPeriodInfo.value = entity.data!;
}
} catch (e) {
} finally {
dismissEasyLoading();
}
}
@override
void onInit() async {
await getLockPassthroughInfo();
super.onInit();
}
@override
onReady() async {
super.onReady();
getLockCloudStorageList();
}
getWebPlayUrl() async {
final AdvancedFeaturesWebEntity entity =
await ApiRepository.to.getServicePackageBuyUrl();
if (entity.errorCode!.codeIsSuccessful) {
state.cloudStorageWebViewUrl.value = entity.data!.cloudStorage!;
final uploadReportBuyRequest = await ApiRepository.to
.uploadReportBuyRequest(lockId: state.getLockId.value);
if (uploadReportBuyRequest.errorCode!.codeIsSuccessful) {
Get.toNamed(Routers.advancedFeaturesWebPage, arguments: <String, int>{
'webBuyType': XSConstantMacro.webBuyTypeCloudStorage,
});
}
}
}
}

View File

@ -26,7 +26,9 @@ class VideoLogPage extends StatefulWidget {
class _VideoLogPageState extends State<VideoLogPage> {
final VideoLogLogic logic = Get.put(VideoLogLogic());
final VideoLogState state = Get.find<VideoLogLogic>().state;
final VideoLogState state = Get
.find<VideoLogLogic>()
.state;
@override
void initState() {
@ -54,64 +56,66 @@ class _VideoLogPageState extends State<VideoLogPage> {
// title加编辑按钮
editVideoTip(),
Obx(
() => Visibility(
visible: !state.isNavLocal.value,
child: state.videoLogList.length > 0
? Expanded(
child: ListView.builder(
itemCount: state.videoLogList.length,
itemBuilder: (BuildContext c, int index) {
final CloudStorageData item =
state.videoLogList[index];
return Column(
children: <Widget>[
Container(
margin: EdgeInsets.only(
left: 20.w, top: 15.w, bottom: 15.w),
child: Row(children: <Widget>[
Text(item.date ?? '',
style: TextStyle(fontSize: 20.sp)),
])),
mainListView(index, item)
],
);
},
),
)
: _buildNotData(),
),
() =>
Visibility(
visible: !state.isNavLocal.value,
child: state.videoLogList.length > 0
? Expanded(
child: ListView.builder(
itemCount: state.videoLogList.length,
itemBuilder: (BuildContext c, int index) {
final CloudStorageData item =
state.videoLogList[index];
return Column(
children: <Widget>[
Container(
margin: EdgeInsets.only(
left: 20.w, top: 15.w, bottom: 15.w),
child: Row(children: <Widget>[
Text(item.date ?? '',
style: TextStyle(fontSize: 20.sp)),
])),
mainListView(index, item)
],
);
},
),
)
: _buildNotData(),
),
),
//
Obx(
() => Visibility(
visible: state.isNavLocal.value,
child: state.lockVideoList.length > 0
? Expanded(
child: ListView.builder(
itemCount: state.lockVideoList.length,
itemBuilder: (BuildContext c, int index) {
final CloudStorageData item =
state.lockVideoList[index];
return Column(
children: <Widget>[
Container(
margin: EdgeInsets.only(
left: 20.w, top: 15.w, bottom: 15.w),
child: Row(
children: <Widget>[
Text(item.date ?? '',
style: TextStyle(fontSize: 20.sp)),
],
),
() =>
Visibility(
visible: state.isNavLocal.value,
child: state.lockVideoList.length > 0
? Expanded(
child: ListView.builder(
itemCount: state.lockVideoList.length,
itemBuilder: (BuildContext c, int index) {
final CloudStorageData item =
state.lockVideoList[index];
return Column(
children: <Widget>[
Container(
margin: EdgeInsets.only(
left: 20.w, top: 15.w, bottom: 15.w),
child: Row(
children: <Widget>[
Text(item.date ?? '',
style: TextStyle(fontSize: 20.sp)),
],
),
lockMainListView(index, item)
],
);
},
),
)
: _buildNotData(),
),
),
lockMainListView(index, item)
],
);
},
),
)
: _buildNotData(),
),
),
],
),
@ -157,13 +161,14 @@ class _VideoLogPageState extends State<VideoLogPage> {
// logic.clearDownloads();
});
},
child: Obx(() => Text('云存'.tr,
style: state.isNavLocal.value == true
? TextStyle(
child: Obx(() =>
Text('云存'.tr,
style: state.isNavLocal.value == true
? TextStyle(
color: Colors.grey,
fontSize: 26.sp,
fontWeight: FontWeight.w600)
: TextStyle(
: TextStyle(
color: Colors.white,
fontSize: 28.sp,
fontWeight: FontWeight.w600)))),
@ -175,18 +180,19 @@ class _VideoLogPageState extends State<VideoLogPage> {
});
},
child: Obx(
() => Text(
'已下载'.tr,
style: state.isNavLocal.value == true
? TextStyle(
() =>
Text(
'已下载'.tr,
style: state.isNavLocal.value == true
? TextStyle(
color: Colors.white,
fontSize: 28.sp,
fontWeight: FontWeight.w600)
: TextStyle(
: TextStyle(
color: Colors.grey,
fontSize: 26.sp,
fontWeight: FontWeight.w600),
),
),
),
),
],
@ -197,41 +203,120 @@ class _VideoLogPageState extends State<VideoLogPage> {
//
Widget vipTip() {
return GestureDetector(
onTap: () {
Get.toNamed(Routers.valueAddedServicesHighFunctionPage);
onTap: () async {
await logic.getWebPlayUrl();
// Get.toNamed(Routers.valueAddedCloudStoragePage);
// Get.toNamed(Routers.valueAddedServicesHighFunctionPage);
},
child: Container(
// height: 150.h,
margin: EdgeInsets.all(15.w),
padding:
EdgeInsets.only(left: 20.w, top: 20.w, bottom: 20.w, right: 10.w),
EdgeInsets.only(left: 20.w, top: 20.w, bottom: 20.w, right: 10.w),
decoration: BoxDecoration(
color: const Color(0xFFF6F7F8),
borderRadius: BorderRadius.circular(20.h)),
child: Row(
children: <Widget>[
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('3天滚动储存'.tr, style: TextStyle(fontSize: 24.sp)),
SizedBox(height: 10.h),
Text("${F.navTitle}${"已为本设备免费提供3大滚动视频储存服务".tr}",
style: TextStyle(fontSize: 22.sp, color: Colors.grey)),
],
)),
SizedBox(width: 15.w),
Text('去升级'.tr, style: TextStyle(fontSize: 22.sp)),
Image(
width: 40.w,
height: 24.w,
image: const AssetImage('images/icon_right_black.png'))
],
child: Obx(
() =>
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: <Widget>[
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('3天滚动储存'.tr,
style: TextStyle(fontSize: 24.sp)),
SizedBox(height: 10.h),
Text("${F
.navTitle}${"已为本设备免费提供3大滚动视频储存服务"
.tr}",
style:
TextStyle(fontSize: 22.sp, color: Colors
.grey)),
],
)),
SizedBox(width: 15.w),
Text('去升级'.tr, style: TextStyle(fontSize: 22.sp)),
Image(
width: 40.w,
height: 24.w,
image: const AssetImage(
'images/icon_right_black.png'))
],
),
SizedBox(
height: 16.h,
),
Text(
'云存服务状态:${_handlerValidityPeriodStatsText()}',
style: TextStyle(
fontSize: 24.sp,
),
),
SizedBox(
height: 8.h,
),
Visibility(
visible: state.validityPeriodInfo.value != null &&
state.validityPeriodInfo.value?.status == 1,
child: Text(
'过期时间:${state.validityPeriodInfo.value
?.validityPeriodEnd}',
style: TextStyle(
fontSize: 24.sp,
),
),
),
SizedBox(
height: 8.h,
),
Visibility(
visible: state.validityPeriodInfo.value != null &&
state.validityPeriodInfo.value?.status == 1,
child: Text(
'滚动存储天数:${state.validityPeriodInfo.value?.rollingStorageDays}',
style: TextStyle(
fontSize: 24.sp,
),
),
),
SizedBox(
height: 8.h,
),
Visibility(
visible: state.validityPeriodInfo.value != null &&
state.validityPeriodInfo.value?.status == 1,
child: Text(
'剩余天数:${state.validityPeriodInfo.value
?.remainingDays} ',
style: TextStyle(
fontSize: 24.sp,
),
),
),
],
),
),
),
);
}
_handlerValidityPeriodStatsText() {
if (state.validityPeriodInfo.value == null) {
return '';
}
if (state.validityPeriodInfo.value?.status == 1) {
return '已开通'.tr;
} else if (state.validityPeriodInfo.value?.status == 2) {
return '已过期'.tr;
} else {
return '未开通'.tr;
}
}
//
Widget localTip() {
return GestureDetector(
@ -244,7 +329,7 @@ class _VideoLogPageState extends State<VideoLogPage> {
// height: 130.h,
margin: EdgeInsets.all(15.w),
padding:
EdgeInsets.only(left: 20.w, top: 30.w, bottom: 30.w, right: 10.w),
EdgeInsets.only(left: 20.w, top: 30.w, bottom: 30.w, right: 10.w),
decoration: BoxDecoration(
color: const Color(0xFFF6F7F8),
borderRadius: BorderRadius.circular(20.h)),
@ -252,15 +337,15 @@ class _VideoLogPageState extends State<VideoLogPage> {
children: <Widget>[
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
// SizedBox(height: 20.h),
Text('下载列表'.tr, style: TextStyle(fontSize: 24.sp)),
SizedBox(height: 15.h),
Text('暂无下载内容'.tr,
style: TextStyle(fontSize: 22.sp, color: Colors.grey)),
],
)),
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
// SizedBox(height: 20.h),
Text('下载列表'.tr, style: TextStyle(fontSize: 24.sp)),
SizedBox(height: 15.h),
Text('暂无下载内容'.tr,
style: TextStyle(fontSize: 22.sp, color: Colors.grey)),
],
)),
SizedBox(width: 15.w),
// Text("去升级", style: TextStyle(fontSize: 24.sp)),
Image(
@ -334,7 +419,7 @@ class _VideoLogPageState extends State<VideoLogPage> {
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
//
//
crossAxisCount: 3,
//
mainAxisSpacing: 15.w,
@ -356,7 +441,7 @@ class _VideoLogPageState extends State<VideoLogPage> {
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
//
//
crossAxisCount: 3,
//
mainAxisSpacing: 15.w,
@ -384,9 +469,10 @@ class _VideoLogPageState extends State<VideoLogPage> {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => FullScreenImagePage(
imageUrl: recordData.imagesUrl!,
),
builder: (context) =>
FullScreenImagePage(
imageUrl: recordData.imagesUrl!,
),
),
);
}

View File

@ -1,6 +1,8 @@
import 'dart:typed_data';
import 'package:get/get.dart';
import 'package:get/get_rx/get_rx.dart';
import 'package:star_lock/main/lockDetail/lockDetail/passthrough_item.dart';
import 'package:star_lock/main/lockDetail/videoLog/videoLog/videoLog_entity.dart';
class VideoLogState {
@ -17,4 +19,8 @@ class VideoLogState {
getLockId.value = map['lockId'];
}
}
//
final validityPeriodInfo = Rx<ValidityPeriod?>(null);
RxString cloudStorageWebViewUrl = ''.obs;
}

View File

@ -16,6 +16,5 @@ class VideoLogDetailState {
if (map['videoDataList'] != null) {
videoLogList.value = map['videoDataList'];
}
print('object');
}
}

View File

@ -0,0 +1,406 @@
import 'dart:async';
import 'dart:typed_data';
import 'package:crypto/crypto.dart';
import 'package:flutter/cupertino.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/appRouters.dart';
import 'package:star_lock/app_settings/app_colors.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_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/main/lockDetail/lockDetail/passthrough_item.dart';
import 'package:star_lock/mine/addLock/lock_voice_setting/lock_voice_setting_state.dart';
import 'package:star_lock/network/api_repository.dart';
import 'package:star_lock/tools/baseGetXController.dart';
import 'package:star_lock/tools/eventBusEventManage.dart';
import 'package:star_lock/tools/showTipView.dart';
import 'package:star_lock/tools/storage.dart';
import 'package:http/http.dart' as http;
class LockVoiceSettingLogic extends BaseGetXController {
LockVoiceSettingState state = LockVoiceSettingState();
StreamSubscription<Reply>? _replySubscription;
bool _isThrottled = false;
@override
void onInit() async {
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) {
handleVoiceConfigureThrottled(reply);
}
});
await sendGetDeviceModelBleMessage();
}
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 {
final PassthroughItem item =
state.languages[state.selectPassthroughListIndex.value];
final timbre = item.timbres[state.selectLanguageIndex.value];
final LoginEntity entity = await ApiRepository.to.settingCurrentVoiceTimbre(
data: {
'lang': item.lang,
'timbre': timbre.timbre,
},
lockId: state.lockSetInfoData.value.lockId!,
);
if (entity.errorCode!.codeIsSuccessful) {
showCupertinoDialog(
context: Get.context!,
builder: (BuildContext context) {
return CupertinoAlertDialog(
title: Text('语音设置'.tr),
content: Text('语音设置成功'.tr),
actions: <Widget>[
CupertinoDialogAction(
child: Text(
'取消'.tr,
style: TextStyle(color: AppColors.mainColor),
),
onPressed: () {
Get.back();
},
),
CupertinoDialogAction(
child: Text(
'返回主页'.tr,
style: TextStyle(color: AppColors.mainColor),
),
onPressed: () {
state.lockSetInfoData.value.lockSettingInfo
?.currentVoiceTimbre?.lang = item.lang;
state.lockSetInfoData.value.lockSettingInfo
?.currentVoiceTimbre?.timbre = timbre.timbre;
eventBus.fire(PassCurrentLockInformationEvent(
state.lockSetInfoData.value));
Get.offAllNamed(Routers.starLockMain);
},
),
],
);
},
);
}
dismissEasyLoading();
}
void saveSpeechLanguageSettings() async {
//
if (state.progress.value > 0) {
return;
}
final oldTimbre =
state.lockSetInfoData.value.lockSettingInfo?.currentVoiceTimbre?.timbre;
final oldLang =
state.lockSetInfoData.value.lockSettingInfo?.currentVoiceTimbre?.lang;
EasyLoading.showProgress(state.progress.value, status: '正在发送数据');
final PassthroughItem item =
state.languages[state.selectPassthroughListIndex.value];
final timbre = item.timbres[state.selectLanguageIndex.value];
debugPrint('选中的语音是:${timbre}');
if ((oldLang != null && oldLang == item.lang) &&
(oldTimbre != null && oldTimbre == timbre.timbre)) {
showToast('已设置为当前选择的语音包'.tr);
}
await downloadFile(timbre.timbrePackUrl);
}
//
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();
// showBlueConnetctToast();
}
});
}
void _handlerVoicePackageConfigureProcess(
VoicePackageConfigureProcessReply reply) {
final int status = reply.data[2];
switch (status) {
case 0x00:
cancelBlueConnetctToastTimer();
state.voiceSubcontractingIndex++;
_sendNextPackage();
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 _handlerDeviceModelReply(GetDeviceModelReply reply) async {
final int status = reply.data[2];
switch (status) {
case 0x00:
//
cancelBlueConnetctToastTimer();
// 3. DeviceModel403~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 (原始): $deviceModel');
state.deviceModel.value = deviceModel;
await initList();
break;
default:
showToast('获取设备型号失败'.tr);
break;
}
}
///
initList() async {
showEasyLoading();
try {
final PassthroughListResponse entity = await ApiRepository.to
.getPassthroughList(data: {'deviceType': state.deviceModel.value});
if (entity.errorCode!.codeIsSuccessful) {
state.languages.value = entity.data!!;
final oldTimbre = state
.lockSetInfoData.value.lockSettingInfo?.currentVoiceTimbre?.timbre;
final oldLang = state
.lockSetInfoData.value.lockSettingInfo?.currentVoiceTimbre?.lang;
for (int index = 0; index < state.languages.value.length; index++) {
final element = state.languages.value[index];
final timbres = element.timbres;
for (int i = 0; i < timbres.length; i++) {
final timbre = timbres[i].timbre;
if ((oldLang != null && oldLang == element.lang) &&
(oldTimbre != null && oldTimbre == timbre)) {
state.selectPassthroughListIndex.value = index;
state.selectLanguageIndex.value = i;
}
}
}
}
} catch (e) {
debugPrint('获取语音包出现错误:$e');
} finally {
dismissEasyLoading();
}
}
//
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 _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();
}
});
}
@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();
}
}

View File

@ -0,0 +1,150 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:get/get_utils/get_utils.dart';
import 'package:star_lock/appRouters.dart';
import 'package:star_lock/app_settings/app_colors.dart';
import 'package:star_lock/main/lockDetail/lockDetail/passthrough_item.dart';
import 'package:star_lock/mine/addLock/lock_voice_setting/lock_voice_setting_logic.dart';
import 'package:star_lock/mine/addLock/lock_voice_setting/lock_voice_setting_state.dart';
import 'package:star_lock/tools/EasyRefreshTool.dart';
import 'package:star_lock/tools/titleAppBar.dart';
class LockVoiceSetting extends StatefulWidget {
const LockVoiceSetting();
@override
State<LockVoiceSetting> createState() => _LockVoiceSettingState();
}
class _LockVoiceSettingState extends State<LockVoiceSetting> {
final LockVoiceSettingLogic logic = Get.put(LockVoiceSettingLogic());
final LockVoiceSettingState state = Get.find<LockVoiceSettingLogic>().state;
@override
Widget build(BuildContext context) {
return EasyRefreshTool(
onRefresh: () {
logic.sendGetDeviceModelBleMessage();
},
child: Scaffold(
backgroundColor: AppColors.mainBackgroundColor,
appBar: TitleAppBar(
barTitle: '锁语音包设置'.tr,
haveBack: false,
haveOtherLeftWidget: true,
leftWidget: TextButton(
onPressed: () {
Get.offAllNamed(Routers.starLockMain);
},
child: Text(
'跳过'.tr,
style: TextStyle(
color: Colors.white,
fontSize: 24.sp,
fontWeight: FontWeight.w500,
),
),
),
backgroundColor: AppColors.mainColor,
actionsList: [
TextButton(
onPressed: logic.saveSpeechLanguageSettings,
child: Text(
'保存'.tr,
style: TextStyle(
color: Colors.white,
fontSize: 24.sp,
fontWeight: FontWeight.w500,
),
),
),
],
),
body: _buildBody(),
),
);
}
Widget _buildBody() {
return Obx(
() => SingleChildScrollView(
child: Column(
children: _buildList(),
),
),
);
}
List<Widget> _buildList() {
final languages = state.languages;
return List.generate(
languages.length,
(index) => _buildItem(languages[index], index),
);
}
_buildItem(PassthroughItem language, int index) {
final timbres = language.timbres;
final isSelected = state.selectPassthroughListIndex == index;
return ExpansionTile(
title: Text(
PassthroughLangHelper.getLangText(language.lang),
style: TextStyle(
fontSize: 24.sp,
fontWeight: isSelected ? FontWeight.bold : null,
),
),
onExpansionChanged: (bool expanded) {},
initiallyExpanded: false,
backgroundColor: Colors.white,
collapsedBackgroundColor: Colors.white,
expandedCrossAxisAlignment: CrossAxisAlignment.center,
expandedAlignment: Alignment.center,
shape: InputBorder.none,
maintainState: true,
//
collapsedShape: InputBorder.none,
//
childrenPadding: EdgeInsets.only(left: 12.w),
children: List.generate(
timbres.length,
(int languageIndex) => ListTile(
title: Text(
timbres[languageIndex].name,
style: TextStyle(
fontSize: 22.sp,
fontWeight:
state.selectLanguageIndex == languageIndex && isSelected
? FontWeight.bold
: null,
),
),
trailing: state.selectLanguageIndex == languageIndex && isSelected
? Icon(
Icons.check_circle,
color: AppColors.mainColor,
) //
: null, //
onTap: () {
//
state.selectLanguageIndex.value = languageIndex;
//
state.selectPassthroughListIndex.value = index;
}, // , //
),
),
);
}
@override
void dispose() {
// TODO: implement dispose
super.dispose();
if (EasyLoading.isShow) {
EasyLoading.dismiss(animation: true);
}
}
}

View File

@ -0,0 +1,47 @@
import 'dart:typed_data';
import 'package:get/get.dart';
import 'package:star_lock/main/lockDetail/lockDetail/passthrough_item.dart';
import 'package:star_lock/main/lockDetail/lockSet/lockSet/lockSetInfo_entity.dart';
class LockVoiceSettingState {
LockVoiceSettingState() {
final map = Get.arguments;
lockSetInfoData.value = map['lockSetInfoData'];
lockBasicInfo.value = lockSetInfoData.value.lockBasicInfo!;
}
Rx<LockSetInfoData> lockSetInfoData = LockSetInfoData().obs;
Rx<LockBasicInfo> lockBasicInfo = LockBasicInfo().obs;
//
RxInt selectPassthroughListIndex = 0.obs;
//
RxInt selectLanguageIndex = 0.obs;
final RxList<PassthroughItem> languages = <PassthroughItem>[].obs;
Map<int, String> languageSpeechDeviceTypeMapping = {0: '2403'};
RxBool otaUpdateIng = false.obs;
RxDouble otaProgress = 0.00.obs;
RxString deviceModel = '2403'.obs;
Uint8List? data;
//
int voiceSubcontractingSize = 256;
//
int voiceSubcontractingCount = 0;
//
int voiceSubcontractingIndex = 0;
// 0.0~1.0
RxDouble progress = 0.0.obs;
}

View File

@ -486,7 +486,6 @@ class SaveLockLogic extends BaseGetXController {
// }
void backAction() async {
// BlueManage().disconnect();
//
@ -502,13 +501,19 @@ class SaveLockLogic extends BaseGetXController {
'lockSetInfoData': state.lockSetInfoData.value,
'pageName': 'saveLock'
});
} else if (state.lockSetInfoData.value.lockFeature?.languageSpeech == 1) {
Get.toNamed(Routers.lockVoiceSettingPage, arguments: {
'lockSetInfoData': state.lockSetInfoData.value,
'pageName': 'saveLock'
});
} else {
eventBus.fire(RefreshLockListInfoDataEvent(clearScanDevices: true,isUnShowLoading: true));
eventBus.fire(RefreshLockListInfoDataEvent(
clearScanDevices: true, isUnShowLoading: true));
Future<void>.delayed(const Duration(seconds: 1), () {
// Get.close(state.isFromMap == 1
// ? (CommonDataManage().seletLockType == 0 ? 4 : 5)
// : (CommonDataManage().seletLockType == 0 ? 5 : 6));
Get.until((route) => route.isFirst);
Get.until((route) => route.isFirst);
});
// 2
Future<void>.delayed(const Duration(milliseconds: 200), () {
@ -520,7 +525,8 @@ class SaveLockLogic extends BaseGetXController {
});
}
} else {
eventBus.fire(RefreshLockListInfoDataEvent(clearScanDevices: true,isUnShowLoading: true));
eventBus.fire(RefreshLockListInfoDataEvent(
clearScanDevices: true, isUnShowLoading: true));
Future<void>.delayed(const Duration(seconds: 1), () {
// Get.close(state.isFromMap == 1
// ? (CommonDataManage().seletLockType == 0 ? 4 : 5)

View File

@ -84,17 +84,17 @@ class _SelectLockTypePageState extends State<SelectLockTypePage>
arguments: <String, int>{'getLockType': 1});
}),
// if (!F.isLite)
lockTypeItem('images/lockType/lockType_NFCLock.png', 'NFC无源锁'.tr, () {
CommonDataManage().seletLockType = 2;
// Navigator.pushNamed(context, Routers.addLockPage);
logic.getNearByLimits();
}),
lockTypeItem('images/lockType/lockType_NFCLock.png', 'NFC无源锁'.tr, () {
CommonDataManage().seletLockType = 2;
// Navigator.pushNamed(context, Routers.addLockPage);
logic.getNearByLimits();
}),
// if (!F.isLite)
lockTypeItem('images/lockType/lockType_padlock.png', '挂锁'.tr, () {
CommonDataManage().seletLockType = 3;
// Navigator.pushNamed(context, Routers.addLockPage);
logic.getNearByLimits();
}),
lockTypeItem('images/lockType/lockType_padlock.png', '挂锁'.tr, () {
CommonDataManage().seletLockType = 3;
// Navigator.pushNamed(context, Routers.addLockPage);
logic.getNearByLimits();
}),
lockTypeItem('images/lockType/lockType_safeLock.png', '保险箱锁'.tr, () {
CommonDataManage().seletLockType = 4;
Navigator.pushNamed(context, Routers.addLockPage,
@ -223,9 +223,16 @@ class _SelectLockTypePageState extends State<SelectLockTypePage>
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(lockTypeTitle,
style: TextStyle(
fontSize: 22.sp, color: AppColors.blackColor)),
Text(
lockTypeTitle,
maxLines: 2, //
overflow: TextOverflow.ellipsis, //
softWrap: true, //
style: TextStyle(
fontSize: 22.sp,
color: AppColors.blackColor,
),
),
],
),
),

View File

@ -32,12 +32,16 @@ class Data {
String? vipBuyUrl;
String? cloudauthBuyUrl;
String? shopList;
String? cloudStorage;
String? valueAddServiceLimitFree;
Data(
{this.smsBuyUrl,
this.emailBuyUrl,
this.vipBuyUrl,
this.cloudauthBuyUrl,
this.cloudStorage,
this.valueAddServiceLimitFree,
this.shopList});
Data.fromJson(Map<String, dynamic> json) {
@ -46,6 +50,8 @@ class Data {
vipBuyUrl = json['vip_buy_url'];
cloudauthBuyUrl = json['cloudauth_buy_url'];
shopList = json['shopList'];
cloudStorage = json['cloud_storage'];
valueAddServiceLimitFree = json['value_add_service_limit_free'];
}
Map<String, dynamic> toJson() {
@ -55,6 +61,8 @@ class Data {
data['vip_buy_url'] = vipBuyUrl;
data['cloudauth_buy_url'] = cloudauthBuyUrl;
data['shopList'] = shopList;
data['cloud_storage'] = cloudStorage;
data['value_add_service_limit_free'] = valueAddServiceLimitFree;
return data;
}
}

View File

@ -40,6 +40,9 @@ class AdvancedFeaturesWebLogic extends BaseGetXController {
} else if (state.webBuyType.value == XSConstantMacro.webBuyTypeShop) {
state.webBuyUrl.value = entity.data!.shopList!;
state.webBuyTitle.value = '商城购买'.tr;
}else if (state.webBuyType.value == XSConstantMacro.webBuyTypeCloudStorage) {
state.webBuyUrl.value = entity.data!.cloudStorage!;
state.webBuyTitle.value = '云存购买'.tr;
}
state.webBuyView.setNavigationDelegate(

View File

@ -19,6 +19,8 @@ class AdvancedFeaturesWebState {
webBuyTitle.value = '邮件购买'.tr;
} else if (webBuyType.value == XSConstantMacro.webBuyTypeShop) {
webBuyTitle.value = '商城购买'.tr;
}else if (webBuyType.value == XSConstantMacro.webBuyTypeCloudStorage) {
webBuyTitle.value = '云存购买'.tr;
}
}
}

View File

@ -0,0 +1,6 @@
import 'package:star_lock/mine/valueAddedServices/cloudStorage/cloud_storage_state.dart';
import 'package:star_lock/tools/baseGetXController.dart';
class CloudStorageLogic extends BaseGetXController {
CloudStorageState state = CloudStorageState();
}

View File

@ -0,0 +1,150 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:star_lock/app_settings/app_colors.dart';
import 'package:star_lock/mine/valueAddedServices/cloudStorage/cloud_storage_logic.dart';
import 'package:star_lock/mine/valueAddedServices/cloudStorage/cloud_storage_state.dart';
import 'package:star_lock/tools/titleAppBar.dart';
class CloudStoragePage extends StatefulWidget {
const CloudStoragePage();
@override
State<CloudStoragePage> createState() => _CloudStoragePageState();
}
class _CloudStoragePageState extends State<CloudStoragePage> {
final CloudStorageLogic logic = Get.put(CloudStorageLogic());
final CloudStorageState state = Get.find<CloudStorageLogic>().state;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: TitleAppBar(
barTitle: '云存储购买'.tr,
haveBack: true,
iconColor: Colors.black,
titleColor: Colors.black,
backgroundColor: Colors.white,
),
body: SafeArea(
child: _buildBody(),
),
);
}
Widget _buildBody() {
return Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.center,
colors: [Color(0xFFC5F0E7), Color(0xFFF7F7F7)],
),
),
child: Column(
children: [
//
_buildPurchasePackage()
],
),
);
}
Widget _buildPurchasePackage() {
return Container(
width: 1.sw,
margin: EdgeInsets.symmetric(
vertical: 12.h,
horizontal: 14.w,
),
decoration: BoxDecoration(
color: const Color(0xFFF7F7F7),
borderRadius: BorderRadiusDirectional.all(
Radius.circular(16.r),
),
),
child: Column(
children: [
_buildTabs(),
_buildTabContent(),
],
),
);
}
Widget _buildTabs() {
return Obx(
() => Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: List.generate(
state.tabs.length,
(int index) => _buildTabItem(index),
),
),
);
}
Widget _buildTabItem(int index) {
return Expanded(
child: GestureDetector(
onTap: () {
state.selectedIndex.value = index;
},
child: Container(
height: 68.h,
decoration: BoxDecoration(
color: _isSelectTabBgColor(index),
borderRadius: BorderRadiusDirectional.only(
topStart: Radius.circular(16.r),
topEnd: Radius.circular(16.r),
),
),
child: Center(
child: Text(
state.tabs[index],
style: _isSelectTabTitle(index),
),
),
),
),
);
}
TextStyle _isSelectTabTitle(int index) {
if (state.selectedIndex.value == index) {
return TextStyle(
color: const Color(0xFF040404),
fontWeight: FontWeight.w600,
fontSize: 28.sp,
);
}
return TextStyle(
color: const Color(0xFF8F8F8F),
fontSize: 28.sp,
);
}
Color _isSelectTabBgColor(int index) {
if (state.selectedIndex.value == index) {
return Colors.white;
}
return const Color(0xFFF7F7F7);
}
Widget _buildTabContent() {
return Container(
width: 1.sw,
padding: EdgeInsets.symmetric(
vertical: 12.h,
horizontal: 14.w,
),
decoration: BoxDecoration(
color: Colors.white,
),
child: Text('asd'),
);
}
}

View File

@ -0,0 +1,13 @@
import 'package:get/get.dart';
class CloudStorageState {
//
RxInt selectedIndex = 0.obs;
//
final List<String> tabs = ['7天滚动存储', '30天滚动存储'];
final List<Map<String, dynamic>> tabContent = [
{'title': '连续包月', 'price': '1', 'price2': '188', 'discount': '立省188元'},
{'title': '连续包月', 'price': '1', 'price2': '188', 'discount': '立省188元'}
];
}

View File

@ -265,6 +265,8 @@ abstract class Api {
final String getServiceUserPackageURL =
'/v2/service/getUserPackage'; //
final String getValidityPeriodInfoURL = '/passthrough';
//
final String getlockCloudStorageListURL = '/lockCloudStorage/list'; //
final String deleteLockCloudStorageURL = '/lockCloudStorage/delete'; //
@ -298,4 +300,9 @@ abstract class Api {
'/SL-A-1.0/peer/nslookup'; // --
final String bindUserStarchartURL =
'/userStarchart/bindUserStarchart'; //
final String getPassthroughListURL = '/passthrough'; //
final String updateCurrentVoiceTimbre =
'/lockSetting/updateLockSetting'; //
final String reportBuyRequestURL =
'/service/reportBuyRequest'; //
}

View File

@ -2400,6 +2400,22 @@ class ApiProvider extends BaseProvider {
'searchStr': searchStr
}));
//
Future<Response> getValidityPeriodInfo(
int lockId, {
String request_method = 'POST',
String request_uri = '/api/v1/cloudStorage/getStorageServiceInfo',
required Map<String, dynamic> post_args,
}) =>
post(
getValidityPeriodInfoURL.toUrl,
jsonEncode({
'lockId': lockId,
'request_method': request_method,
'request_uri': request_uri,
'post_args': post_args,
}));
//
Future<Response> getLockCloudStorageList(int lockId) =>
post(getlockCloudStorageListURL.toUrl, jsonEncode({'lockId': lockId}));
@ -2773,6 +2789,55 @@ class ApiProvider extends BaseProvider {
isShowNetworkErrorMsg: false,
isShowErrMsg: false,
isUnShowLoading: true);
///
Future<Response> getPassthroughList(
String requestMethod,
String requestUri,
Map<String, String> data,
) =>
post(
getPassthroughListURL.toUrl,
jsonEncode({
'request_method': requestMethod,
'request_uri': requestUri,
'post_args': data,
}),
isShowNetworkErrorMsg: false,
isShowErrMsg: false,
isUnShowLoading: true);
///
Future<Response> settingCurrentVoiceTimbre(
int lockId,
Map<String, String> data,
) =>
post(
updateCurrentVoiceTimbre.toUrl,
jsonEncode({
'lockId': lockId,
'currentVoiceTimbre': data,
}),
isShowNetworkErrorMsg: false,
isShowErrMsg: false,
isUnShowLoading: true);
///
Future<Response> reportBuyRequest(
int lockId,
String type,
) =>
post(
reportBuyRequestURL.toUrl,
jsonEncode({
'lockId': lockId,
'type': type,
}),
isShowNetworkErrorMsg: false,
isShowErrMsg: false,
isUnShowLoading: true);
}
extension ExtensionString on String {

View File

@ -13,6 +13,7 @@ import 'package:star_lock/main/lockDetail/electronicKey/sendEmailNotification/se
import 'package:star_lock/main/lockDetail/face/addFace/addFace_entity.dart';
import 'package:star_lock/main/lockDetail/fingerprint/fingerprintList/fingerprint_entity.dart';
import 'package:star_lock/main/lockDetail/lockDetail/device_network_info.dart';
import 'package:star_lock/main/lockDetail/lockDetail/passthrough_item.dart';
import 'package:star_lock/main/lockDetail/lockSet/basicInformation/basicInformation/KeyDetailEntity.dart';
import 'package:star_lock/main/lockDetail/lockSet/lockEscalation/updateLockInfo_entity.dart';
import 'package:star_lock/main/lockDetail/lockSet/lockEscalation/version_entity.dart';
@ -2435,6 +2436,30 @@ class ApiRepository {
return CoerceFingerprintListEntity.fromJson(res.body);
}
//
Future<ValidityPeriodResponse> getValidityPeriodInfo({
required int lockId,
}) async {
Map<String, dynamic> post_args = Map.of({'lockId': lockId});
final res = await apiProvider.getValidityPeriodInfo(
lockId,
post_args: post_args,
);
return ValidityPeriodResponse.fromJson(res.body);
}
//
Future<LoginEntity> uploadReportBuyRequest({
required int lockId,
String type = 'cloud_storage',
}) async {
final res = await apiProvider.reportBuyRequest(
lockId,
type,
);
return LoginEntity.fromJson(res.body);
}
//
Future<VideoLogEntity> getLockCloudStorageList({required int lockId}) async {
final res = await apiProvider.getLockCloudStorageList(lockId);
@ -2770,4 +2795,30 @@ class ApiRepository {
);
return DeviceNetwork.fromJson(res.body);
}
//
Future<PassthroughListResponse> getPassthroughList({
String requestMethod = 'POST',
String requestUri = '/api/v1/voice/packs',
required Map<String, String> data,
}) async {
final res = await apiProvider.getPassthroughList(
requestMethod,
requestUri,
data,
);
return PassthroughListResponse.fromJson(res.body);
}
//
Future<LoginEntity> settingCurrentVoiceTimbre({
required int lockId,
required Map<String, String> data,
}) async {
final res = await apiProvider.settingCurrentVoiceTimbre(
lockId,
data,
);
return LoginEntity.fromJson(res.body);
}
}

View File

@ -277,7 +277,6 @@ class MessageCommand {
int? SpTotal,
int? SpIndex,
}) {
final payload = talkData.writeToBuffer();
ScpMessage message = ScpMessage(
ProtocolFlag: ProtocolFlagConstant.scp01,

View File

@ -630,6 +630,7 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
//
StartChartManage().reSetDefaultTalkExpect();
StartChartManage().stopTalkExpectMessageTimer();
VideoDecodePlugin.releaseDecoder();
//

View File

@ -40,6 +40,7 @@ const String lockNetWorkInfo = 'lockNetWorkInfo'; //锁板配网信息
const String appVersionHistoryUrl = 'appVersionHistoryUrl'; //
const String voipToken = 'voipToken'; //
const String deviceModel = 'deviceModel'; //
class Storage {
factory Storage() => _instance;