新增监控页面及连接中动画

This commit is contained in:
Daisy 2024-01-03 15:24:42 +08:00
parent ef5a7c63fb
commit 85484ff4ff
6 changed files with 719 additions and 15 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

View File

@ -15,6 +15,7 @@ import 'package:star_lock/main/lockDetail/lcokSet/msgNotification/msgNotificatio
import 'package:star_lock/main/lockDetail/lcokSet/notificationMode/notificationMode_page.dart';
import 'package:star_lock/main/lockDetail/lcokSet/openDoorDirection/openDoorDirection_page.dart';
import 'package:star_lock/main/lockDetail/lockDetail/lockDetail_main_page.dart';
import 'package:star_lock/main/lockDetail/lockDetail/realTimePicture/realTimePicture_page.dart';
import 'package:star_lock/main/lockDetail/passwordKey/passwordKeyDetailChangeDate/passwordKeyDetailChangeDate_page.dart';
import 'package:star_lock/mine/about/webviewShow_page.dart';
import 'package:star_lock/mine/mine/safeVerify/safeVerify_page.dart';
@ -406,6 +407,7 @@ abstract class Routers {
static const addFaceTypeManagePage = '/AddFaceTypeManagePage'; //
static const passwordKeyDetailChangeDatePage =
'/passwordKeyDetailChangeDatePage'; //
static const realTimePicturePage = '/realTimePicturePage'; //
}
abstract class AppRouters {
@ -953,12 +955,9 @@ abstract class AppRouters {
GetPage(
name: Routers.monitoringRealTimeScreenPage,
page: () => const MonitoringRealTimeScreenPage()),
GetPage(name: Routers.videoLogPage, page: () => const VideoLogPage()),
GetPage(
name: Routers.videoLogPage,
page: () => const VideoLogPage()),
GetPage(
name: Routers.editVideoLogPage,
page: () => const EditVideoLogPage()),
name: Routers.editVideoLogPage, page: () => const EditVideoLogPage()),
GetPage(
name: Routers.videoLogDetailPage,
page: () => const VideoLogDetailPage()),
@ -971,15 +970,11 @@ abstract class AppRouters {
GetPage(
name: Routers.addRemoteControlManagePage,
page: () => const AddRemoteControlManagePage()),
GetPage(
name: Routers.cardListPage,
page: () => const CardListPage()),
GetPage(name: Routers.cardListPage, page: () => const CardListPage()),
GetPage(
name: Routers.addCardTypeManagePage,
page: () => const AddCardTypeManagePage()),
GetPage(
name: Routers.cardDetailPage,
page: () => const CardDetailPage()),
GetPage(name: Routers.cardDetailPage, page: () => const CardDetailPage()),
GetPage(
name: Routers.fingerprintListPage,
page: () => const FingerprintListPage()),
@ -995,6 +990,9 @@ abstract class AppRouters {
page: () => const AddFaceTypeManagePage()),
GetPage(
name: Routers.passwordKeyDetailChangeDatePage,
page: () => const PasswordKeyDetailChangeDatePage())
page: () => const PasswordKeyDetailChangeDatePage()),
GetPage(
name: Routers.realTimePicturePage,
page: () => const RealTimePicturePage())
];
}

View File

