diff --git a/lib/blue/blue_manage.dart b/lib/blue/blue_manage.dart index 5ae2f0df..dba9581c 100755 --- a/lib/blue/blue_manage.dart +++ b/lib/blue/blue_manage.dart @@ -144,12 +144,11 @@ class BlueManage { if (isAvailable) { if (_adapterState == BluetoothAdapterState.on) { try { - //android 扫描比较慢,取样只要 8 分之一 + //android 扫描比较慢,取样只要 3 分之一 final int divisor = Platform.isAndroid ? 3 : 1; FlutterBluePlus.startScan( continuousDivisor: divisor, continuousUpdates: true, - // withServiceData:[ServiceDataFilter()], withKeywords: [deviceName], timeout: Duration(seconds: timeout)); final Completer completer = Completer(); @@ -317,7 +316,7 @@ class BlueManage { if (isAddEquipment == false && isExistDevice == false) { //取消缓存直接使用,存在配对场景设备信息会更变 - startScanSingle(deviceName, 10, (List scanDevices) { + startScanSingle(deviceName, 15, (List scanDevices) { _connectDevice(scanDevices, deviceName, connectStateCallBack, isAddEquipment: isAddEquipment); }); diff --git a/lib/main/lockDetail/lockDetail/lockDetail_logic.dart b/lib/main/lockDetail/lockDetail/lockDetail_logic.dart index 50a4fa5b..c079f792 100755 --- a/lib/main/lockDetail/lockDetail/lockDetail_logic.dart +++ b/lib/main/lockDetail/lockDetail/lockDetail_logic.dart @@ -8,6 +8,7 @@ import 'package:permission_handler/permission_handler.dart'; import 'package:star_lock/common/XSConstantMacro/XSConstantMacro.dart'; import 'package:star_lock/main/lockDetail/electronicKey/electronicKeyList/entity/ElectronicKeyListEntity.dart'; import 'package:star_lock/main/lockDetail/lockSet/lockTime/getServerDatetime_entity.dart'; +import 'package:star_lock/tools/throttler.dart'; import 'package:star_lock/widget/permission/permission_dialog.dart'; @@ -33,6 +34,10 @@ import 'lockNetToken_entity.dart'; class LockDetailLogic extends BaseGetXController { final LockDetailState state = LockDetailState(); + //节流器,用来限制开锁按钮的触发频率 + FunctionBlocker functionBlocker = + FunctionBlocker(duration: const Duration(seconds: 2)); + // 监听设备返回的数据 void initReplySubscription() { state.replySubscription = @@ -114,10 +119,19 @@ class LockDetailLogic extends BaseGetXController { break; case 0x16: // 正在开锁中... + final int isOpen = reply.data[8]; + String? msg; + if (isOpen == 0) { + msg = '正在开锁中...'.tr; + } else if (isOpen == 32) { + msg = '正在闭锁中...'.tr; + } resetOpenDoorState(); - showToast('正在开锁中...'.tr, something: () { - cancelBlueConnetctToastTimer(); - }); + if (msg != null) { + showToast(msg, something: () { + cancelBlueConnetctToastTimer(); + }); + } break; case 0x0d: // 钥匙无效 @@ -161,14 +175,13 @@ class LockDetailLogic extends BaseGetXController { void openDoorError() { resetOpenDoorState(); state.animationController!.stop(); - cancelBlueConnetctToastTimer(); - BlueManage().disconnect(); + blueManageDisconnect(); } //清除开锁动画以及状态 void closeLuckStatus() { state.openLockBtnState.value = 0; - state.openDoorBtnisUneable.value = false; + state.openDoorBtnisUneable.value = true; state.animationController!.stop(canceled: true); cancelBlueConnetctToastTimer(); } @@ -225,29 +238,28 @@ class LockDetailLogic extends BaseGetXController { if (dataLength == state.logCountPage) { senderReferEventRecordTime(); } else { - await BlueManage().disconnect(); + await blueManageDisconnect(); } } break; case 0x06: //无权限 - BlueManage().disconnect(); - cancelBlueConnetctToastTimer(); + blueManageDisconnect(); break; default: - BlueManage().disconnect(); - cancelBlueConnetctToastTimer(); + blueManageDisconnect(); break; } } // 点击开门事件 Future openDoorAction() async { - showBlueConnetctToastTimer(action: () { - resetOpenDoorState(); - closeLuckStatus(); - BlueManage().disconnect(); - }); + showBlueConnetctToastTimer( + outTimer: 20, + action: () { + resetOpenDoorState(); + blueManageDisconnect(); + }); final List? privateKey = await Storage.getStringList(saveBluePrivateKey); final List getPrivateKeyList = changeStringListToIntList(privateKey!); @@ -285,13 +297,20 @@ class LockDetailLogic extends BaseGetXController { }); } + //蓝牙关闭 + Future blueManageDisconnect() async { + //顺便清除倒计时 + closeLuckStatus(); + cancelBlueConnetctToastTimer(); + await BlueManage().disconnect(); + } + // 查询事件记录(时间查询) void senderReferEventRecordTime() { showBlueConnetctToastTimer( isShowBlueConnetctToast: false, action: () { - closeLuckStatus(); - BlueManage().disconnect(); + blueManageDisconnect(); }); BlueManage().blueSendData(BlueManage().connectDeviceName, (BluetoothConnectionState connectionState) async { @@ -325,7 +344,7 @@ class LockDetailLogic extends BaseGetXController { } // 从服务器获取锁的时间 开锁时传入 - void getServerDatetime() async { + Future getServerDatetime() async { final GetServerDatetimeEntity entity = await ApiRepository.to.getServerDatetimeData(); if (entity.errorCode!.codeIsSuccessful) { @@ -341,7 +360,7 @@ class LockDetailLogic extends BaseGetXController { } // 获取手机联网token,根据锁设置里面获取的开锁时是否联网来判断是否调用这个接口 - void getLockNetToken() async { + Future getLockNetToken() async { final LockNetTokenEntity entity = await ApiRepository.to .getLockNetToken(lockId: state.keyInfos.value.lockId.toString()); if (entity.errorCode!.codeIsSuccessful) { @@ -375,7 +394,7 @@ class LockDetailLogic extends BaseGetXController { } // 查询锁记录最后时间 - void getLockRecordLastUploadDataTime() async { + Future getLockRecordLastUploadDataTime() async { final LockOperatingRecordGetLastRecordTimeEntity entity = await ApiRepository.to.getLockRecordLastUploadDataTime( lockId: state.keyInfos.value.lockId.toString()); @@ -402,7 +421,7 @@ class LockDetailLogic extends BaseGetXController { } // 普通用户或者授权管理员删除钥匙 - void deletKeyData() async { + Future deletKeyData() async { final ElectronicKeyListEntity entity = await ApiRepository.to .deleteElectronicKey( keyId: state.keyInfos.value.keyId.toString(), includeUnderlings: 0); @@ -497,13 +516,6 @@ class LockDetailLogic extends BaseGetXController { await PermissionDialog.request(Permission.location); await PermissionDialog.requestBluetooth(); - - final String connectDeviceName = - state.keyInfos.value.bluetooth!.bluetoothDeviceName!; - if (!BlueManage().isExistScanDevices(connectDeviceName)) { - BlueManage().startScanSingle( - connectDeviceName, 15, (List p0) => null); - } } @override diff --git a/lib/main/lockDetail/lockDetail/lockDetail_page.dart b/lib/main/lockDetail/lockDetail/lockDetail_page.dart index ebbfa934..f32d72e7 100755 --- a/lib/main/lockDetail/lockDetail/lockDetail_page.dart +++ b/lib/main/lockDetail/lockDetail/lockDetail_page.dart @@ -254,14 +254,20 @@ class _LockDetailPageState extends State children: [ const Spacer(), GestureDetector( - onTap: state.openDoorBtnisUneable.value == true - ? isNeedRealNameAuthThenOpenLock - : null, - onLongPressStart: state.openDoorBtnisUneable.value == true - ? (LongPressStartDetails details) { - setState(startUnLock); - } - : null, + onTap: () { + if (state.openDoorBtnisUneable.value == true) { + logic.functionBlocker.block(isNeedRealNameAuthThenOpenLock); + } + }, + onLongPressStart: (LongPressStartDetails details) { + if (state.openDoorBtnisUneable.value == true) { + void callback() { + setState(startUnLock); + } + + logic.functionBlocker.block(callback); + } + }, child: Container( width: 100.r, height: 100.r, @@ -583,14 +589,20 @@ class _LockDetailPageState extends State children: [ Center( child: GestureDetector( - onTap: state.openDoorBtnisUneable.value == true - ? isNeedRealNameAuthThenOpenLock - : null, - onLongPressStart: state.openDoorBtnisUneable.value == true - ? (LongPressStartDetails details) { - setState(startUnLock); - } - : null, + onTap: () { + if (state.openDoorBtnisUneable.value == true) { + logic.functionBlocker.block(isNeedRealNameAuthThenOpenLock); + } + }, + onLongPressStart: (LongPressStartDetails details) { + if (state.openDoorBtnisUneable.value == true) { + void callback() { + setState(startUnLock); + } + + logic.functionBlocker.block(callback); + } + }, child: Stack( children: [ FlavorsImg( @@ -1347,7 +1359,7 @@ class _LockDetailPageState extends State state.iSOpenLock.value = false; state.openLockBtnState.value = 1; state.animationController!.forward(); - EasyLoading.showToast('正在尝试闭锁……'.tr, duration: 2000.milliseconds); + EasyLoading.showToast('正在尝试闭锁……'.tr, duration: 1000.milliseconds); AppLog.log('长按闭锁'); if (state.isOpenLockNeedOnline.value == 0) { // 不需要联网 diff --git a/lib/main/lockDetail/lockSet/lockEscalation/lockEscalation_logic.dart b/lib/main/lockDetail/lockSet/lockEscalation/lockEscalation_logic.dart index db0570b1..e1850e7a 100755 --- a/lib/main/lockDetail/lockSet/lockEscalation/lockEscalation_logic.dart +++ b/lib/main/lockDetail/lockSet/lockEscalation/lockEscalation_logic.dart @@ -48,16 +48,16 @@ class LockEscalationLogic extends BaseGetXController { //手动升级 Future otaUpdate() async { - var status = await PermissionDialog.requestStorage(); + final bool status = await PermissionDialog.requestStorage(); if (status != true) { return; } - FilePickerResult? result = await FilePicker.platform.pickFiles(); + final FilePickerResult? result = await FilePicker.platform.pickFiles(); if (result == null || result.files.single.path is! String) { return; } - File file = File(result.files.single.path!); - Uint8List data = await file.readAsBytes(); + final File file = File(result.files.single.path!); + final Uint8List data = await file.readAsBytes(); headJson = await getHeadFile(data); if (headJson is! Map) { return; @@ -66,15 +66,13 @@ class LockEscalationLogic extends BaseGetXController { if (otaBin == null) { return; } - String md5Str = md5.convert(otaBin!).toString(); + final String md5Str = md5.convert(otaBin!).toString(); headJson!['fwMd5'] = md5Str; - ShowTipView().showIosTipWithContentDialog("未避免异常情况,请在门打开时升级".tr, () async { + ShowTipView().showIosTipWithContentDialog('未避免异常情况,请在门打开时升级'.tr, () async { blueOTAUpgrade(headJson!, [0, 0, 0, 0]); EasyLoading.show( status: '设备连接中...'.tr, maskType: EasyLoadingMaskType.black); - Future.delayed(const Duration(seconds: 4), () { - EasyLoading.dismiss(); - }); + Future.delayed(const Duration(seconds: 4), EasyLoading.dismiss); }); } @@ -83,11 +81,11 @@ class LockEscalationLogic extends BaseGetXController { BlueManage().blueSendData(BlueManage().connectDeviceName, (BluetoothConnectionState deviceConnectionState) async { if (deviceConnectionState == BluetoothConnectionState.connected) { - var privateKey = await Storage.getStringList(saveBluePrivateKey); - List getPrivateKeyList = changeStringListToIntList(privateKey!); - var signKey = await Storage.getStringList(saveBlueSignKey); - List signKeyDataList = changeStringListToIntList(signKey!); - String uid = await Storage.getUid() ?? ''; + final privateKey = await Storage.getStringList(saveBluePrivateKey); + final List getPrivateKeyList = changeStringListToIntList(privateKey!); + final signKey = await Storage.getStringList(saveBlueSignKey); + final List signKeyDataList = changeStringListToIntList(signKey!); + final String uid = await Storage.getUid() ?? ''; BlueManage().writeCharacteristicWithResponse(OTAUpgradeCommand( lockID: BlueManage().connectDeviceName, userID: uid, @@ -113,30 +111,30 @@ class LockEscalationLogic extends BaseGetXController { if (!state.otaUpdateIng.value) { return; } - int length = otaBin?.length ?? 0; + final int length = otaBin?.length ?? 0; if (otaCount == 0) { //首次 - int difference = length % 240; + final int difference = length % 240; otaCount = length ~/ 240 + (difference > 0 ? 1 : 0); startSecond = DateTime.now().millisecondsSinceEpoch ~/ 1000; startOTAData(); } if (otaCount <= otaIndex) { - int now = DateTime.now().millisecondsSinceEpoch ~/ 1000; - String msg = + final int now = DateTime.now().millisecondsSinceEpoch ~/ 1000; + final String msg = '传输完成 时间:${now - startSecond}秒 otaCount:$otaCount otaIndex:$otaIndex '; closeOTADAta(); AppLog.log(msg); // showToast(msg); return; } - int star = otaIndex * 240; + final int star = otaIndex * 240; int end = (otaIndex + 1) * 240; if (end > length) { end = length; } - int size = end - star; - List data = otaBin!.sublist(star, end); + final int size = end - star; + final List data = otaBin!.sublist(star, end); state.otaProgress.value = otaIndex / otaCount; await BlueManage().writeCharacteristicWithResponse( ProcessOtaUpgradeCommand(index: otaIndex, size: size, data: data) @@ -176,7 +174,8 @@ class LockEscalationLogic extends BaseGetXController { // 检查文件头 String header; try { - header = utf8.decode(data.sublist(0, 12)); + final Uint8List list = data.sublist(0, 12); + header = utf8.decode(list); } catch (e) { showToast('非SYD固件,请选择正确的文件'.tr); return null; @@ -210,7 +209,7 @@ class LockEscalationLogic extends BaseGetXController { return null; } AppLog.log(metaStr); - var meta = jsonDecode(metaStr); + final meta = jsonDecode(metaStr); if (meta is! Map) { showToast('解析元数据失败,请选择正确的文件'.tr); return null; @@ -220,11 +219,11 @@ class LockEscalationLogic extends BaseGetXController { //检测升级文件并读取 bin Future checkFile(Uint8List data, Map meta) async { - num binOffset = 16 + (meta['metaLen'] ?? 0); + final num binOffset = 16 + (meta['metaLen'] ?? 0); // 获取固件数据部分 - Uint8List bin = data.sublist(binOffset.toInt(), data.length); + final Uint8List bin = data.sublist(binOffset.toInt(), data.length); //md5 校验有问题,暂时不解析 - String md5Str = md5.convert(bin).toString().toUpperCase(); + final String md5Str = md5.convert(bin).toString().toUpperCase(); AppLog.log('固件 md5 检验md5:$md5Str 固件信息 md5:${meta['fwMd5']}'); if (md5Str != meta['fwMd5']) { showToast('文件校验失败 0x02'.tr); diff --git a/lib/tools/throttler.dart b/lib/tools/throttler.dart new file mode 100644 index 00000000..8cdf1304 --- /dev/null +++ b/lib/tools/throttler.dart @@ -0,0 +1,73 @@ +import 'dart:async'; + +import 'package:star_lock/app_settings/app_settings.dart'; + +/// +/// 节流器 可以防止一个函数在短时间内被重复调用 +// 创建一个1秒的节流器 +// Throttler throttler = Throttler(Duration(seconds: 1)); +// +// // 示例函数 +// void myFunction() { +// print("函数被调用"); +// } +// +// // 调用示例函数,会在1秒内只输出一次 +// throttler.throttle(myFunction); +// +// // 在自定义时间间隔内防止函数被重复调用 +// Throttler customThrottler = Throttler(Duration(milliseconds: 500)); // 自定义500毫秒的间隔 +// customThrottler.throttle(myFunction); // 在500毫秒内多次调用只会执行一次函数 +// +// // 在需要时取消节流器 +// customThrottler.cancel(); // 取消节流器,函数再次被调用会立即执行 + +class Throttler { + Throttler(this._delay); + + final Duration _delay; + Timer? _timer; + late Function _callback; + + void throttle(Function callback) { + if (_timer == null || !_timer!.isActive) { + _callback = callback; + _timer = Timer(_delay, () { + _callback(); + _timer?.cancel(); + }); + } + } + + void cancel() { + _timer?.cancel(); + } +} + +/// +/// 防止抖动 +/// +class FunctionBlocker { + FunctionBlocker({required this.duration}); + + bool _blocked = false; + Duration duration; + + void block(Function function) { + if (!_blocked) { + _blocked = true; + function(); + Timer(duration, () { + _blocked = false; + }); + } + } + + //倒计时禁止 + void countdownProhibited({required Duration duration}) { + _blocked = true; + Timer(duration, () { + _blocked = false; + }); + } +}