Merge branch 'master' of https://gitee.com/starlock-cn/app-starlock
This commit is contained in:
commit
55c65e4590
BIN
star_lock/images/main/realTime_connecting.png
Normal file
BIN
star_lock/images/main/realTime_connecting.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.5 KiB |
@ -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/notificationMode/notificationMode_page.dart';
|
||||||
import 'package:star_lock/main/lockDetail/lcokSet/openDoorDirection/openDoorDirection_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/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/main/lockDetail/passwordKey/passwordKeyDetailChangeDate/passwordKeyDetailChangeDate_page.dart';
|
||||||
import 'package:star_lock/mine/about/webviewShow_page.dart';
|
import 'package:star_lock/mine/about/webviewShow_page.dart';
|
||||||
import 'package:star_lock/mine/mine/safeVerify/safeVerify_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 addFaceTypeManagePage = '/AddFaceTypeManagePage'; // 添加人脸
|
||||||
static const passwordKeyDetailChangeDatePage =
|
static const passwordKeyDetailChangeDatePage =
|
||||||
'/passwordKeyDetailChangeDatePage'; //密码更改时间
|
'/passwordKeyDetailChangeDatePage'; //密码更改时间
|
||||||
|
static const realTimePicturePage = '/realTimePicturePage'; //实时监控画面
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class AppRouters {
|
abstract class AppRouters {
|
||||||
@ -953,12 +955,9 @@ abstract class AppRouters {
|
|||||||
GetPage(
|
GetPage(
|
||||||
name: Routers.monitoringRealTimeScreenPage,
|
name: Routers.monitoringRealTimeScreenPage,
|
||||||
page: () => const MonitoringRealTimeScreenPage()),
|
page: () => const MonitoringRealTimeScreenPage()),
|
||||||
|
GetPage(name: Routers.videoLogPage, page: () => const VideoLogPage()),
|
||||||
GetPage(
|
GetPage(
|
||||||
name: Routers.videoLogPage,
|
name: Routers.editVideoLogPage, page: () => const EditVideoLogPage()),
|
||||||
page: () => const VideoLogPage()),
|
|
||||||
GetPage(
|
|
||||||
name: Routers.editVideoLogPage,
|
|
||||||
page: () => const EditVideoLogPage()),
|
|
||||||
GetPage(
|
GetPage(
|
||||||
name: Routers.videoLogDetailPage,
|
name: Routers.videoLogDetailPage,
|
||||||
page: () => const VideoLogDetailPage()),
|
page: () => const VideoLogDetailPage()),
|
||||||
@ -971,15 +970,11 @@ abstract class AppRouters {
|
|||||||
GetPage(
|
GetPage(
|
||||||
name: Routers.addRemoteControlManagePage,
|
name: Routers.addRemoteControlManagePage,
|
||||||
page: () => const AddRemoteControlManagePage()),
|
page: () => const AddRemoteControlManagePage()),
|
||||||
GetPage(
|
GetPage(name: Routers.cardListPage, page: () => const CardListPage()),
|
||||||
name: Routers.cardListPage,
|
|
||||||
page: () => const CardListPage()),
|
|
||||||
GetPage(
|
GetPage(
|
||||||
name: Routers.addCardTypeManagePage,
|
name: Routers.addCardTypeManagePage,
|
||||||
page: () => const AddCardTypeManagePage()),
|
page: () => const AddCardTypeManagePage()),
|
||||||
GetPage(
|
GetPage(name: Routers.cardDetailPage, page: () => const CardDetailPage()),
|
||||||
name: Routers.cardDetailPage,
|
|
||||||
page: () => const CardDetailPage()),
|
|
||||||
GetPage(
|
GetPage(
|
||||||
name: Routers.fingerprintListPage,
|
name: Routers.fingerprintListPage,
|
||||||
page: () => const FingerprintListPage()),
|
page: () => const FingerprintListPage()),
|
||||||
@ -995,6 +990,9 @@ abstract class AppRouters {
|
|||||||
page: () => const AddFaceTypeManagePage()),
|
page: () => const AddFaceTypeManagePage()),
|
||||||
GetPage(
|
GetPage(
|
||||||
name: Routers.passwordKeyDetailChangeDatePage,
|
name: Routers.passwordKeyDetailChangeDatePage,
|
||||||
page: () => const PasswordKeyDetailChangeDatePage())
|
page: () => const PasswordKeyDetailChangeDatePage()),
|
||||||
|
GetPage(
|
||||||
|
name: Routers.realTimePicturePage,
|
||||||
|
page: () => const RealTimePicturePage())
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -374,9 +374,11 @@ class _LockDetailPageState extends State<LockDetailPage> with TickerProviderStat
|
|||||||
//可视对讲门锁新增->监控
|
//可视对讲门锁新增->监控
|
||||||
if (state.keyInfos.value.lockFeature!.videoIntercom == 1) {
|
if (state.keyInfos.value.lockFeature!.videoIntercom == 1) {
|
||||||
showWidgetArr.add(
|
showWidgetArr.add(
|
||||||
bottomItem('images/main/icon_catEyes.png', TranslationLoader.lanKeys!.monitoring!.tr, () {
|
bottomItem('images/main/icon_catEyes.png',
|
||||||
Get.toNamed(Routers.lockMonitoringPage, arguments: {
|
TranslationLoader.lanKeys!.monitoring!.tr, () {
|
||||||
"lockId": widget.lockListInfoItemEntity.lockId
|
Get.toNamed(Routers.realTimePicturePage, arguments: {
|
||||||
|
"lockId": widget.lockListInfoItemEntity.lockId,
|
||||||
|
"isMonitoring": true
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|||||||
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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;
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user