@ -373,9 +373,11 @@ class _LockDetailPageState extends State<LockDetailPage> with TickerProviderStat
//->
if (state.keyInfos.value.lockFeature!.videoIntercom == 1) {
showWidgetArr.add(
bottomItem('images/main/icon_catEyes.png', TranslationLoader.lanKeys!.monitoring!.tr, () {
Get.toNamed(Routers.lockMonitoringPage, arguments: {
"lockId": widget.lockListInfoItemEntity.lockId
bottomItem('images/main/icon_catEyes.png',
TranslationLoader.lanKeys!.monitoring!.tr, () {
Get.toNamed(Routers.realTimePicturePage, arguments: {
"lockId": widget.lockListInfoItemEntity.lockId,
"isMonitoring": true
});
}),
);

View File

@ -0,0 +1,368 @@
import 'dart:async';
import 'dart:math';
import 'package:flutter/services.dart';
import 'package:flutter_voice_processor/flutter_voice_processor.dart';
import 'package:get/get.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:star_lock/main/lockDetail/lockDetail/realTimePicture/realTimePicture_state.dart';
import '../../../../talk/call/g711.dart';
import '../../../../talk/udp/udp_manage.dart';
import '../../../../talk/udp/udp_senderManage.dart';
import '../../../../tools/baseGetXController.dart';
import '../../../../tools/eventBusEventManage.dart';
class RealTimePictureLogic extends BaseGetXController {
final RealTimePictureState state = RealTimePictureState();
///
initRecorder() {
state.voiceProcessor = VoiceProcessor.instance;
}
///
StreamSubscription? _getTVDataRefreshUIEvent;
void _getTVDataRefreshUIAction() {
// eventBus
_getTVDataRefreshUIEvent =
eventBus.on<GetTVDataRefreshUI>().listen((event) {
if (event.tvList.isNotEmpty) {
//
Uint8List imageData = Uint8List.fromList(event.tvList);
//
state.listData.value = imageData;
}
});
}
///
udpAnswerAction() async {
UDPSenderManage.sendMainProtocol(
command: 150,
commandTypeIsCalling: 1,
subCommand: 6,
lockID: UDPManage().lockId,
lockIP: UDPManage().host,
userMobile: await state.userMobile,
userMobileIP: await state.userMobileIP,
endData: []);
}
///
udpHangUpAction() async {
UDPSenderManage.sendMainProtocol(
command: 150,
commandTypeIsCalling: 1,
subCommand: 30,
lockID: UDPManage().lockId,
lockIP: UDPManage().host,
userMobile: await state.userMobile,
userMobileIP: await state.userMobileIP,
endData: []);
}
///
udpOpenDoorAction() async {
UDPSenderManage.sendMainProtocol(
command: 150,
commandTypeIsCalling: 1,
subCommand: 10,
lockID: UDPManage().lockId,
lockIP: UDPManage().host,
userMobile: await state.userMobile,
userMobileIP: await state.userMobileIP,
endData: []);
Get.back();
}
Future<void> _readG711Data() async {
String filePath = 'assets/s10-g711.bin';
List<int> audioData = await G711().readAssetFile(filePath);
// Get.log('发送读取711文件数据为:$audioData');// :$audioData
// return;
// print('发送读取711文件数据长度为:${audioData.length}');// :$audioData
if (audioData.isNotEmpty) {
//
// pcmBytes = G711().convertList(audioData);
// print('发送转换pcmBytes数据长度为:${pcmBytes.length}');
int start = 0;
int length = 320;
while (start < audioData.length) {
// await Future.delayed(const Duration(milliseconds: 50));
int end = (start + length > audioData.length)
? audioData.length
: start + length;
List<int> sublist = audioData.sublist(start, end);
sendRecordData({
"bytes": sublist,
// "udpSendDataFrameNumber": 0,
"lockID": UDPManage().lockId,
"lockIP": UDPManage().host,
"userMobile": await state.userMobile,
"userMobileIP": await state.userMobileIP,
});
print(sublist);
start += length;
}
print('G711数据发送完成');
} else {
print('Failed to read audio data.');
}
}
Future<void> startProcessing() async {
frameListener(List<int> frame) async {
// Get.log('Get data.length:${frame.length} Received data:$frame');
for (int i = 0; i < frame.length; i++) {
frame[i] = linearToULaw(frame[i]);
}
// Get.log('change Get data.length:${frame.length} change Received data:$frame');
await Future.delayed(const Duration(milliseconds: 50));
sendRecordData({
"bytes": frame,
// "udpSendDataFrameNumber": 0,
"lockID": UDPManage().lockId,
"lockIP": UDPManage().host,
"userMobile": await state.userMobile,
"userMobileIP": await state.userMobileIP,
});
}
errorListener(VoiceProcessorException error) {
print("VoiceProcessorException: $error");
}
;
state.voiceProcessor?.addFrameListener(frameListener);
state.voiceProcessor?.addErrorListener(errorListener);
try {
if (await state.voiceProcessor?.hasRecordAudioPermission() ?? false) {
await state.voiceProcessor?.start(320, 8000);
bool? isRecording = await state.voiceProcessor?.isRecording();
} else {}
} on PlatformException catch (ex) {
Get.log("PlatformException: $ex");
} finally {}
}
Future<void> stopProcessing() async {
try {
await state.voiceProcessor?.stop();
} on PlatformException catch (ex) {
Get.log("PlatformException: $ex");
} finally {}
}
void onError(Object e) {
print(e);
}
sendRecordData(Map<String, dynamic> args) async {
List<int> bytes = args["bytes"];
// int udpSendDataFrameNumber = args["udpSendDataFrameNumber"];
String? lockID = args["lockID"];
String? lockIP = args["lockIP"];
String? userMobile = args["userMobile"];
String? userMobileIP = args["userMobileIP"];
// int length = 320; // List的长度
// List<int> list = state.listAudioData.value.sublist(0, 320);
// for (int i = 0; i < bytes.length; i += length) {
// int end = (i + length < bytes.length) ? i + length : bytes.length;
// bytes.sublist(i, end);
// // _sendRecordData(bytes.sublist(i, end));
// // //
// }
// while(list.isNotEmpty) {
state.udpSendDataFrameNumber++;
if (state.udpSendDataFrameNumber >= 65536) state.udpSendDataFrameNumber = 1;
// 57
List<int> topBytes = [];
// var cID = "XXXCID";
// List<int> cIDData = utf8.encode(cID!);
// topBytes.addAll(cIDData);
// // topBytes = getFixedLengthList(cIDData, 20 - cIDData.length);
// for (int i = 0; i < 6 - cIDData.length; i++) {
// topBytes.add(0);
// }
//
// //
// topBytes.add(150);
//
// //
// topBytes.add(1);
//
// //
// topBytes.add(8);
//
// // lockID
// List<int> lockIDData = utf8.encode(lockID!);
// topBytes.addAll(lockIDData);
// // topBytes = getFixedLengthList(lockIDData, 20 - lockIDData.length);
// for (int i = 0; i < 20 - lockIDData.length; i++) {
// topBytes.add(0);
// }
//
// // lockIP
// var lockIPList = lockIP!.split(".");
// lockIPList.forEach((element) {
// topBytes.add(int.parse(element));
// });
//
// // userMobile
// List<int> userMobileData = utf8.encode(userMobile!);
// topBytes.addAll(userMobileData);
// // topBytes = getFixedLengthList(topBytes, 20 - userMobileData.length);
// for (int i = 0; i < 20 - userMobileData.length; i++) {
// topBytes.add(0);
// }
//
// // userMobileIP
// var userMobileIPList = userMobileIP!.split(".");
// userMobileIPList.forEach((element) {
// topBytes.add(int.parse(element));
// });
topBytes.addAll([
1, 1, 1, 1, //
1, 0, //
1, 0, //
64, 0, 0, 0, //
1, 0, //
1, 0, //
64, 1, //
176, 4, //
]);
topBytes[6] = (state.udpSendDataFrameNumber & 0x000000FF);
topBytes[7] = ((state.udpSendDataFrameNumber & 0x0000FF00) >> 8);
print(
"udpSendDataFrameNumber:${state.udpSendDataFrameNumber} topBytes[63]:${topBytes[6]} topBytes[64]:${topBytes[7]}");
topBytes.addAll(bytes);
Get.log("setVoiceBytes:$topBytes");
UDPSenderManage.sendMainProtocol(
command: 150,
commandTypeIsCalling: 1,
subCommand: 8,
lockID: lockID,
lockIP: lockIP,
userMobile: userMobile,
userMobileIP: userMobileIP,
endData: topBytes);
// UDPManage().sendData(topBytes);
}
// pcm
int linearToULaw(int pcmVal) {
int mask;
int seg;
int uval;
if (pcmVal < 0) {
pcmVal = 0x84 - pcmVal;
mask = 0x7F;
} else {
pcmVal += 0x84;
mask = 0xFF;
}
seg = search(pcmVal);
if (seg >= 8) {
return 0x7F ^ mask;
} else {
uval = (seg << 4);
uval |= ((pcmVal >> (seg + 3)) & 0xF);
return uval ^ mask;
}
}
int search(int val) {
List<int> table = [
0xFF,
0x1FF,
0x3FF,
0x7FF,
0xFFF,
0x1FFF,
0x3FFF,
0x7FFF
];
int size = 8;
for (int i = 0; i < size; i++) {
if (val <= table[i]) {
return i;
}
}
return size;
}
double _calculateVolumeLevel(List<int> frame) {
double rms = 0.0;
for (int sample in frame) {
rms += pow(sample, 2);
}
rms = sqrt(rms / frame.length);
double dbfs = 20 * log(rms / 32767.0) / log(10);
double normalizedValue = (dbfs + 50) / 50;
return normalizedValue.clamp(0.0, 1.0);
}
Future<bool> getPermissionStatus() async {
Permission permission = Permission.microphone;
//granted denied permanentlyDenied
PermissionStatus status = await permission.status;
if (status.isGranted) {
return true;
} else if (status.isDenied) {
requestPermission(permission);
} else if (status.isPermanentlyDenied) {
openAppSettings();
} else if (status.isRestricted) {
requestPermission(permission);
} else {}
return false;
}
///
void requestPermission(Permission permission) async {
PermissionStatus status = await permission.request();
if (status.isPermanentlyDenied) {
openAppSettings();
}
}
@override
void onReady() {
// TODO: implement onReady
super.onReady();
print("onReady()");
_getTVDataRefreshUIAction();
initRecorder();
}
@override
void onInit() {
// TODO: implement onInit
super.onInit();
}
@override
void onClose() {
// TODO: implement onClose
print("锁详情界面销毁了");
_getTVDataRefreshUIEvent!.cancel();
stopProcessing();
}
}

View File

@ -0,0 +1,301 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:star_lock/main/lockDetail/lockDetail/realTimePicture/realTimePicture_logic.dart';
import '../../../../app_settings/app_colors.dart';
import '../../../../tools/showTFView.dart';
import '../../../../tools/toast.dart';
class RealTimePicturePage extends StatefulWidget {
const RealTimePicturePage({Key? key}) : super(key: key);
@override
State<RealTimePicturePage> createState() => _RealTimePicturePageState();
}
class _RealTimePicturePageState extends State<RealTimePicturePage>
with TickerProviderStateMixin {
final logic = Get.put(RealTimePictureLogic());
final state = Get.find<RealTimePictureLogic>().state;
@override
void initState() {
super.initState();
// listeningAnimations();
state.animationController =
AnimationController(duration: const Duration(seconds: 2), vsync: this);
state.animationController.repeat();
//StatusListener
state.animationController.addStatusListener((status) {
// print("AnimationStatus:$status");
if (status == AnimationStatus.completed) {
state.animationController.reset();
state.animationController.forward();
} else if (status == AnimationStatus.dismissed) {
state.animationController.reset();
state.animationController.forward();
}
});
}
@override
Widget build(BuildContext context) {
return SizedBox(
width: 1.sw,
height: 1.sh,
child: Stack(
children: [
Obx(() => state.listData.value.isEmpty
? Container(color: Colors.black)
: Image.memory(
state.listData.value,
// key: ValueKey<int>(state.listData.value.hashCode),
gaplessPlayback: true,
width: 1.sw,
height: 1.sh,
fit: BoxFit.cover,
)),
Positioned(
bottom: 10.w,
child: Container(
width: 1.sw - 30.w * 2,
// height: 300.h,
margin: EdgeInsets.all(30.w),
decoration: BoxDecoration(
color: const Color(0xC83C3F41),
borderRadius: BorderRadius.circular(20.h)),
child: Column(
children: [
SizedBox(height: 20.h),
bottomTopBtnWidget(),
SizedBox(height: 20.h),
bottomBottomBtnWidget(),
SizedBox(height: 20.h),
],
),
)),
buildRotationTransition()
],
),
);
}
Widget bottomTopBtnWidget() {
return Row(mainAxisAlignment: MainAxisAlignment.center, children: [
//
GestureDetector(
onTap: () {
state.isOpenVoice.value = !state.isOpenVoice.value;
},
child: Container(
width: 50.w,
height: 50.w,
padding: EdgeInsets.all(5.w),
child: Obx(() => Image(
width: 40.w,
height: 40.w,
image: state.isOpenVoice.value
? const AssetImage(
"images/main/icon_lockDetail_monitoringCloseVoice.png")
: const AssetImage(
"images/main/icon_lockDetail_monitoringOpenVoice.png"))),
),
),
SizedBox(width: 60.w),
//
GestureDetector(
onTap: () {
// Get.toNamed(Routers.monitoringRealTimeScreenPage);
},
child: Container(
width: 50.w,
height: 50.w,
padding: EdgeInsets.all(5.w),
child: Image(
width: 40.w,
height: 40.w,
image: const AssetImage(
"images/main/icon_lockDetail_monitoringScreenshot.png")),
),
),
SizedBox(width: 60.w),
//
GestureDetector(
onTap: () {
// Get.toNamed(Routers.monitoringRealTimeScreenPage);
},
child: Container(
width: 50.w,
height: 50.w,
padding: EdgeInsets.all(5.w),
child: Image(
width: 40.w,
height: 40.w,
image: const AssetImage(
"images/main/icon_lockDetail_monitoringScreenRecording.png")),
),
),
]);
}
Widget bottomBottomBtnWidget() {
return Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [
//
Obx(() => bottomBtnItemWidget(
getAnswerBtnImg(), getAnswerBtnName(), Colors.white, () async {
//
await logic.getPermissionStatus().then((value) async {
if (!value) {
return;
}
// state.isSenderAudioData.value = false;
print("发送接听了");
//
logic.udpAnswerAction();
});
}, longPress: () {
//
print("onLongPress");
state.listAudioData.value = <int>[];
if (state.udpStatus.value == 8) {
state.udpStatus.value = 9;
}
// logic.readG711Data();
logic.startProcessing();
}, longPressUp: () async {
//
print("onLongPressUp");
if (state.udpStatus.value == 9) {
state.udpStatus.value = 8;
}
})),
bottomBtnItemWidget(
"images/main/icon_lockDetail_hangUp.png", "挂断", Colors.red, () async {
logic.stopProcessing();
//
logic.udpHangUpAction();
}),
bottomBtnItemWidget("images/main/icon_lockDetail_monitoringUnlock.png",
"开锁", AppColors.mainColor, () {
showDeletPasswordAlertDialog(context);
})
]);
}
String getAnswerBtnImg() {
switch (state.udpStatus.value) {
case 8:
return "images/main/icon_lockDetail_monitoringUnTalkback.png";
case 9:
return "images/main/icon_lockDetail_monitoringTalkback.png";
default:
return "images/main/icon_lockDetail_monitoringAnswerCalls.png";
}
}
String getAnswerBtnName() {
switch (state.udpStatus.value) {
case 8:
return "长按说话";
case 9:
return "松开发送";
default:
return "接听";
}
}
Widget bottomBtnItemWidget(
String iconUrl, String name, Color backgroundColor, Function() onClick,
{Function()? longPress, Function()? longPressUp}) {
var wh = 80.w;
return GestureDetector(
onTap: onClick,
onLongPress: longPress,
onLongPressUp: longPressUp,
child: SizedBox(
height: 140.h,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
width: wh,
height: wh,
decoration: BoxDecoration(
color: backgroundColor,
borderRadius: BorderRadius.circular((wh + 10.w * 2) / 2)),
padding: EdgeInsets.all(20.w),
child: Image.asset(iconUrl, fit: BoxFit.fitWidth),
),
SizedBox(height: 20.w),
Expanded(
child: Text(name,
style: TextStyle(fontSize: 20.sp, color: Colors.white),
textAlign: TextAlign.center))
],
)),
);
}
void showDeletPasswordAlertDialog(BuildContext context) {
showDialog(
barrierDismissible: false,
context: context,
builder: (BuildContext context) {
return ShowTFView(
title: "请输入六位数字开锁密码",
tipTitle: "",
controller: state.passwordTF,
inputFormatters: [
LengthLimitingTextInputFormatter(6), //
FilteringTextInputFormatter.allow(RegExp("[0-9]")),
],
sureClick: () async {
//
if (state.passwordTF.text.isEmpty) {
Toast.show(msg: "请输入开锁密码");
return;
}
//
logic.udpOpenDoorAction();
},
cancelClick: () {
Get.back();
},
);
},
);
}
//
Widget buildRotationTransition() {
return Positioned(
left: ScreenUtil().screenWidth / 2 - 220.w / 2,
top: ScreenUtil().screenHeight / 2 - 220.w / 2 - 150.h,
child: RotationTransition(
//
alignment: Alignment.center,
//
turns: state.animationController,
//view
child: Image.asset(
'images/main/realTime_connecting.png',
width: 220.w,
height: 220.w,
),
),
);
}
@override
void dispose() {
state.animationController.dispose();
super.dispose();
}
}

View File

@ -0,0 +1,35 @@
import 'dart:async';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter_voice_processor/flutter_voice_processor.dart';
import 'package:get/get.dart';
import 'package:network_info_plus/network_info_plus.dart';
import '../../../../tools/storage.dart';
class RealTimePictureState {
var isOpenVoice = false.obs;
var udpSendDataFrameNumber = 0; //
// var isSenderAudioData = false.obs;//
var userMobileIP = NetworkInfo().getWifiIP();
var userMobile = Storage.getMobile();
var udpStatus =
0.obs; //0 1 2 3 4 5 6 8 9
var passwordTF = TextEditingController();
var listData = Uint8List(0).obs; //
var listAudioData = <int>[].obs; //
late final VoiceProcessor? voiceProcessor;
var oneMinuteTime = 0.obs; //
// 10
late Timer answerTimer;
late Timer hangUpTimer;
late Timer openDoorTimer;
late AnimationController animationController;
}