import 'dart:async'; import 'dart:math'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; import 'package:http/http.dart' as http; import 'package:provider/provider.dart'; import 'package:star_lock/flavors.dart'; import 'package:star_lock/talk/call/callTalk.dart'; import 'package:star_lock/talk/starChart/constant/talk_status.dart'; import 'package:star_lock/talk/starChart/handle/impl/debug_Info_model.dart'; import 'package:star_lock/talk/starChart/handle/impl/udp_talk_data_handler.dart'; import 'package:star_lock/talk/starChart/views/talkView/talk_view_logic.dart'; import 'package:star_lock/talk/starChart/views/talkView/talk_view_state.dart'; import '../../../../app_settings/app_colors.dart'; import '../../../../tools/showTFView.dart'; class TalkViewPage extends StatefulWidget { const TalkViewPage({Key? key}) : super(key: key); @override State createState() => _TalkViewPageState(); } class _TalkViewPageState extends State with TickerProviderStateMixin { final TalkViewLogic logic = Get.put(TalkViewLogic()); final TalkViewState state = Get.find().state; late Stream _latencyStream; @override void initState() { super.initState(); state.listData.value = Uint8List(0); //写一个定时器,三十秒后页面自动返回 // state.autoBackTimer = Timer(const Duration(seconds: 30), Get.back); state.animationController = AnimationController( vsync: this, // 确保使用的TickerProvider是当前Widget duration: const Duration(seconds: 1), ); state.animationController.repeat(); //动画开始、结束、向前移动或向后移动时会调用StatusListener state.animationController.addStatusListener((AnimationStatus status) { if (status == AnimationStatus.completed) { state.animationController.reset(); state.animationController.forward(); } else if (status == AnimationStatus.dismissed) { state.animationController.reset(); state.animationController.forward(); } }); // _latencyStream = measureServerLatencyStream(F.apiPrefix); // 替换为你的服务器地址 } // // Stream measureServerLatencyStream(String url) async* { // while (true) { // final latency = await measureServerLatency(url); // yield latency; // await Future.delayed(Duration(seconds: 1)); // 每秒测量一次 // } // } // // Future measureServerLatency(String url) async { // final Stopwatch stopwatch = Stopwatch()..start(); // try { // final http.Response response = await http.get(Uri.parse(url)); // if (response.statusCode == 200) { // stopwatch.stop(); // return stopwatch.elapsedMilliseconds; // } else { // return -1; // 表示请求失败 // } // } catch (e) { // return -1; // 表示请求失败 // } // } @override Widget build(BuildContext context) { return WillPopScope( onWillPop: () async { // 返回 false 表示禁止退出 return false; }, child: SizedBox( width: 1.sw, height: 1.sh, child: Stack( alignment: Alignment.center, children: [ Obx( () { final double screenWidth = MediaQuery.of(context).size.width; final double screenHeight = MediaQuery.of(context).size.height; final double logicalWidth = MediaQuery.of(context).size.width; final double logicalHeight = MediaQuery.of(context).size.height; final double devicePixelRatio = MediaQuery.of(context).devicePixelRatio; // 计算物理像素值 final double physicalWidth = logicalWidth * devicePixelRatio; final double physicalHeight = logicalHeight * devicePixelRatio; // 旋转后的图片尺寸 const int rotatedImageWidth = 480; // 原始高度 const int rotatedImageHeight = 864; // 原始宽度 // 计算缩放比例 final double scaleWidth = physicalWidth / rotatedImageWidth; final double scaleHeight = physicalHeight / rotatedImageHeight; max(scaleWidth, scaleHeight); // 选择较大的缩放比例 return state.listData.value.isEmpty ? Image.asset( 'images/main/monitorBg.png', width: screenWidth, height: screenHeight, fit: BoxFit.cover, ) : PopScope( canPop: false, child: RepaintBoundary( key: state.globalKey, child: SizedBox.expand( child: RotatedBox( quarterTurns: -1, child: Obx( () => state.currentImage.value != null ? RawImage( image: state.currentImage.value, width: ScreenUtil().scaleWidth, height: ScreenUtil().scaleHeight, fit: BoxFit.cover, filterQuality: FilterQuality.high, ) : Container(color: Colors.transparent), ), ), ), ), ); }, ), Obx(() => state.listData.value.isEmpty ? Positioned( bottom: 310.h, child: Text( '正在创建安全连接...'.tr, style: TextStyle(color: Colors.black, fontSize: 26.sp), )) : Container()), Obx( () => state.listData.value.isNotEmpty && state.oneMinuteTime.value > 0 ? Positioned( top: ScreenUtil().statusBarHeight + 75.h, width: 1.sw, child: Obx( () { final String sec = (state.oneMinuteTime.value % 60) .toString() .padLeft(2, '0'); final String min = (state.oneMinuteTime.value ~/ 60) .toString() .padLeft(2, '0'); return Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( '$min:$sec', style: TextStyle( fontSize: 26.sp, color: Colors.white), ), ], ); }, ), ) : Container(), ), Positioned( bottom: 10.w, child: Container( width: 1.sw - 30.w * 2, // height: 300.h, margin: EdgeInsets.all(30.w), decoration: BoxDecoration( color: Colors.black.withOpacity(0.2), borderRadius: BorderRadius.circular(20.h)), child: Column( children: [ SizedBox(height: 20.h), bottomTopBtnWidget(), SizedBox(height: 20.h), bottomBottomBtnWidget(), SizedBox(height: 20.h), ], ), ), ), // Positioned( // top: 100.h, // left: 10.w, // child: Obx( // () => Text( // 'FPS:${state.fps.value}', // style: TextStyle( // fontSize: 30.sp, // color: Colors.orange, // fontWeight: FontWeight.bold), // ), // ), // ), Obx(() => state.listData.value.isEmpty ? buildRotationTransition() : Container()), Obx(() => state.isLongPressing.value ? Positioned( top: 80.h, left: 0, right: 0, child: Center( child: Container( padding: EdgeInsets.all(10.w), decoration: BoxDecoration( color: Colors.black.withOpacity(0.7), borderRadius: BorderRadius.circular(10.w), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.mic, color: Colors.white, size: 24.w), SizedBox(width: 10.w), Text( '正在说话...'.tr, style: TextStyle( fontSize: 20.sp, color: Colors.white), ), ], ), ), ), ) : Container()), //ToDo: 增加对讲调试、正式可删除 // Visibility( // visible: true, // child: Positioned( // top: 20, // 设置在顶部 // left: 20, // right: 20, // 确保调试信息在屏幕宽度内居中 // child: Container( // height: 100, // 设置高度为 100 // color: Colors.black.withOpacity(0.5), // 可选:设置背景颜色和透明度 // padding: const EdgeInsets.all(10), // 可选:设置内边距 // child: Consumer( // builder: (BuildContext context, DebugInfoModel debugInfo, // Widget? child) { // return Column( // crossAxisAlignment: CrossAxisAlignment.start, // children: [ // Text( // 'recv ${debugInfo.recvDataRate}KB/s [${debugInfo.recvPacketCount}]', // style: const TextStyle( // color: Colors.white), // 设置文本颜色为白色 // ), // Text( // 'send ${debugInfo.sendDataRate}KB/s [${debugInfo.sendPacketCount}]', // style: const TextStyle( // color: Colors.white), // 设置文本颜色为白色 // ), // ], // ); // }, // ), // ), // ), // ), // 添加服务器延迟检测 // Positioned( // top: 120, // left: 20, // right: 20, // child: Container( // height: 50, // color: Colors.black.withOpacity(0.5), // padding: const EdgeInsets.all(10), // child: StreamBuilder( // stream: _latencyStream, // builder: (BuildContext context, AsyncSnapshot snapshot) { // if (snapshot.connectionState == ConnectionState.waiting) { // return const Text( // '检测服务器延迟中...', // style: TextStyle(color: Colors.white), // ); // } else if (snapshot.hasError || // !snapshot.hasData || // snapshot.data == -1) { // return const Text( // '服务器延迟检测失败', // style: TextStyle(color: Colors.white), // ); // } else { // return Text( // '服务器延迟: ${snapshot.data} ms', // style: const TextStyle(color: Colors.white), // ); // } // }, // ), // ), // ), ], ), ), ); } Widget bottomTopBtnWidget() { return Row(mainAxisAlignment: MainAxisAlignment.center, children: [ // 打开关闭声音 GestureDetector( onTap: () { if (state.talkStatus.value == TalkStatus.answeredSuccessfully) { // 打开关闭声音 logic.updateTalkExpect(); } }, 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_monitoringOpenVoice.png') : const AssetImage( 'images/main/icon_lockDetail_monitoringCloseVoice.png'))), ), ), SizedBox(width: 50.w), // 截图 GestureDetector( onTap: () async { if (state.talkStatus.value == TalkStatus.answeredSuccessfully) { await logic.captureAndSavePng(); } }, 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: 50.w), // 录制 GestureDetector( onTap: () async { logic.showToast('功能暂未开放'.tr); // if ( // state.talkStatus.value == TalkStatus.answeredSuccessfully) { // if (state.isRecordingScreen.value) { // await logic.stopRecording(); // } else { // await logic.startRecording(); // } // } }, child: Container( width: 50.w, height: 50.w, padding: EdgeInsets.all(5.w), child: Image( width: 40.w, height: 40.w, fit: BoxFit.fill, image: const AssetImage( 'images/main/icon_lockDetail_monitoringScreenRecording.png'), ), ), ), SizedBox(width: 50.w), GestureDetector( onTap: () { logic.showToast('功能暂未开放'.tr); }, child: Image( width: 28.w, height: 28.w, fit: BoxFit.fill, image: const AssetImage('images/main/icon_lockDetail_rectangle.png'), ), ), ]); } Widget bottomBottomBtnWidget() { return Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ // 接听 Obx( () => bottomBtnItemWidget( getAnswerBtnImg(), getAnswerBtnName(), Colors.white, longPress: () async { if (state.talkStatus.value == TalkStatus.answeredSuccessfully) { // 启动录音 logic.startProcessingAudio(); state.isLongPressing.value = true; } }, longPressUp: () async { // 停止录音 logic.stopProcessingAudio(); state.isLongPressing.value = false; }, onClick: () async { if (state.talkStatus.value == TalkStatus.passiveCallWaitingAnswer) { // 接听 logic.initiateAnswerCommand(); } }, ), ), bottomBtnItemWidget( 'images/main/icon_lockDetail_hangUp.png', '挂断'.tr, Colors.red, onClick: () { // 挂断 logic.udpHangUpAction(); }), bottomBtnItemWidget( 'images/main/icon_lockDetail_monitoringUnlock.png', '开锁'.tr, AppColors.mainColor, onClick: () { // if (state.talkStatus.value == TalkStatus.answeredSuccessfully && // state.listData.value.length > 0) { // logic.udpOpenDoorAction(); logic.remoteOpenLock(); // } // if (UDPManage().remoteUnlock == 1) { // logic.udpOpenDoorAction(); // showDeletPasswordAlertDialog(context); // } else { // logic.showToast('请在锁设置中开启远程开锁'.tr); // } }, ) ]); } String getAnswerBtnImg() { switch (state.talkStatus.value) { case TalkStatus.passiveCallWaitingAnswer: return 'images/main/icon_lockDetail_monitoringAnswerCalls.png'; case TalkStatus.answeredSuccessfully: case TalkStatus.proactivelyCallWaitingAnswer: return 'images/main/icon_lockDetail_monitoringUnTalkback.png'; default: return 'images/main/icon_lockDetail_monitoringAnswerCalls.png'; } } String getAnswerBtnName() { switch (state.talkStatus.value) { case TalkStatus.passiveCallWaitingAnswer: return '接听'.tr; case TalkStatus.proactivelyCallWaitingAnswer: case TalkStatus.answeredSuccessfully: return '长按说话'.tr; default: return '接听'.tr; } } Widget bottomBtnItemWidget( String iconUrl, String name, Color backgroundColor, { required Function() onClick, Function()? longPress, Function()? longPressUp, }) { double wh = 80.w; return GestureDetector( onTap: onClick, onLongPress: longPress, onLongPressUp: longPressUp, child: SizedBox( height: 160.w, width: 140.w, child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ Container( width: wh, height: wh, constraints: BoxConstraints( minWidth: 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), Text( name, style: TextStyle(fontSize: 20.sp, color: Colors.white), textAlign: TextAlign.center, // 当文本超出指定行数时,使用省略号表示 maxLines: 2, // 设置最大行数为1 ) ], ), ), ); } // void showDeletPasswordAlertDialog(BuildContext context) { // showDialog( // barrierDismissible: false, // context: context, // builder: (BuildContext context) { // return ShowTFView( // title: '请输入6位数字开锁密码'.tr, // tipTitle: '', // controller: state.passwordTF, // inputFormatters: [ // LengthLimitingTextInputFormatter(6), //限制长度 // FilteringTextInputFormatter.allow(RegExp('[0-9]')), // ], // sureClick: () async { // //发送删除锁请求 // // if (state.passwordTF.text.isEmpty) { // // logic.showToast('请输入开锁密码'.tr); // // return; // // } // // // List numbers = state.passwordTF.text.split('').map((char) => int.parse(char)).toList(); // // 开锁 // // lockID // // final List numbers = []; // // final List lockIDData = utf8.encode(state.passwordTF.text); // // numbers.addAll(lockIDData); // // // topBytes = getFixedLengthList(lockIDData, 20 - lockIDData.length); // // for (int i = 0; i < 6 - lockIDData.length; i++) { // // numbers.add(0); // // } // // 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: GestureDetector( child: RotationTransition( //设置动画的旋转中心 alignment: Alignment.center, //动画控制器 turns: state.animationController, //将要执行动画的子view child: AnimatedOpacity( opacity: 0.5, duration: const Duration(seconds: 2), child: Image.asset( 'images/main/realTime_connecting.png', width: 220.w, height: 220.w, ), ), ), onTap: () { state.animationController.forward(); }, ), ); } @override void dispose() { state.animationController.dispose(); state.realTimePicTimer.cancel(); state.autoBackTimer.cancel(); state.videoBuffer.clear(); state.listData.value = Uint8List(0); CallTalk().finishAVData(); // UdpTalkDataHandler().resetDataRates(); super.dispose(); } }