diff --git a/android/app/build.gradle b/android/app/build.gradle index af31424..1e6f864 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -41,6 +41,9 @@ android { targetSdkVersion 29 versionCode flutterVersionCode.toInteger() versionName flutterVersionName + ndk { + abiFilters "arm64-v8a", "armeabi", "armeabi-v7a", "mips" // 不支持"x86", "x86_64"模拟器 + } } buildTypes { diff --git a/android/app/release/output.json b/android/app/release/output.json index 940283b..02a09d3 100644 --- a/android/app/release/output.json +++ b/android/app/release/output.json @@ -1 +1 @@ -[{"outputType":{"type":"APK"},"apkData":{"type":"MAIN","splits":[],"versionCode":20220620,"versionName":"1.5.3","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}] \ No newline at end of file +[{"outputType":{"type":"APK"},"apkData":{"type":"MAIN","splits":[],"versionCode":20250521,"versionName":"1.6.4","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}] \ No newline at end of file diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index c132438..3004b3f 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -36,6 +36,9 @@ + + + -1) { + if (mapKey.length == 0 || key.length < mapKey.length) { + mapKey = key; + } + } + } + + _mapChsName = mapChsName[mapKey]; + + if (_mapChsName == null) { + _mapChsName = mapName + (mapName.length > 0 ? '地图' : ''); + } + return _mapChsName; +} + +// key 全部小写 +Map mapChsName = { + 'apple': '苹果地图', + 'google': '谷歌地图', + 'googlego': '谷歌地图轻量版', + 'amap': '高德地图', + 'baidu': '百度地图', + 'waze': '位智地图', + 'yandexmaps': 'Yandex地图', + 'yandexnavi': 'Yandex导航地图', + 'citymapper': '城市地图', + 'mapswithme': '离线地图', + 'osmand': 'OsmAnd地图', + 'doubleGis': 'doubleGis地图', + 'tencent': '腾讯地图', +}; + // Timer g_remindTimer; //定时提醒变量 // // // 设置定时提醒 @@ -187,6 +228,7 @@ String copyright_info_PinYin = ''; // 区县中心地址 double center_latitude = -1; // 区县中心纬度 double center_longitude = -1; // 区县中心经度 +String official_seal = ''; // 区县单位公章 clear_user_info() { qx_code = -1; @@ -887,7 +929,7 @@ Widget getImageWidget() { color: Color.fromRGBO(49, 216, 123, 1), fontWeight: FontWeight.bold)), ), - SizedBox(height: ScreenUtil().setHeight(copyright_info.contains('\n') ? 25 : 90)), + SizedBox(height: ScreenUtil().setHeight(copyright_info.contains('\n') ? 25 : 70)), Container( alignment: Alignment.center, child: Text(copyright_info, diff --git a/lib/components/dioFun.dart b/lib/components/dioFun.dart index dd6e120..fd6c83f 100644 --- a/lib/components/dioFun.dart +++ b/lib/components/dioFun.dart @@ -2078,6 +2078,7 @@ Future playUrl({@required int index, String url, BuildContext context}) { id: index + 1, dwip: listDwinfoGetList2[index]['dwip'], url: urlnew, + urlType: 'rtmp', title: '点位视频\n${(index + 1)}、${listDwinfoGetList2[index]['dwmc']}', ))); diff --git a/lib/main.dart b/lib/main.dart index e24d0d1..d4253fc 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -91,7 +91,7 @@ class _MyAppState extends State { // g_bVoiceRemind = (null == g_bVoiceRemind) ? false : g_bVoiceRemind; // 默认关闭 g_bVoiceRemind = (null == g_bVoiceRemind) ? true : g_bVoiceRemind; // 默认开启 // print('g_bVoiceRemind = $g_bVoiceRemind'); - g_remindGap = await Storage.getInt('nRemindGap'); + g_remindGap = await Storage.getInt('nRemindGap'); g_remindGap = (null == g_remindGap) ? 60 : g_remindGap; // 提醒间隔默认为60S eventBus.fire(VoiceRemindUpdate('g_bVoiceRemind 数据已更新')); diff --git a/lib/my_fijkPanel_fix/my_fijkvalue.dart b/lib/my_fijkPanel_fix/my_fijkvalue.dart new file mode 100644 index 0000000..f9cc64c --- /dev/null +++ b/lib/my_fijkPanel_fix/my_fijkvalue.dart @@ -0,0 +1,50 @@ +//MIT License +// +//Copyright (c) [2019] [Befovy] +// +//Permission is hereby granted, free of charge, to any person obtaining a copy +//of this software and associated documentation files (the "Software"), to deal +//in the Software without restriction, including without limitation the rights +//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//copies of the Software, and to permit persons to whom the Software is +//furnished to do so, subject to the following conditions: +// +//The above copyright notice and this permission notice shall be included in all +//copies or substantial portions of the Software. +// +//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//SOFTWARE. + +//r:\Flutter\FlutterSDK\flutter\.pub-cache\hosted\pub.flutter-io.cn\fijkplayer-0.8.4\lib\core\MyFijkValue.dart +//part of fijkplayer; + +import 'dart:core'; + +//新增的,为变私有成员为公有成员 +class MyFijkData { + static String fijkViewPanelVolume = "__fijkview_panel_init_volume"; + static String fijkViewPanelBrightness = "__fijkview_panel_init_brightness"; + static String fijkViewPanelSeekto = "__fijkview_panel_sekto_position"; +} + +//新增的,为变私有成员为公有成员 +String mYduration2String(Duration duration) { + if (duration.inMilliseconds < 0) return "-: negtive"; + + String twoDigits(int n) { + if (n >= 10) return "$n"; + return "0$n"; + } + + String twoDigitMinutes = twoDigits(duration.inMinutes.remainder(60)); + String twoDigitSeconds = twoDigits(duration.inSeconds.remainder(60)); + int inHours = duration.inHours; + return inHours > 0 + ? "$inHours:$twoDigitMinutes:$twoDigitSeconds" + : "$twoDigitMinutes:$twoDigitSeconds"; +} diff --git a/lib/my_fijkPanel_fix/my_panel3.dart b/lib/my_fijkPanel_fix/my_panel3.dart new file mode 100644 index 0000000..c4225a3 --- /dev/null +++ b/lib/my_fijkPanel_fix/my_panel3.dart @@ -0,0 +1,828 @@ +import 'dart:async'; +import 'dart:core'; +import 'dart:math'; +import 'dart:ui'; +import 'dart:typed_data'; + +import 'package:fijkplayer/fijkplayer.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; +import 'package:flutter/widgets.dart'; +import 'my_fijkvalue.dart'; +import '../components/commonFun.dart'; +import 'package:provider/provider.dart'; +import '../provider/player_ratio.dart'; +import 'my_slider.dart'; +import '../my_extended_image/common/image_picker/_image_picker_io.dart'; +import '../services/Storage.dart'; + +// class MyFijkPanelWidgetBuilder extends StatefulWidget { +// const MyFijkPanelWidgetBuilder({Key key, this.path}) : super(key: key); +// final path; +// @override +// State createState() { +// return MyFijkPanelWidgetBuilderState(); +// } +// } + +// snapshot +ImageProvider myImageProvider; +//ImageProvider _imageProvider; +//Timer _snapshotTimer; + +FijkPanelWidgetBuilder fijkPanel2Builder3( + {Key key, + final FijkPlayer player, + final bool fill = false, + final int duration = 4000, + final bool doubleTap = true, + final bool snapShot = false, + final VoidCallback onBack}) { + return (FijkPlayer player, FijkData data, BuildContext context, Size viewSize, + Rect texturePos) { + return MyFijkPanelWidgetBuilder( + key: key, + player: player, + data: data, + onBack: onBack, + viewSize: viewSize, + texPos: texturePos, + fill: fill, + doubleTap: doubleTap, + snapShot: snapShot, + hideDuration: duration, + ); + }; +} + +class MyFijkPanelWidgetBuilder extends StatefulWidget { + final FijkPlayer player; + final FijkData data; + final VoidCallback onBack; + final Size viewSize; + final Rect texPos; + final bool fill; + final bool doubleTap; + final bool snapShot; + final int hideDuration; + + const MyFijkPanelWidgetBuilder( + {Key key, + @required this.player, + this.data, + this.fill, + this.onBack, + this.viewSize, + this.hideDuration, + this.doubleTap, + this.snapShot, + this.texPos}) + : assert(player != null), + assert( + hideDuration != null && hideDuration > 0 && hideDuration < 10000), + super(key: key); + + // @override + // FijkPanel2State createState() => FijkPanel2State(); + // @override + //State createState() { + MyFijkPanelWidgetBuilderState createState() { + return MyFijkPanelWidgetBuilderState(player); + } +} + +class MyFijkPanelWidgetBuilderState extends State + with SingleTickerProviderStateMixin { + FijkPlayer player; + + MyFijkPanelWidgetBuilderState(@required this.player) { + _oldFullScreen = player.value.fullScreen; + } + + PlayerRatioProvide playerRatioProvide; + AnimationController _controller; + Animation _animation; + Offset _normalizedOffset; + double _previousScale; + double _kMinFlingVelocity = 600.0; + + Offset _offset = Offset.zero; + double _scale = 1.0; + Offset _focalPoint = Offset.zero; + Offset _deltaPoint = Offset.zero; + + bool _oldFullScreen; + bool _bScaleing = false; + + //FijkPlayer get player => widget.player; + + Timer _hideTimer; + bool _hideStuff = true; + + Timer _statelessTimer; + bool _prepared = false; + bool _playing = false; + + //bool _dragLeft; + int _nDrag; //0 Left, 1 Center, 2 Right + double _volume; + double _brightness; + double _deltaX = 0; + double _deltaY = 0; + + double _seekPos = -1.0; + Duration _duration = Duration(); + Duration _currentPos = Duration(); + Duration _bufferPos = Duration(); + + StreamSubscription _currentPosSubs; + StreamSubscription _bufferPosSubs; + + StreamController _valController; + + // snapshot + //ImageProvider _imageProvider; + Timer _snapshotTimer; + + // Is it needed to clear seek data in FijkData (widget.data) + bool _needClearSeekData = true; + + static const MyFijkSliderColors sliderColors = MyFijkSliderColors(); + + @override + void initState() { + super.initState(); + + _controller = AnimationController(vsync: this); + _controller.addListener(() { + setState(() { + _offset = _animation.value; + }); + }); + + _valController = StreamController.broadcast(); + _prepared = player.state.index >= FijkState.prepared.index; + _playing = player.state == FijkState.started; + _duration = player.value.duration; + _currentPos = player.currentPos; + _bufferPos = player.bufferPos; + + _currentPosSubs = player.onCurrentPosUpdate.listen((v) { + if (_hideStuff == false) { + setState(() { + _currentPos = v; + }); + } else { + _currentPos = v; + } + if (_needClearSeekData) { + widget.data.clearValue(MyFijkData.fijkViewPanelSeekto); + } + _needClearSeekData = false; + }); + + if (widget.data.contains(MyFijkData.fijkViewPanelSeekto)) { + var pos = widget.data.getValue(MyFijkData.fijkViewPanelSeekto) as double; + _currentPos = Duration(milliseconds: pos.toInt()); + } + + _bufferPosSubs = player.onBufferPosUpdate.listen((v) { + if (_hideStuff == false) { + setState(() { + _bufferPos = v; + }); + } else { + _bufferPos = v; + } + }); + player.addListener(_playerValueChanged); + } + + @override + void dispose() { + super.dispose(); + _controller.dispose(); + _valController?.close(); + _hideTimer?.cancel(); + _statelessTimer?.cancel(); + _snapshotTimer?.cancel(); + _currentPosSubs?.cancel(); + _bufferPosSubs?.cancel(); + player.removeListener(_playerValueChanged); + } + + double dura2double(Duration d) { + return d != null ? d.inMilliseconds.toDouble() : 0.0; + } + + void _playerValueChanged() { + FijkValue value = player.value; + + if (value.duration != _duration) { + if (_hideStuff == false) { + setState(() { + _duration = value.duration; + }); + } else { + _duration = value.duration; + } + } + bool playing = (value.state == FijkState.started); + bool prepared = value.prepared; + if (playing != _playing || + prepared != _prepared || + value.state == FijkState.asyncPreparing) { + setState(() { + _playing = playing; + _prepared = prepared; + }); + } + } + + void _restartHideTimer() { + _hideTimer?.cancel(); + _hideTimer = Timer(Duration(milliseconds: widget.hideDuration), () { + setState(() { + _hideStuff = true; + }); + }); + } + + void onTapFun() { + if (_hideStuff == true) { + _restartHideTimer(); + } + setState(() { + _hideStuff = !_hideStuff; + }); + } + + void playOrPause() { + if (player.isPlayable() || player.state == FijkState.asyncPreparing) { + if (player.state == FijkState.started) { + //bPlaying = false; + playerRegionProvide.changePlayerState(false); + Storage.setString('bPlaying', 'false'); + player.pause(); + } else { + //bPlaying = true; + playerRegionProvide.changePlayerState(true); + Storage.setString('bPlaying', 'true'); + player.start(); + } + //setState(() {}); + } else { + FijkLog.w("Invalid state ${player.state} ,can't perform play or pause"); + } + } + + void onVerticalDragStartFun(DragStartDetails d) { + // if (_bScaleing) { + // return; + // } + if (d.localPosition.dx > panelWidth() * 3 / 4) { + // right, volume + //_dragLeft = false; + _nDrag = 2; //0 Left, 1 Center, 2 Right + + //https://fijkplayer.befovy.com/docs/zh/system-volume.html#gsc.tab=0 + /// never show system volume changed UI. + int neverShowUI = 2; + FijkVolume.setUIMode(neverShowUI); + + FijkVolume.getVol().then((v) { + if (widget.data != null && + !widget.data.contains(MyFijkData.fijkViewPanelVolume)) { + widget.data.setValue(MyFijkData.fijkViewPanelVolume, v); + } + setState(() { + _volume = v; + _valController.add(v); + }); + }); + } else if (d.localPosition.dx < panelWidth() / 4) { + // left, brightness + //_dragLeft = true; + _nDrag = 0; //0 Left, 1 Center, 2 Right + FijkPlugin.screenBrightness().then((v) { + if (widget.data != null && + !widget.data.contains(MyFijkData.fijkViewPanelBrightness)) { + widget.data.setValue(MyFijkData.fijkViewPanelBrightness, v); + } + setState(() { + _brightness = v; + _valController.add(v); + }); + }); + } else { + _nDrag = 1; //0 Left, 1 Center, 2 Right + // setState(() { + // if (_oldFullScreen != player.value.fullScreen) { + // //全屏和窗口之间切换,便初始化相关变量 + // _oldFullScreen = player.value.fullScreen; + // // _offset = Offset.zero; + // // _scale = 1.0; + // //_focalPoint = Offset.zero; + // //_deltaPoint = Offset.zero; + // } + // // _bScaleing = true; + // // _previousScale = _scale; + // // _normalizedOffset = (d.localPosition - _offset) / _scale; + // // // 计算图片放大后的位置 + // // _controller.stop(); + // }); + } + _statelessTimer?.cancel(); + _statelessTimer = Timer(const Duration(milliseconds: 2000), () { + setState(() {}); + }); + } + + void onVerticalDragUpdateFun(DragUpdateDetails d) { + // if (_bScaleing) { + // return; + // } + + double delta = d.primaryDelta / panelHeight(); + print("d.primaryDelta = ${d.primaryDelta}, delta = {$delta}"); + + delta = -delta.clamp(-1.0, 1.0); + //if (_dragLeft != null && _dragLeft == false) { + if (_nDrag != null && _nDrag == 2) { + if (_volume != null) { + _volume += delta; + _volume = _volume.clamp(0.0, 1.0); + FijkVolume.setVol(_volume); + setState(() { + _valController.add(_volume); + }); + } + //} else if (_dragLeft != null && _dragLeft == true) { + } else if (_nDrag != null && _nDrag == 0) { + if (_brightness != null) { + _brightness += delta; + _brightness = _brightness.clamp(0.0, 1.0); + FijkPlugin.setScreenBrightness(_brightness); + setState(() { + _valController.add(_brightness); + }); + } + } else { + setState(() { + //_scale = (_previousScale * details.scale).clamp(1.0, 10.0); + // 限制放大倍数 1~10倍 + // _offset = _clampOffset(d.localPosition - _normalizedOffset * _scale); + // playerRatioProvide.changeOffset(_offset); + + _deltaY -= 2 * d.primaryDelta / panelHeight(); + _deltaY = _deltaY.clamp(-1.0, 1.0); + playerRatioProvide.changeDeltaY(_deltaY); + // 更新当前位置 + }); + } + } + + void onVerticalDragEndFun(DragEndDetails e) { + _volume = null; + _brightness = null; + } + + Widget buildPlayButton(BuildContext context, double height) { + Icon icon = (player.state == FijkState.started) + ? Icon(Icons.pause) + : Icon(Icons.play_arrow); + bool fullScreen = player.value.fullScreen; + return IconButton( + padding: EdgeInsets.all(0), + iconSize: fullScreen ? height : height * 0.8, + color: Color(0xFFFFFFFF), + icon: icon, + onPressed: playOrPause, + ); + } + + Widget buildFullScreenButton(BuildContext context, double height) { + Icon icon = player.value.fullScreen + ? Icon(Icons.fullscreen_exit) + : Icon(Icons.fullscreen); + bool fullScreen = player.value.fullScreen; + return IconButton( + padding: EdgeInsets.all(0), + iconSize: fullScreen ? height : height * 0.8, + color: Color(0xFFFFFFFF), + icon: icon, + onPressed: () { + player.value.fullScreen + ? player.exitFullScreen() + : player.enterFullScreen(); + }, + ); + } + + Widget buildTimeText(BuildContext context, double height) { + String text = "${mYduration2String(_currentPos)}" + + "/${mYduration2String(_duration)}"; + return Text(text, style: TextStyle(fontSize: 12, color: Color(0xFFFFFFFF))); + } + + Widget buildSlider(BuildContext context) { + double duration = dura2double(_duration); + + double currentValue = _seekPos > 0 ? _seekPos : dura2double(_currentPos); + currentValue = currentValue.clamp(0.0, duration); + + double bufferPos = dura2double(_bufferPos); + bufferPos = bufferPos.clamp(0.0, duration); + + return Padding( + padding: EdgeInsets.only(left: 0), + child: MyFijkSlider( + colors: sliderColors, + value: currentValue, + cacheValue: bufferPos, + min: 0.0, + max: duration, + onChanged: (v) { + _restartHideTimer(); + setState(() { + _seekPos = v; + }); + }, + onChangeEnd: (v) { + setState(() { + player.seekTo(v.toInt()); + _currentPos = Duration(milliseconds: _seekPos.toInt()); + widget.data.setValue(MyFijkData.fijkViewPanelSeekto, _seekPos); + _needClearSeekData = true; + _seekPos = -1.0; + }); + }, + ), + ); + } + + Widget buildBottom(BuildContext context, double height) { + if (_duration != null && _duration.inMilliseconds > 0) { + return Row( + children: [ + buildPlayButton(context, height), + buildTimeText(context, height), + Expanded(child: buildSlider(context)), + buildFullScreenButton(context, height), + ], + ); + } else { + return Row( + children: [ + buildPlayButton(context, height), + Expanded(child: Container()), + buildFullScreenButton(context, height), + ], + ); + } + } + + void takeSnapshot() { + player.takeSnapShot().then((v) { + var provider = MemoryImage(v); + precacheImage(provider, context).then((_) { + setState(() { + myImageProvider = provider; + }); + }); + FijkLog.d("get snapshot succeed"); + + //Uint8List fileData; + String fileFath; + ImageSaver.save('extended_image_cropped_image.jpg', v).then((value) { + fileFath = value; + // var fileFath = await ImagePickerSaver.saveFile(fileData: fileData); + print('my save fileFath : $fileFath'); + }); + }).catchError((e) { + FijkLog.d("get snapshot failed"); + }); + } + + Widget buildPanel(BuildContext context) { + double height = panelHeight(); + + bool fullScreen = player.value.fullScreen; + Widget centerWidget = Container( + color: Color(0x00000000), + ); + + Widget centerChild = Container( + color: Color(0x00000000), + ); + + if (fullScreen && widget.snapShot) { + centerWidget = Row( + children: [ + Expanded(child: centerChild), + Padding( + padding: EdgeInsets.only(left: 10, right: 10, top: 8, bottom: 8), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + IconButton( + padding: EdgeInsets.all(0), + color: Color(0xFFFFFFFF), + icon: Icon(Icons.camera_alt), + onPressed: () { + takeSnapshot(); + }, + ), + ], + ), + ) + ], + ); + } + return Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Container( + height: height > 200 ? 80 : height / 5, + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [Color(0x88000000), Color(0x00000000)], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ), + ), + ), + Expanded( + child: centerWidget, + ), + Container( + height: height > 80 ? 80 : height / 2, + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [Color(0x88000000), Color(0x00000000)], + end: Alignment.topCenter, + begin: Alignment.bottomCenter, + ), + ), + alignment: Alignment.bottomCenter, + child: Container( + height: height > 80 ? 45 : height / 2, + padding: EdgeInsets.only(left: 8, right: 8, bottom: 5), + child: buildBottom(context, height > 80 ? 40 : height / 2), + // child: CustomFijkWidgetBottom( + // player: player, + // buildContext: context, + // viewSize: + // Size(widget.viewSize.width, widget.viewSize.height - 50), + // texturePos: Rect.fromLTRB(widget.texPos.left, widget.texPos.top, + // widget.texPos.width, widget.texPos.height - 50), + //), + ), + ) + ], + ); + } + + Rect panelRect() { + Rect rect = player.value.fullScreen || (true == widget.fill) + ? Rect.fromLTWH(0, 0, widget.viewSize.width, widget.viewSize.height) + : Rect.fromLTRB( + max(0.0, widget.texPos.left), + max(0.0, widget.texPos.top), + min(widget.viewSize.width, widget.texPos.right), + min(widget.viewSize.height, widget.texPos.bottom)); + return rect; + } + + double panelHeight() { + if (player.value.fullScreen || (true == widget.fill)) { + return widget.viewSize.height; + } else { + return min(widget.viewSize.height, widget.texPos.bottom) - + max(0.0, widget.texPos.top); + } + } + + double panelWidth() { + if (player.value.fullScreen || (true == widget.fill)) { + return widget.viewSize.width; + } else { + return min(widget.viewSize.width, widget.texPos.right) - + max(0.0, widget.texPos.left); + } + } + + Widget buildBack(BuildContext context) { + return IconButton( + padding: EdgeInsets.only(left: 5), + icon: Icon( + Icons.arrow_back_ios, + color: Color(0xDDFFFFFF), + ), + onPressed: widget.onBack, + ); + } + + Widget buildStateless() { + if (_volume != null || _brightness != null) { + Widget toast = _volume == null + ? defaultFijkBrightnessToast(_brightness, _valController.stream) + : defaultFijkVolumeToast(_volume, _valController.stream); + return IgnorePointer( + child: AnimatedOpacity( + opacity: 1, + duration: Duration(milliseconds: 500), + child: toast, + ), + ); + } else if (player.state == FijkState.asyncPreparing) { + return Container( + alignment: Alignment.center, + child: SizedBox( + width: 30, + height: 30, + child: CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation(Colors.white)), + ), + ); + } else if (player.state == FijkState.error) { + return Container( + alignment: Alignment.center, + child: Icon( + Icons.error, + size: 30, + color: Color(0x99FFFFFF), + ), + ); + } else if (myImageProvider != null) { + _snapshotTimer?.cancel(); + _snapshotTimer = Timer(Duration(milliseconds: 1500), () { + if (mounted) { + setState(() { + myImageProvider = null; + }); + } + }); + return Center( + child: IgnorePointer( + child: Container( + decoration: BoxDecoration( + border: Border.all(color: Colors.yellowAccent, width: 3)), + child: + Image(height: 200, fit: BoxFit.contain, image: myImageProvider), + ), + ), + ); + } else { + return Container(); + } + } + + GestureDetector buildGestureDetector(BuildContext context) { + return GestureDetector( + onScaleStart: _handleOnScaleStart, + onScaleUpdate: _handleOnScaleUpdate, + onScaleEnd: _handleOnScaleEnd, + onDoubleTap: () { + //自定义 FijkView 的双击响应 + print('My onDoubleTap'); + if (1.0 == playerRatioProvide.scale) { + playOrPause(); + } else { + _handleScaleBack(); + } + }, + + onTap: onTapFun, + //onDoubleTap: widget.doubleTap ? onDoubleTapFun : null, + onVerticalDragUpdate: onVerticalDragUpdateFun, + onVerticalDragStart: onVerticalDragStartFun, + onVerticalDragEnd: onVerticalDragEndFun, + //onHorizontalDragUpdate: (d) {}, + child: AbsorbPointer( + absorbing: _hideStuff, + child: AnimatedOpacity( + opacity: _hideStuff ? 0 : 1, + duration: Duration(milliseconds: 300), + child: buildPanel(context), + ), + ), + ); + } + + void _handleOnScaleStart(ScaleStartDetails details) { + setState(() { + // if (_oldFullScreen != player.value.fullScreen) { + // //全屏和窗口之间切换,便初始化相关变量 + // _oldFullScreen = player.value.fullScreen; + // // _offset = Offset.zero; + // // _scale = 1.0; + // //_focalPoint = Offset.zero; + // //_deltaPoint = Offset.zero; + // } + + _bScaleing = true; + _previousScale = _scale; + _normalizedOffset = (details.focalPoint - _offset) / _scale; + // 计算图片放大后的位置 + _controller.stop(); + _focalPoint = details.focalPoint; + }); + } + + void _handleScaleBack() { + // 更新当前位置 + playerRatioProvide.changeScale(1.0); + playerRatioProvide.changeOffset(Offset(0, 0)); + playerRatioProvide.changeDeltaX(0.0); + playerRatioProvide.changeDeltaY(0.0); + } + + void _handleOnScaleUpdate(ScaleUpdateDetails details) { + setState(() { + print('details.scale = ${details.scale}'); + //print('_scale = $_scale, offset.dx = ${_offset.dx}, offset.dy = ${_offset.dy}'); + //print('details.focalPoint = (${details.focalPoint.dx}, ${details.focalPoint.dy})'); + //print('details.horizontalScale = ${details.horizontalScale}; details.verticalScale = ${details.verticalScale})'); + + if (1.0 != details.scale) { + _scale = (_previousScale * details.scale).clamp(1.0, 10.0); + playerRatioProvide.changeScale(_scale); + // 限制放大倍数 1~10倍 + _offset = _clampOffset(details.focalPoint - _normalizedOffset * _scale); + } else { + _deltaPoint = _clampdeltaPoint(details.focalPoint - _focalPoint); + _focalPoint = details.focalPoint; + print('_deltaPoint = (${_deltaPoint.dx}, ${_deltaPoint.dy})'); + playerRatioProvide.changeDeltaX(_deltaPoint.dx); + playerRatioProvide.changeDeltaY(_deltaPoint.dy); + } + + // 更新当前位置 + playerRatioProvide.changeOffset(_offset); + }); + } + + Offset _clampdeltaPoint(Offset delta) { + final Size size = context.size; // widget的屏幕尺寸 + double rate = 2.0; + Offset deltaPoint = + Offset((rate * delta.dx / size.width), (rate * delta.dy / size.height)); + _deltaPoint -= deltaPoint; + return Offset( + _deltaPoint.dx.clamp(-1.0, 1.0), _deltaPoint.dy.clamp(-1.0, 1.0)); + } + + Offset _clampOffset(Offset offset) { + final Size size = context.size; + // widget的屏幕宽度 + final Offset minOffset = Offset(size.width, size.height) * (1.0 - _scale); + // 限制他的最小尺寸 + return Offset( + offset.dx.clamp(minOffset.dx, 0.0), offset.dy.clamp(minOffset.dy, 0.0)); + } + + void _handleOnScaleEnd(ScaleEndDetails details) { + final double magnitude = details.velocity.pixelsPerSecond.distance; + if (magnitude < _kMinFlingVelocity) return; + final Offset direction = details.velocity.pixelsPerSecond / magnitude; + // 计算当前的方向 + final double distance = (Offset.zero & context.size).shortestSide; + // 计算放大倍速,并相应的放大宽和高,比如原来是600*480的图片,放大后倍数为1.25倍时,宽和高是同时变化的 + _animation = _controller.drive(Tween( + begin: _offset, end: _clampOffset(_offset + direction * distance))); + _controller + ..value = 0.0 + ..fling(velocity: magnitude / 1000.0); + _bScaleing = false; + } + + @override + Widget build(BuildContext context) { + playerRatioProvide = Provider.of(context); + Rect rect = panelRect(); + + List ws = []; + + if (_statelessTimer != null && _statelessTimer.isActive) { + ws.add(buildStateless()); + } else if (player.state == FijkState.asyncPreparing) { + ws.add(buildStateless()); + } else if (player.state == FijkState.error) { + ws.add(buildStateless()); + } else if (myImageProvider != null) { + ws.add(buildStateless()); + } + ws.add(buildGestureDetector(context)); + if (widget.onBack != null) { + ws.add(buildBack(context)); + } + return Positioned.fromRect( + rect: rect, + child: Stack(children: ws), + ); + } + +} diff --git a/lib/my_fijkPanel_fix/my_slider.dart b/lib/my_fijkPanel_fix/my_slider.dart new file mode 100644 index 0000000..46e4c2d --- /dev/null +++ b/lib/my_fijkPanel_fix/my_slider.dart @@ -0,0 +1,244 @@ +//MIT License +// +//Copyright (c) [2019] [Befovy] +// +//Permission is hereby granted, free of charge, to any person obtaining a copy +//of this software and associated documentation files (the "Software"), to deal +//in the Software without restriction, including without limitation the rights +//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//copies of the Software, and to permit persons to whom the Software is +//furnished to do so, subject to the following conditions: +// +//The above copyright notice and this permission notice shall be included in all +//copies or substantial portions of the Software. +// +//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//SOFTWARE. + +//part of fijkplayer; +import 'package:flutter/material.dart'; +import 'dart:core'; +import 'dart:math'; +import 'dart:ui'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/rendering.dart'; +import 'package:flutter/widgets.dart'; + +/// MyFijkSlider is like Slider in Flutter SDK. +/// MyFijkSlider support [cacheValue] which can be used +/// to show the player's cached buffer. +/// The [colors] is used to make colorful painter to draw the line and circle. +class MyFijkSlider extends StatefulWidget { + final double value; + final double cacheValue; + + final ValueChanged onChanged; + final ValueChanged onChangeStart; + final ValueChanged onChangeEnd; + + final double min; + final double max; + + final MyFijkSliderColors colors; + + const MyFijkSlider({ + Key key, + @required this.value, + @required this.onChanged, + this.cacheValue = 0.0, + this.onChangeStart, + this.onChangeEnd, + this.min = 0.0, + this.max = 1.0, + this.colors = const MyFijkSliderColors(), + }) : assert(value != null), + assert(cacheValue != null), + assert(min != null), + assert(max != null), + assert(min <= max), + assert(value >= min && value <= max), + super(key: key); + + @override + State createState() { + return _MyFijkSliderState(); + } +} + +class _MyFijkSliderState extends State { + bool dragging = false; + + double dragValue; + + static const double margin = 2.0; + + @override + Widget build(BuildContext context) { + double v = widget.value / (widget.max - widget.min); + double cv = widget.cacheValue / (widget.max - widget.min); + + return GestureDetector( + child: Container( + margin: EdgeInsets.only(left: margin, right: margin), + height: double.infinity, + width: double.infinity, + color: Colors.transparent, + child: CustomPaint( + painter: _MySliderPainter(v, cv, dragging, colors: widget.colors), + ), + ), + onHorizontalDragStart: (DragStartDetails details) { + setState(() { + dragging = true; + }); + dragValue = widget.value; + if (widget.onChangeStart != null) { + widget.onChangeStart(dragValue); + } + }, + onHorizontalDragUpdate: (DragUpdateDetails details) { + final box = context.findRenderObject() as RenderBox; + final dx = details.localPosition.dx; + dragValue = (dx - margin) / (box.size.width - 2 * margin); + dragValue = max(0, min(1, dragValue)); + dragValue = dragValue * (widget.max - widget.min) + widget.min; + if (widget.onChanged != null) { + widget.onChanged(dragValue); + } + }, + onHorizontalDragEnd: (DragEndDetails details) { + setState(() { + dragging = false; + }); + if (widget.onChangeEnd != null) { + widget.onChangeEnd(dragValue); + } + }, + ); + } +} + +/// Colors for the MyFijkSlider +class MyFijkSliderColors { + const MyFijkSliderColors({ + this.playedColor = const Color.fromRGBO(255, 0, 0, 0.6), + this.bufferedColor = const Color.fromRGBO(50, 50, 100, 0.4), + this.cursorColor = const Color.fromRGBO(255, 0, 0, 0.8), + this.baselineColor = const Color.fromRGBO(200, 200, 200, 0.5), + }); + + final Color playedColor; + final Color bufferedColor; + final Color cursorColor; + final Color baselineColor; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is MyFijkSliderColors && + runtimeType == other.runtimeType && + hashCode == other.hashCode; + + @override + int get hashCode => + hashValues(playedColor, bufferedColor, cursorColor, baselineColor); +} + +class _MySliderPainter extends CustomPainter { + final double v; + final double cv; + + final bool dragging; + final Paint pt = Paint(); + + final MyFijkSliderColors colors; + + _MySliderPainter(this.v, this.cv, this.dragging, + {this.colors = const MyFijkSliderColors()}) + : assert(colors != null), + assert(v != null), + assert(cv != null); + + @override + void paint(Canvas canvas, Size size) { + double lineHeight = min(size.height / 2, 1); + pt.color = colors.baselineColor; + + double radius = min(size.height / 2, 4); + // draw background + canvas.drawRRect( + RRect.fromRectAndRadius( + Rect.fromPoints( + Offset(0, size.height / 2 - lineHeight), + Offset(size.width, size.height / 2 + lineHeight), + ), + Radius.circular(radius), + ), + pt, + ); + + final double value = v * size.width; + + // draw played part + pt.color = colors.playedColor; + canvas.drawRRect( + RRect.fromRectAndRadius( + Rect.fromPoints( + Offset(0, size.height / 2 - lineHeight), + Offset(value, size.height / 2 + lineHeight), + ), + Radius.circular(radius), + ), + pt, + ); + + // draw cached part + final double cacheValue = cv * size.width; + if (cacheValue > value && cacheValue > 0) { + pt.color = colors.bufferedColor; + canvas.drawRRect( + RRect.fromRectAndRadius( + Rect.fromPoints( + Offset(value, size.height / 2 - lineHeight), + Offset(cacheValue, size.height / 2 + lineHeight), + ), + Radius.circular(radius), + ), + pt, + ); + } + + //注意:改变拖动圆形光标,必须按Q退出App,再重新flutter run才能生效 + // draw circle cursor + // pt.color = colors.cursorColor; + // pt.color = pt.color.withAlpha(max(0, pt.color.alpha - 50)); + //radius = min(size.height / 2, dragging ? 10 : 5); + pt.color = Colors.red; + //radius = 10; + radius = min(size.height / 2, 10); + canvas.drawCircle(Offset(value, size.height / 2), radius, pt); + //pt.color = colors.cursorColor; + //radius = min(size.height / 2, dragging ? 6 : 3); + //canvas.drawCircle(Offset(value, size.height / 2), radius, pt); + } + + @override + bool operator ==(Object other) => + identical(this, other) || + other is _MySliderPainter && hashCode == other.hashCode; + + @override + int get hashCode => hashValues(v, cv, dragging, colors); + + @override + bool shouldRepaint(_MySliderPainter oldDelegate) { + return hashCode != oldDelegate.hashCode; + } +} diff --git a/lib/my_flutter_drag_scale/example/test/widget_test.dart b/lib/my_flutter_drag_scale/example/test/widget_test.dart index 0fb031a..be5e3af 100644 --- a/lib/my_flutter_drag_scale/example/test/widget_test.dart +++ b/lib/my_flutter_drag_scale/example/test/widget_test.dart @@ -8,12 +8,12 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:flutter_drag_scale_example/main.dart'; +// import 'package:flutter_drag_scale_example/main.dart'; void main() { testWidgets('Verify Platform version', (WidgetTester tester) async { // Build our app and trigger a frame. - await tester.pumpWidget(MyApp()); + // await tester.pumpWidget(MyApp()); // Verify that platform version is retrieved. expect( diff --git a/lib/my_flutter_superplayer/android/src/main/java/com/tencent/liteav/demo/superplayer/model/SuperPlayerImpl.java b/lib/my_flutter_superplayer/android/src/main/java/com/tencent/liteav/demo/superplayer/model/SuperPlayerImpl.java index 7fc5c74..1bfaa32 100644 --- a/lib/my_flutter_superplayer/android/src/main/java/com/tencent/liteav/demo/superplayer/model/SuperPlayerImpl.java +++ b/lib/my_flutter_superplayer/android/src/main/java/com/tencent/liteav/demo/superplayer/model/SuperPlayerImpl.java @@ -103,7 +103,7 @@ public class SuperPlayerImpl implements SuperPlayer, ITXVodPlayListener, ITXLive case TXLiveConstants.PLAY_ERR_NET_DISCONNECT: case TXLiveConstants.PLAY_EVT_PLAY_END: if (mCurrentPlayType == SuperPlayerDef.PlayerType.LIVE_SHIFT) { // 直播时移失败,返回直播 - mLivePlayer.resumeLive(); + // mLivePlayer.resumeLive(); updatePlayerType(SuperPlayerDef.PlayerType.LIVE); onError(SuperPlayerCode.LIVE_SHIFT_FAIL, "时移失败,返回直播"); updatePlayerState(SuperPlayerDef.PlayerState.PLAYING); @@ -398,7 +398,10 @@ public class SuperPlayerImpl implements SuperPlayer, ITXVodPlayListener, ITXLive mCurrentPlayVideoURL = url; if (mLivePlayer != null) { mLivePlayer.setPlayListener(this); - int result = mLivePlayer.startPlay(url, playType); // result返回值:0 success; -1 empty url; -2 invalid url; -3 invalid playType; + // 由于腾讯播放器SDK在2022-10-11进行更新,导致hyzp_ybqx项目编译出现下面报错。将下面文件 + //\hyzp_ybqx\lib\my_flutter_superplayer\android\src\main\java\com\tencent\liteav\demo\superplayer\model\SuperPlayerImpl.java + //中的TXLivePlayer 的 startPlay 变更为 startLivePlay,将TXVodPlayer 的 startPlay 变更为 startVodPlay,问题解决。 + int result = mLivePlayer.startLivePlay(url, playType); // result返回值:0 success; -1 empty url; -2 invalid url; -3 invalid playType; if (result != 0) { TXCLog.e(TAG, "playLiveURL videoURL:" + url + ",result:" + result); } else { @@ -449,9 +452,9 @@ public class SuperPlayerImpl implements SuperPlayer, ITXVodPlayListener, ITXLive query += "spfileid=" + mFileId + "&spdrmtype=" + drmType + "&spappid=" + mAppId; Uri newUri = uri.buildUpon().query(query).build(); TXCLog.i(TAG, "playVodURL: newurl = " + Uri.decode(newUri.toString()) + " ;url= " + url); - ret = mVodPlayer.startPlay(Uri.decode(newUri.toString())); + ret = mVodPlayer.startVodPlay(Uri.decode(newUri.toString())); } else { - ret = mVodPlayer.startPlay(url); + ret = mVodPlayer.startVodPlay(url); } if (ret == 0) { @@ -498,7 +501,7 @@ public class SuperPlayerImpl implements SuperPlayer, ITXVodPlayListener, ITXLive e.printStackTrace(); TXCLog.e(TAG, "playTimeShiftLiveURL: bizidNum error = " + bizid); } - mLivePlayer.prepareLiveSeek(domian, bizidNum); + // mLivePlayer.prepareLiveSeek(domian, bizidNum); } /** @@ -730,7 +733,7 @@ public class SuperPlayerImpl implements SuperPlayer, ITXVodPlayListener, ITXLive @Override public void resumeLive() { if (mCurrentPlayType == SuperPlayerDef.PlayerType.LIVE_SHIFT) { - mLivePlayer.resumeLive(); + // mLivePlayer.resumeLive(); } updatePlayerType(SuperPlayerDef.PlayerType.LIVE); } @@ -805,7 +808,7 @@ public class SuperPlayerImpl implements SuperPlayer, ITXVodPlayListener, ITXLive updatePlayerType(SuperPlayerDef.PlayerType.LIVE_SHIFT); LogReport.getInstance().uploadLogs(LogReport.ELK_ACTION_TIMESHIFT, 0, 0); if (mLivePlayer != null) { - mLivePlayer.seek(position); + // mLivePlayer.seek(position); } } if (mObserver != null) { @@ -856,7 +859,7 @@ public class SuperPlayerImpl implements SuperPlayer, ITXVodPlayListener, ITXLive mVodPlayer.stopPlay(true); TXCLog.i(TAG, "onQualitySelect quality.url:" + quality.url); mVodPlayer.setStartTime(currentTime); - mVodPlayer.startPlay(quality.url); + mVodPlayer.startVodPlay(quality.url); } else { //br!=0;index!=-1;url=null TXCLog.i(TAG, "setBitrateIndex quality.index:" + quality.index); // 说明是多bitrate的m3u8子流,会自动无缝seek diff --git a/lib/pages/Works/DWDT/basic_map.dart b/lib/pages/Works/DWDT/basic_map.dart index a23d116..84bf989 100644 --- a/lib/pages/Works/DWDT/basic_map.dart +++ b/lib/pages/Works/DWDT/basic_map.dart @@ -4,8 +4,10 @@ import 'package:flutter_bmfmap/BaiduMap/bmfmap_map.dart'; import 'package:flutter_screenutil/screen_util.dart'; import 'package:hyzp_ybqx/components/commonFun.dart'; import 'package:hyzp_ybqx/components/hyxx_data_handle.dart'; +import 'package:map_launcher/map_launcher.dart'; import '../../../components/dioFun.dart'; +import '../../../services/Storage.dart'; import 'dwInfoDialog.dart'; import 'dwInfo_data.dart'; @@ -20,6 +22,11 @@ class BasicMap extends StatefulWidget { } class _BasicMapState extends State { + // 导航相关代码 + double destinationLatitude = 28.45382237207785; + double destinationLongitude = 104.7506958256658; + String destinationTitle = '珙县大坪上'; + Size screenSize; BMFMapOptions mapOptions; BMFMapController myMapController; @@ -32,9 +39,18 @@ class _BasicMapState extends State { } } + List availableMaps; + + void init() async { + // 获取用户选择的默认地图 + availableMaps = await MapLauncher.installedMaps; + } + @override void initState() { super.initState(); + init(); + mapOptions = BMFMapOptions( //center: BMFCoordinate(39.965, 116.404),//北京市 //30 116.395645038,39.9299857781 北京-北京市 @@ -291,8 +307,17 @@ class _BasicMapState extends State { .push( PageRouteBuilder( opaque: false, - pageBuilder: (context, animation, secondaryAnimation) => - dwInfoDialog(id: id, dwIndex: dwIndex, title: title, content: content), + pageBuilder: (context, animation, secondaryAnimation) => dwInfoDialog( + id: id, + dwIndex: dwIndex, + title: title, + content: content, + parentContext: context, + destinationLatitude: double.parse(listCoordinate[1]), + destinationLongitude: double.parse(listCoordinate[0]), + destinationTitle: listDwinfoGetList2[dwIndex]["dwmc"], + availableMaps: availableMaps, + ), ), ) .then((value) async { diff --git a/lib/pages/Works/DWDT/dwInfoDialog.dart b/lib/pages/Works/DWDT/dwInfoDialog.dart index 7437318..6594dd5 100644 --- a/lib/pages/Works/DWDT/dwInfoDialog.dart +++ b/lib/pages/Works/DWDT/dwInfoDialog.dart @@ -1,21 +1,38 @@ import 'package:flutter/material.dart'; +import 'package:geolocator/geolocator.dart'; import 'package:hyzp_ybqx/components/dioFun.dart'; +import 'package:map_launcher/map_launcher.dart'; //import 'package:hyzp_ybqx/widget/player_pro.dart'; import '../../../components/commonFun.dart'; +import '../../../services/Storage.dart'; +import 'maps_sheet.dart'; //确认对话框 class dwInfoDialog extends Dialog { - dwInfoDialog({@required this.id, this.title = "", @required this.dwIndex, this.content}); + dwInfoDialog({ + @required this.id, + this.title = "", + @required this.dwIndex, + this.content, + // 导航相关参数 + this.parentContext, + this.destinationLatitude, + this.destinationLongitude, + this.destinationTitle, + this.availableMaps, + }); int dwIndex; String id; String title; String content; bool ret = false; + BuildContext parentContext; @override Widget build(BuildContext context) { + getCurrentPosition(); Size mediaSize = MediaQuery.of(context).size; return WillPopScope( child: Material( @@ -90,9 +107,9 @@ class dwInfoDialog extends Dialog { onPressed: () async { ret = true; getingDwVideo = false; - Navigator.pop(context, ret); //关闭弹框,返回sRet + navigationMap(context); }, - child: Text("确认"), + child: Text("导航"), ), RaisedButton( child: Text("取消"), @@ -115,4 +132,102 @@ class dwInfoDialog extends Dialog { }, ); } + + // 导航相关代码 + double destinationLatitude = 28.45382237207785; + double destinationLongitude = 104.7506958256658; + String destinationTitle = '珙县大坪上'; + + double originLatitude; + double originLongitude; + String originTitle = '我的位置'; + + List waypoints = [ + // Coords(37.7705112, -122.4108267), + // Coords(37.6988984, -122.4830961), + // Coords(37.7935754, -122.483654), + ]; + + DirectionsMode directionsMode = DirectionsMode.driving; + + List availableMaps; + + // String defaultMapName = 'Amap'; + String defaultMapName = g_defaultMapName; + AvailableMap defalutMap; + + void getCurrentPosition() async { + print('getCurrentPosition begin'); + + // 是否保存默认地图 + g_bSaveDefaultMap = await Storage.getBool('bSaveDefaultMap'); + g_bSaveDefaultMap = (null == g_bSaveDefaultMap) ? false : g_bSaveDefaultMap; // 默认不保存 + // 用户选择的默认地图名称 + g_defaultMapName = await Storage.getString('defaultMapName'); + g_defaultMapName = (null == g_defaultMapName) ? '' : g_defaultMapName; // 默认为空字符串 + defaultMapName = g_defaultMapName; + + // 获取用户选择的默认地图 + // availableMaps = await MapLauncher.installedMaps; // 为避免延迟错乱,该变量由父组件传入 + if (defaultMapName != null && defaultMapName.length > 0) { + for (var map in availableMaps) { + if (defaultMapName.toLowerCase() == map.mapName.toLowerCase()) { + defalutMap = map; + break; + } + } + } + + await Geolocator.getCurrentPosition().then((Position value) { + originLatitude = value.latitude; + originLongitude = value.longitude; + print('value = ${value.toString()}'); + // value = Latitude: 28.796201, Longitude: 104.607751 + print('getCurrentPosition end'); + }); + } + + navigationMap(BuildContext context) { + print('this.defalutMap = ${defalutMap}'); + if (defalutMap != null) { + defalutMap.showDirections( + destination: Coords( + destinationLatitude, + destinationLongitude, + ), + destinationTitle: destinationTitle, + origin: originLatitude == null || originLongitude == null + ? null + : Coords(originLatitude, originLongitude), + originTitle: originTitle, + waypoints: waypoints, + directionsMode: directionsMode, + ); + } else { + MapsSheet.show( + context: parentContext, + onMapTap: (map) { + if (g_bSaveDefaultMap) { + g_defaultMapName = map.mapName; + Storage.setString('defaultMapName', g_defaultMapName); + } + + map.showDirections( + destination: Coords( + destinationLatitude, + destinationLongitude, + ), + destinationTitle: destinationTitle, + origin: originLatitude == null || originLongitude == null + ? null + : Coords(originLatitude, originLongitude), + originTitle: originTitle, + waypoints: waypoints, + directionsMode: directionsMode, + ); + }, + ); + } + Navigator.pop(context, ret); //关闭弹框,返回sRet + } } diff --git a/lib/pages/Works/DWDT/maps_sheet.dart b/lib/pages/Works/DWDT/maps_sheet.dart new file mode 100644 index 0000000..a2bda47 --- /dev/null +++ b/lib/pages/Works/DWDT/maps_sheet.dart @@ -0,0 +1,49 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:map_launcher/map_launcher.dart'; + +class MapsSheet { + static show({ + @required BuildContext context, + @required Function(AvailableMap map) onMapTap, + }) async { + final availableMaps = await MapLauncher.installedMaps; + + showModalBottomSheet( + context: context, + builder: (BuildContext context) { + return SafeArea( + child: Column( + children: [ + Expanded( + child: SingleChildScrollView( + child: Container( + child: Wrap( + children: [ + for (var map in availableMaps) + ListTile( + onTap: () { + print('map.mapName = ${map.mapName}'); + // map.mapName = Amap + Navigator.pop(context); + onMapTap(map); + }, + title: Text(map.mapName), + leading: SvgPicture.asset( + map.icon, + height: 30.0, + width: 30.0, + ), + ), + ], + ), + ), + ), + ), + ], + ), + ); + }, + ); + } +} diff --git a/lib/pages/Works/HYSH/tsjj_content_new.dart b/lib/pages/Works/HYSH/tsjj_content_new.dart index 868b669..776ac0f 100644 --- a/lib/pages/Works/HYSH/tsjj_content_new.dart +++ b/lib/pages/Works/HYSH/tsjj_content_new.dart @@ -13,6 +13,7 @@ import 'package:keyboard_avoider/keyboard_avoider.dart'; // import '../../../components/commonFun.dart'; + //import 'package:hyzp_ybqx/widget/player_pro_new.dart'; import '../../../components/dioFun.dart'; import '../../../components/doJSON.dart'; @@ -75,6 +76,8 @@ class _LoginPageState extends State with SingleTickerProviderSta try_setState(); //避免如下异常报错 }); + print('official_seal = $official_seal'); + _widthLeft = _screenWidth / 2; getListFlields(); @@ -123,7 +126,8 @@ class _LoginPageState extends State with SingleTickerProviderSta print('_mapTsjjGetTsStatus = $_mapTsjjGetTsStatus'); // 获取网络图片尺寸,getMediaUrl(_mapGetTsjjGetData['pic_url']) - await flustars.WidgetUtil.getImageWH(url: getMediaUrl(_mapGetTsjjGetData['pic_url'])).then((rect) { + await flustars.WidgetUtil.getImageWH(url: getMediaUrl(_mapGetTsjjGetData['pic_url'])) + .then((rect) { if (null != rect) { _radioImage = rect.height / rect.width; print("rect = $rect,_radioImage = $_radioImage"); @@ -915,8 +919,13 @@ class _LoginPageState extends State with SingleTickerProviderSta //color: Colors.black12, decoration: BoxDecoration( //color: Colors.white, + // image: DecorationImage( + // image: AssetImage("assets/images/jkzx_stamp.png"), fit: BoxFit.contain), + // image: DecorationImage( + // image: AssetImage("assets/images/宜宾市长宁生态环境局.png"), fit: BoxFit.contain), image: DecorationImage( - image: AssetImage("assets/images/jkzx_stamp.png"), fit: BoxFit.contain), + image: AssetImage("assets/images/" + official_seal), fit: BoxFit.contain), + ), //child: ), diff --git a/lib/pages/tabs/page4_myMsics_new.dart b/lib/pages/tabs/page4_myMsics_new.dart index 346c738..c88de0c 100644 --- a/lib/pages/tabs/page4_myMsics_new.dart +++ b/lib/pages/tabs/page4_myMsics_new.dart @@ -16,6 +16,7 @@ import 'package:hyzp_ybqx/pages/Works/TJXX/tj_data.dart'; import 'package:hyzp_ybqx/pages/tabs/page5_userManager.dart'; import 'package:hyzp_ybqx/pages/tabs/page6_download.dart'; import 'package:hyzp_ybqx/pages/tabs/page7_setRemind.dart'; +import 'package:hyzp_ybqx/pages/tabs/page8_defaultMap.dart'; import 'package:hyzp_ybqx/services/EventBus.dart'; import 'package:hyzp_ybqx/widget/JdButton.dart'; import 'package:package_info/package_info.dart'; @@ -111,35 +112,35 @@ class _Page4_MyMsicsNewState extends State static onNullFun() {} Widget _getListTile( - title, { - String leadPath = '', - Color leadColor, - Color textColor, - onTapFun = onNullFun, - onLongPressFun = onNullFun, - size = 16.0, - bool bBadge = false, - Widget trailWidget = const Icon(Icons.arrow_forward_ios), - }) { + title, { + String leadPath = '', + Color leadColor, + Color textColor, + onTapFun = onNullFun, + onLongPressFun = onNullFun, + size = 16.0, + bool bBadge = false, + Widget trailWidget = const Icon(Icons.arrow_forward_ios), + }) { // print("_getListTile bVoiceRemind = $bVoiceRemind"); // _getListTile bVoiceRemind = false return Column( children: [ ListTile( leading: bBadge ? Badge( - position: BadgePosition.topEnd(top: -7, end: -12), - badgeContent: null, - child: Image.asset( - leadPath, - height: ScreenUtil().setHeight(78), - fit: BoxFit.fitHeight, - ), - ) + position: BadgePosition.topEnd(top: -7, end: -12), + badgeContent: null, + child: Image.asset( + leadPath, + height: ScreenUtil().setHeight(78), + fit: BoxFit.fitHeight, + ), + ) : Image.asset( - leadPath, - height: ScreenUtil().setHeight(78), - fit: BoxFit.fitHeight, - ), + leadPath, + height: ScreenUtil().setHeight(78), + fit: BoxFit.fitHeight, + ), title: Text(title, style: TextStyle(fontSize: size, color: textColor)), trailing: trailWidget, contentPadding: EdgeInsets.symmetric(horizontal: 20.0, vertical: 0), @@ -202,11 +203,13 @@ class _Page4_MyMsicsNewState extends State leadPath: 'assets/images/账户管理.png', leadColor: _deepBlueColor, onTapFun: OnTap_user_manager), + _getListTile('默认导航', + leadPath: 'assets/images/默认地图3.png', + leadColor: _deepBlueColor, + onTapFun: OnTap_DefaultMap), //bNewVer:是否发现新版本 _getListTile('提醒设置', - leadPath: 'assets/images/语音提醒.png', - leadColor: _deepGreyColor, - onTapFun: OnTap_Remind), + leadPath: 'assets/images/语音提醒.png', leadColor: _deepGreyColor, onTapFun: OnTap_Remind), _getListTile('关于', leadPath: 'assets/images/关于.png', leadColor: _deepBlueColor, onTapFun: OnTap_MyAbout) @@ -511,6 +514,10 @@ class _Page4_MyMsicsNewState extends State Navigator.of(context).push(MaterialPageRoute(builder: (context) => page5_userManager())); } + OnTap_DefaultMap() async { + Navigator.of(context).push(MaterialPageRoute(builder: (context) => page8_defaultMap())); + } + OnTap_download() { Navigator.of(context).push(MaterialPageRoute(builder: (context) => page6_download())); } diff --git a/lib/pages/tabs/page8_defaultMap.dart b/lib/pages/tabs/page8_defaultMap.dart new file mode 100644 index 0000000..7f8723c --- /dev/null +++ b/lib/pages/tabs/page8_defaultMap.dart @@ -0,0 +1,284 @@ +import 'package:badges/badges.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:hyzp_ybqx/pages/Login/FaceReg.dart'; +import 'package:hyzp_ybqx/pages/Login/ModifyPassword.dart'; +import 'package:hyzp_ybqx/services/Storage.dart'; + +import '../../components/commonFun.dart'; +import '../../widget/JdButton.dart'; + +class page8_defaultMap extends StatefulWidget { + page8_defaultMap({Key key}) : super(key: key); + + _LoginPageState createState() => _LoginPageState(); +} + +class _LoginPageState extends State { + bool bSaveDefaultMap = false; + String defaultMapName = ''; + + dispose() { + super.dispose(); + } + + @override + void initState() { + super.initState(); + init(); + } + + void init() async { + // 从磁盘读取变量 + // 是否保存默认地图 + g_bSaveDefaultMap = await Storage.getBool('bSaveDefaultMap'); + g_bSaveDefaultMap = (null == g_bSaveDefaultMap) ? false : g_bSaveDefaultMap; // 默认不保存 + // 用户选择的默认地图名称 + g_defaultMapName = await Storage.getString('defaultMapName'); + g_defaultMapName = (null == g_defaultMapName) ? '' : g_defaultMapName; // 默认为空字符串 + + bSaveDefaultMap = g_bSaveDefaultMap; + // bSaveDefaultMap = false; + // defaultMapName = g_defaultMapName; + // defaultMapName = '高德地图'; + // defaultMapName = getMapChsName('amap'); + defaultMapName = getMapChsName(g_defaultMapName); + setState(() {}); + } + + //////// + static onNullFun() {} + + Widget _getListTile( + title, { + String leadPath = '', + Color leadColor, + Color textColor, + onTapFun = onNullFun, + onLongPressFun = onNullFun, + size = 16.0, + bool bBadge = false, + Widget trailWidget = const Icon(Icons.arrow_forward_ios), + }) { + // print("_getListTile bSaveDefaultMap = $bSaveDefaultMap "); // _getListTile bSaveDefaultMap = false + return Column( + children: [ + ListTile( + leading: bBadge + ? Badge( + position: BadgePosition.topEnd(top: -7, end: -12), + badgeContent: null, + child: Image.asset( + leadPath, + height: ScreenUtil().setHeight(78), + fit: BoxFit.fitHeight, + // color: leadColor, // 无效 + ), + ) + : Image.asset( + leadPath, + height: ScreenUtil().setHeight(78), + fit: BoxFit.fitHeight, + ), + title: Text(title, style: TextStyle(fontSize: size, color: textColor)), + trailing: trailWidget, + contentPadding: EdgeInsets.symmetric(horizontal: 20.0, vertical: 0), + enabled: true, + onTap: onTapFun, + onLongPress: onLongPressFun, + ), + Divider( + height: 1.0, + ), + ], + ); + } + + List _listViewUser = []; + + Color _greenColor = Color.fromRGBO(36, 206, 192, 1); //绿色 + Color _deepBlueColor = Color.fromRGBO(79, 118, 230, 1); //深蓝 + Color _deepGreyColor = Color.fromRGBO(116, 139, 161, 1); //深灰 + Color _ligthBlueColor = Color.fromRGBO(80, 159, 245, 1); //亮蓝 + + OnTap_FaceReg() async { + Navigator.of(context).push(MaterialPageRoute(builder: (context) => FaceReg())); + } + + OnTap_modify_password() { + Navigator.of(context).push(MaterialPageRoute(builder: (context) => ModifyPassword())); + } + + OnTap_Remind() { + setState(() { + bSaveDefaultMap = !bSaveDefaultMap; + print("bSaveDefaultMap = $bSaveDefaultMap"); + }); + } + + Widget getTextField() { + return Container( + width: ScreenUtil().setWidth(400), + child: TextField( + readOnly: !bSaveDefaultMap, + focusNode: FocusNode(), + textAlignVertical: TextAlignVertical(y: 1.0), + controller: TextEditingController.fromValue(TextEditingValue( + text: defaultMapName.toString(), + // 保持光标在最后 + selection: TextSelection.fromPosition(TextPosition( + affinity: TextAffinity.downstream, offset: defaultMapName.toString().length)))), + maxLines: 1, + keyboardType: TextInputType.text, + decoration: InputDecoration( + //contentPadding: EdgeInsets.only(bottom: 16), + // hintText: widget.text, + // border: OutlineInputBorder( + // borderRadius: BorderRadius.circular(30), borderSide: BorderSide.none), + border: UnderlineInputBorder(borderSide: BorderSide(color: Colors.lightBlue)), + focusedBorder: UnderlineInputBorder(borderSide: BorderSide(color: Colors.lightBlue)), + enabledBorder: UnderlineInputBorder(borderSide: BorderSide(color: Colors.lightBlue)), + ), + onChanged: (v) { + defaultMapName = v; + }, + ), + ); + } + + Widget getText(String text) { + return Container( + child: Text(text, + style: TextStyle( + fontSize: 16, + color: Colors.blueAccent, + fontWeight: FontWeight.bold, + decoration: TextDecoration.underline, + )), + ); + } + + //////// + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: PreferredSize( + preferredSize: Size.fromHeight(ScreenUtil().setHeight(173)), // 设置appBar高度 + // 设置appBar高度 + child: AppBar( + automaticallyImplyLeading: false, + centerTitle: true, + titleSpacing: 0.0, + //设置title的左边距 + flexibleSpace: Container( + //SizedBox(height: ScreenUtil().statusBarHeight), //显示顶部状态栏 + // SizedBox(height: ScreenUtil().setHeight(10)), //显示顶部状态栏 + padding: EdgeInsets.only(top: ScreenUtil().statusBarHeight), //留出顶部状态栏高度 + child: Container( + //height: ScreenUtil().setHeight(173), + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.centerLeft, + end: Alignment.centerRight, + colors: [ + Color.fromRGBO(12, 186, 156, 1), + Color.fromRGBO(39, 127, 235, 1), + ], + ), + ), + // decoration: BoxDecoration( + // gradient: LinearGradient(colors: [ + // Color(0xFF0018EB), + // Color(0xFF01C1D9), + // ], begin: Alignment.bottomCenter, end: Alignment.topCenter), + // ), + ), + ), + title: Padding( + padding: EdgeInsets.only(left: 0, right: 0), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + getIconAndTextButton( + iconColor: Colors.white, + iconData: Icons.chevron_left_outlined, + onPress: () { + Navigator.pop(context); + }, + ), + Expanded( + child: Text("默认导航设置", + style: TextStyle(color: Colors.white, fontSize: 20), + textAlign: TextAlign.center, + overflow: TextOverflow.ellipsis), + ), + SizedBox(width: 50), + ], + ), + ), + ), + ), + body: Container( + padding: EdgeInsets.only(top: 30, bottom: 20, left: 20, right: 20), + child: ListView( + children: [ + Center( + child: Container( + margin: EdgeInsets.only(top: 30), + height: ScreenUtil().setWidth(160), + width: ScreenUtil().setWidth(160), + //child: Image.asset('assets/images/user.png', fit: BoxFit.cover), + child: Image.asset('assets/images/ybsthbj.png', fit: BoxFit.fitHeight), + ), + ), + SizedBox(height: 50), + _getListTile('保存默认导航地图', + leadPath: 'assets/images/地图选中.png', + leadColor: _deepGreyColor, + // textColor: bSaveDefaultMap ? Colors.blue : null, + onTapFun: OnTap_Remind, + trailWidget: bSaveDefaultMap + ? Icon(Icons.check_box, color: Colors.blue) + : Icon(Icons.check_box_outline_blank, color: Colors.blueAccent)), + _getListTile('已选默认导航地图:', + leadPath: 'assets/images/用户选择.png', + leadColor: _deepBlueColor, + trailWidget: getText(defaultMapName)), + SizedBox(height: 60), + Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + JdButton( + height: 128, + width: 282, + //height: 126, + text: "确认", + color: Colors.blueAccent, + onTop: () async { + g_bSaveDefaultMap = bSaveDefaultMap; + await Storage.setBool('bSaveDefaultMap', g_bSaveDefaultMap); + if (!g_bSaveDefaultMap) { + await Storage.setString('defaultMapName', ''); + } + Navigator.pop(context); + }, + ), + JdButton( + height: 128, + width: 282, + //height: 126, + text: "取消", + color: Colors.blueAccent, + onTop: () { + Navigator.pop(context); + }, + ) + ], + ), + ], + ), + ), + ); + } +} diff --git a/lib/widget/my_superplayer-0.0.3.dart b/lib/widget/my_superplayer-0.0.3.dart new file mode 100644 index 0000000..9323294 --- /dev/null +++ b/lib/widget/my_superplayer-0.0.3.dart @@ -0,0 +1,739 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:ui'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_screenutil/screen_util.dart'; +import 'package:flutter_superplayer/flutter_superplayer.dart'; +import 'package:flutterptcontrol/flutterptcontrol.dart'; +import 'package:hyzp_ybqx/components/commonFun.dart'; +import 'package:hyzp_ybqx/components/dioFun.dart'; +import 'package:hyzp_ybqx/provider/player_ratio.dart'; +import 'package:hyzp_ybqx/provider/player_region.dart'; +import 'package:provider/provider.dart'; + +import '../components/commonFun.dart'; +import '../services/Storage.dart'; + +const _kControlViewTypes = [kControlViewTypeDefault, kControlViewTypeWithout]; + +class SuperPlayerPage extends StatefulWidget { + SuperPlayerPage( + {@required this.url, + this.id = -2, // 播放点位视频的点位编号,-2 表示播放违章视频 + this.dwip = '', // 点位IP,用于点位视频控制球机方向 + this.loop = 1, + this.title = 'Tencent Player', + Key key}) + : super(key: key); + String dwip; + String url; + String title; + int loop; //设置播放循环,默认播放器的循环次数是1, 即不循环播放。如果设置循环次数0,表示无限循环。 + int id; + + @override + _SuperPlayerPageState createState() => _SuperPlayerPageState(); +} + +class _SuperPlayerPageState extends State with SuperPlayerListener { + SuperPlayerController _playerController = SuperPlayerController(); + + String _sdkVersion = 'Unknown'; + List _logs = []; + bool bFullScreen = false; + + String _controlViewType = _kControlViewTypes.first; + + @override + void dispose() { + Playing = false; + // 云台控制代码:1:停止动作、3:启动雨刷、11:焦距变大、12:焦距变小 + setSphericalCameraDio(id: widget.id, dwip: widget.dwip, cmdCode: 1); + super.dispose(); + } + + @override + void initState() { + super.initState(); + //initPlatformState(); + // Future.delayed(const Duration(milliseconds: 1000), () { + // _playerController.playWithModel(SuperPlayerModel(url: widget.url)); + // setState(() { + // }); + // }); + init(); + } + + Future init() async { + await _playerController.addListener(this); + await initPlatformState(); + if (!mounted) return; + print('mounted = ${mounted}'); + // 开启调试日志 + //await FTXPlayerController.setConsoleEnabled(true); + // 初始化播放器 + //await _controller.initialize(onlyAudio: true); + + await _playerController.uiHideDanmu(); // 隐藏弹幕 + //设置播放循环,默认播放器的循环次数是1, 即不循环播放。如果设置循环次数0,表示无限循环。 + if (0 == widget.loop) { + await _playerController.setLoop(true); + } + + await _playerController.playWithModel(testSuperPlayerModel); + //_controller.play("http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"); + // _controller + // .play('rtmp://125.64.218.67:9901/rtp/gb_play_34020000001320003016_34020000001320003016'); + // 设置循环播放 + //await _controller.setLoop(true); + // 开始播放 + //await _controller.play("http://125.64.218.67:9908/video/2_6063_20210409_140608_川Q31715.mp4"); + } + + SuperPlayerModel get testSuperPlayerModel { + // int appId = 1252463788; + // String fileId = "5285890781763144364"; + + SuperPlayerModel superPlayerModel = SuperPlayerModel( + url: widget.url, + // appId: appId, + // videoId: SuperPlayerVideoId(fileId: fileId), + ); + return superPlayerModel; + } + + // Platform messages are asynchronous, so we initialize in an async method. + Future initPlatformState() async { + String sdkVersion; + // Platform messages may fail, so we use a try/catch PlatformException. + try { + sdkVersion = await FlutterSuperPlayer.sdkVersion; + } on PlatformException { + sdkVersion = 'Failed to get platform version.'; + } + print('sdkVersion = ${sdkVersion}'); + print('mounted = ${mounted}'); + + // If the widget was removed from the tree while the asynchronous platform + // message was in flight, we want to discard the reply rather than calling + // setState to update our non-existent appearance. + if (!mounted) return; + + setState(() { + _sdkVersion = sdkVersion; + }); + } + + void _addLog(String method, dynamic data) { + _logs.add('>>>$method'); + if (data != null) { + _logs.add(data is Map ? json.encode(data) : data); + } + _logs.add(' '); + + setState(() {}); + } + + PlayerRatioProvide playerRatioProvide; + + @override + Widget build(BuildContext context) { + playerRegionProvide = Provider.of(context); + playerRatioProvide = Provider.of(context); + // List listData = getDataListControl2(); + double btnHeight1 = 70; //第一按钮行高度 + double btnHeight2 = 160; //第二按钮行高度 + int btnCount = 4; //每行按钮个数 + // int btnCount3 = listData.length; //每行按钮个数 + var mediaSize = MediaQuery.of(context).size; + + // widget.id:播放点位视频的点位编号,-2 表示播放违章视频 + double btn_left = -2 == widget.id ? 347 : 70; //第一按钮行高度 + double btn_gap = -2 == widget.id ? 104 : 70; //第一按钮行高度 + + //远程控制球机方向按钮外半径和内半径 + double _outerRadius = 270; + double _innerRadius = _outerRadius / 2; + + //double barHeight = bFullScreen ? 0 : MediaQueryData.fromWindow(window).padding.top; + return Scaffold( + appBar: bFullScreen + ? null + : PreferredSize( + preferredSize: Size.fromHeight(ScreenUtil().setHeight(173)), // 设置appBar高度 + // 设置appBar高度 + child: AppBar( + automaticallyImplyLeading: false, + centerTitle: true, + titleSpacing: 0.0, + //设置title的左边距 + flexibleSpace: Container( + //SizedBox(height: ScreenUtil().statusBarHeight), //显示顶部状态栏 + // SizedBox(height: ScreenUtil().setHeight(10)), //显示顶部状态栏 + padding: EdgeInsets.only(top: ScreenUtil().statusBarHeight), //留出顶部状态栏高度 + child: Container( + //height: ScreenUtil().setHeight(173), + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.centerLeft, + end: Alignment.centerRight, + colors: [ + Color.fromRGBO(12, 186, 156, 1), + Color.fromRGBO(39, 127, 235, 1), + ], + ), + ), + // decoration: BoxDecoration( + // gradient: LinearGradient(colors: [ + // Color(0xFF0018EB), + // Color(0xFF01C1D9), + // ], begin: Alignment.bottomCenter, end: Alignment.topCenter), + // ), + ), + ), + title: Padding( + padding: EdgeInsets.only(left: 0, right: 0), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + getIconAndTextButton( + iconColor: Colors.white, + iconData: Icons.chevron_left_outlined, + onPress: () { + getingDwVideo = false; + Navigator.pop(context); + }, + ), + Expanded( + child: Text(widget.title, + style: TextStyle(color: Colors.white, fontSize: 18), + textAlign: TextAlign.center, + overflow: TextOverflow.ellipsis), + ), + SizedBox(width: 50), + ], + ), + ), + ), + ), + body: WillPopScope( + child: Container( + // height: ScreenUtil().screenHeight - + // ScreenUtil().statusBarHeight - + // ScreenUtil().bottomBarHeight, + color: Color.fromRGBO(224, 224, 224, 1), + child: Column( + children: [ + //第2行组件,视频播放区 + Center( + child: Container( + //padding: EdgeInsets.only(top: barHeight), + alignment: Alignment(0, -1), + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.width * (9 / 16), + color: Colors.black, + child: Stack( + children: [ + _playState <= 2 ? getMoreWidget(strokeWidth: 3.0) : SizedBox.shrink(), + SuperPlayerView( + controller: _playerController, + controlViewType: _kControlViewTypes[0], + ), + //_playState < 3 ? SizedBox.shrink() : getMoreWidget(strokeWidth: 3.0), + ], + ), + ), + ), + //第3.1行组件,控制按钮区 + bFullScreen ? SizedBox.shrink() : SizedBox(height: ScreenUtil().setHeight(69)), + bFullScreen + ? SizedBox.shrink() + : Row( + children: [ + SizedBox(width: ScreenUtil().setWidth(btn_left)), + getRoundButton( + //(bPlaying) ? '暂停' : '播放', + text: playerRegionProvide.playerText, + icon: playerRegionProvide.playerIcon, + diameter: 130, + onPress: playOrPause, + ), + SizedBox(width: ScreenUtil().setWidth(btn_gap)), + getRoundButton( + text: '刷新', + icon: Icons.autorenew, + diameter: 130, + onPress: () { + restartPlay(urlnew); + }, + ), + // widget.id:播放点位视频的点位编号,-2 表示播放违章视频 + -2 == widget.id + ? SizedBox.shrink() + : SizedBox(width: ScreenUtil().setWidth(btn_gap)), + -2 == widget.id + ? SizedBox.shrink() + : getRoundButton_image( + text: '雨刷', + image_path: 'assets/images/wiper.png', + diameter: 130, + onPress: () { + // 云台控制代码:1:停止动作、3:启动雨刷、11:焦距变大、12:焦距变小 + setSphericalCameraDio( + id: widget.id, dwip: widget.dwip, cmdCode: 3); + }, + ), + -2 == widget.id + ? SizedBox.shrink() + : SizedBox(width: ScreenUtil().setWidth(btn_gap)), + -2 == widget.id + ? SizedBox.shrink() + : getRoundButton_image( + text: '放大', + image_path: 'assets/images/zoom_in.png', + imageSize: 72, + diameter: 130, + onPress: () { + // 云台控制代码:1:停止动作、3:启动雨刷、11:焦距变大、12:焦距变小 + setSphericalCameraDio( + id: widget.id, dwip: widget.dwip, cmdCode: 11); + }, + ), + -2 == widget.id + ? SizedBox.shrink() + : SizedBox(width: ScreenUtil().setWidth(btn_gap)), + -2 == widget.id + ? SizedBox.shrink() + : getRoundButton_image( + text: '缩小', + image_path: 'assets/images/zoom_out.png', + imageSize: 72, + diameter: 130, + onPress: () { + // 云台控制代码:1:停止动作、3:启动雨刷、11:焦距变大、12:焦距变小 + setSphericalCameraDio( + id: widget.id, dwip: widget.dwip, cmdCode: 12); + }, + ), + ], + ), + bFullScreen ? SizedBox.shrink() : SizedBox(height: ScreenUtil().setHeight(79)), + bFullScreen + ? SizedBox.shrink() + : Container( + height: ScreenUtil().setHeight(2 * _outerRadius), + width: ScreenUtil().setWidth(2 * _outerRadius), + child: PtControlWidget( + // innerRadius: _innerRadius, + // outerRadius: _outerRadius, + // 进行像素单位转换,解决按钮响应错乱问题 + innerRadius: _innerRadius / ScreenUtil().pixelRatio, + outerRadius: _outerRadius / ScreenUtil().pixelRatio, + callback: -2 == widget.id // 播放点位视频的点位编号,-2 表示播放违章视频 + ? null + : (status) { + ///点击回调 + print('status = $status'); + switch (status) { + + /// status == -1 超出范围 + /// status == 0 右 + case 0: + setSphericalCameraDio( + id: widget.id, dwip: widget.dwip, cmdCode: 24); + break; + + /// status == 1 右上 + case 1: + setSphericalCameraDio( + id: widget.id, dwip: widget.dwip, cmdCode: 26); + break; + + ///三、球机位移接口方向代码说明: + // 上:8 下:4 左:2 右:1 左上:10 左下:6 右上:9 右下:5 + + // 云台控制代码:1:停止动作、3:启动雨刷、11:焦距变大、12:焦距变小 + // 21:上移 + // 22:下移 + // 23:左移 + // 24:右移 + // 25:左上移动 + // 26:右上移动 + // 27:左下移动 + // 28:右下移动 + /// status == 2 上 + case 2: + setSphericalCameraDio( + id: widget.id, dwip: widget.dwip, cmdCode: 21); + break; + + /// status == 3 左上 + case 3: + setSphericalCameraDio( + id: widget.id, dwip: widget.dwip, cmdCode: 25); + break; + + /// status == 4 左 + case 4: + setSphericalCameraDio( + id: widget.id, dwip: widget.dwip, cmdCode: 23); + break; + + /// status == 5 左下 + case 5: + setSphericalCameraDio( + id: widget.id, dwip: widget.dwip, cmdCode: 27); + break; + + /// status == 6 下 + case 6: + setSphericalCameraDio( + id: widget.id, dwip: widget.dwip, cmdCode: 22); + break; + + /// status == 7 右下 + case 7: + setSphericalCameraDio( + id: widget.id, dwip: widget.dwip, cmdCode: 28); + break; + + /// status == 8 还原 + case 8: + break; + default: + break; + } + }, + ), + // child: GridView.custom( + // // padding: EdgeInsets.only( + // // left: ScreenUtil().setWidth(35), right: ScreenUtil().setWidth(35)), + // gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + // crossAxisCount: btnCount3, + // mainAxisSpacing: 0, + // crossAxisSpacing: 1, + // childAspectRatio: ratio3, + // ), + // childrenDelegate: SliverChildBuilderDelegate((context, position) { + // return getItemContainer(listData[position]); + // }, childCount: btnCount3)), + ), + //SizedBox(height: ScreenUtil().setHeight(49)), + //Divider(color: Colors.blue), + //第4行组件,分隔栏 + // Container( + // //height: 11, + // height: ScreenUtil().setHeight(28), + // color: Color.fromRGBO(224, 224, 224, 1), + // ), + ], + ), + ), + onWillPop: () { + Playing = false; + getingDwVideo = false; + // 云台控制代码:1:停止动作、3:启动雨刷、11:焦距变大、12:焦距变小 + setSphericalCameraDio(id: widget.id, dwip: widget.dwip, cmdCode: 1); + Navigator.pop(context); //关闭弹框,播放输入视频地址 + }, + ), + ); + } + + @override + void onClickFloatCloseBtn() { + _addLog('onClickFloatCloseBtn', {}); + } + + @override + void onClickSmallReturnBtn() { + _addLog('onClickSmallReturnBtn', {}); + Navigator.maybePop(context); + } + + @override + void onFullScreenChange(bool isFullScreen) { + _addLog('onFullScreenChange', {'isFullScreen': isFullScreen}); + bFullScreen = !bFullScreen; + setState(() {}); + } + + @override + void onPlayProgressChange(int current, int duration) { + _addLog('onPlayProgressChange', {'current': current, 'duration': duration}); + } + + int _playState = 4; + int i = 0; + + @override + void onPlayStateChange(int playState) { + _playState = playState; + i++; + print('$i、playState = $playState'); + _addLog('onPlayStateChange', {'playState': playState}); + setPlayOrPauseIcon(playState); + } + + void setPlayOrPauseIcon(int state) { + //state : 1 播放状态,2 暂停状态 + print('state = $state'); + if (1 == state) { + bPlaying = true; + } else { + bPlaying = false; + } + playerRegionProvide.changePlayerState(bPlaying); + Storage.setString('bPlaying', bPlaying ? 'true' : 'false'); + setState(() {}); + } + + @override + void onStartFloatWindowPlay() { + _addLog('onStartFloatWindowPlay', {}); + } + + //生成圆形按钮部件,基于图标 + Widget getRoundButton( + {double diameter = 144, + double marginVer = 10, + String text, + IconData icon, + double fontSize = 16, + double iconSize = 90, + Color color = const Color.fromRGBO(52, 157, 237, 1), + var onPress}) { + return InkWell( + onTap: onPress, + child: Column( + children: [ + Container( + width: ScreenUtil().setWidth(diameter), + height: ScreenUtil().setHeight(diameter), + alignment: Alignment.center, + child: Icon( + icon, + size: ScreenUtil().setWidth(iconSize), + color: Color.fromRGBO(52, 157, 237, 1), + ), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.all(Radius.circular(200)), + border: Border.all(width: 0, style: BorderStyle.none), + ), + ), + SizedBox(height: ScreenUtil().setHeight(marginVer)), + Text(text, style: TextStyle(fontSize: fontSize, color: Color.fromRGBO(139, 139, 139, 1))), + ], + ), + ); + } + + //生成圆形按钮部件,基于图片 + Widget getRoundButton_image( + {double diameter = 144, + double marginVer = 10, + String text, + String image_path, + double fontSize = 16, + double imageSize = 90, + Color color = const Color.fromRGBO(52, 157, 237, 1), + Color color_bkg = Colors.white, + var onPress}) { + return InkWell( + onTap: onPress, + child: Column( + children: [ + Container( + width: ScreenUtil().setWidth(diameter), + height: ScreenUtil().setHeight(diameter), + alignment: Alignment.center, + child: Image.asset(image_path, + fit: BoxFit.fitWidth, + width: ScreenUtil().setWidth(imageSize), + //height: ScreenUtil().setWidth(iconSize), + color: Color.fromRGBO(52, 157, 237, 1)), + // child: Icon( + // icon, + // size: ScreenUtil().setWidth(iconSize), + // color: Color.fromRGBO(52, 157, 237, 1), + // ), + decoration: BoxDecoration( + color: color_bkg, + borderRadius: BorderRadius.all(Radius.circular(200)), + border: Border.all(width: 0, style: BorderStyle.none), + ), + ), + SizedBox(height: ScreenUtil().setHeight(marginVer)), + Text(text, style: TextStyle(fontSize: fontSize, color: Color.fromRGBO(139, 139, 139, 1))), + ], + ), + ); + } + + bool bPlaying = true; + + void playOrPause() { + //state : 1 播放状态,2 暂停状态 + _playerController.getPlayState().then((state) { + print('state = $state'); + if (1 == state) { + bPlaying = false; + _playerController.pause(); + } else { + bPlaying = true; + _playerController.resume(); + } + playerRegionProvide.changePlayerState(bPlaying); + Storage.setString('bPlaying', bPlaying ? 'true' : 'false'); + setState(() {}); + }); + } + + void restartPlay(String url) async { + _playState = 4; + bPlaying = true; + _playerController.resetPlayer(); + _playerController.resume(); + // //writeCurrentPosFile(); + // await player.stop(); + // await player.reset(); + // await player.setOption(FijkOption.playerCategory, "mediacodec-all-videos", 1); + // await player.setOption(FijkOption.hostCategory, "enable-snapshot", 1); + // await player.setOption(FijkOption.hostCategory, "request-screen-on", 1); + // await player.setOption(FijkOption.hostCategory, "request-audio-focus", 1); + // await player.setOption(FijkOption.hostCategory, "enable-accurate-seek", 1); + // await player.setOption(FijkOption.hostCategory, "max-buffer-size", 500 * 1024); + // await player.setDataSource(url, autoPlay: true).catchError((e) { + // print("setDataSource error: $e"); + // }); + // await player.setLoop(widget.loop); //设置播放循环,默认播放器的循环次数是1, 即不循环播放。如果设置循环次数0,表示无限循环。 + // bPlaying = true; + // setState(() {}); + // playerRegionProvide.changePlayerState(bPlaying); + } + + //生成播放控制区第2行按钮List +// List getDataListControl2() { +// double _diameter = 100; +// double _iconSize = 70; +// double _fontSize = 14; +// double _marginVer = 8; +// +// List list = [ +// // getRoundButton( +// // diameter: _diameter, +// // iconSize: _iconSize, +// // text: '快退', +// // icon: Icons.fast_rewind, +// // onPress: () { +// // fastSeek(false); +// // }, +// // ), +// // getRoundButton( +// // diameter: _diameter, +// // iconSize: _iconSize, +// // text: '快进', +// // icon: Icons.fast_forward, +// // onPress: () { +// // fastSeek(true); +// // }, +// // ), +// getRoundButton( +// diameter: _diameter, +// iconSize: _iconSize, +// fontSize: _fontSize, +// marginVer: _marginVer, +// text: '放大', +// icon: Icons.zoom_in, +// onPress: () { +// //print('Icons.videocam'); +// //_inputDialog(context2); +// if (10 >= playerRatioProvide.scale) { +// playerRatioProvide.changeScale(playerRatioProvide.scale + 0.5); +// } +// }, +// ), +// getRoundButton( +// diameter: _diameter, +// iconSize: _iconSize, +// fontSize: _fontSize, +// marginVer: _marginVer, +// text: '缩小', +// icon: Icons.zoom_out, +// onPress: () { +// //print('Icons.videocam'); +// //_getFileDialog(context2); +// if (1 < playerRatioProvide.scale) { +// playerRatioProvide.changeScale(playerRatioProvide.scale - 0.5); +// } +// }, +// ), +// // _getIconAndTextButton( +// // '还原', +// // Icons.reply, +// // Colors.orange, +// // () { +// // //print('Icons.videocam'); +// // //_getFileDialog(context2); +// // // 更新当前位置 +// // playerRatioProvide.changeScale(1.0); +// // playerRatioProvide.changeOffset(Offset(0, 0)); +// // playerRatioProvide.changeDeltaX(0.0); +// // playerRatioProvide.changeDeltaY(0.0); +// // }, +// // ), +// getRoundButton( +// diameter: _diameter, +// iconSize: _iconSize, +// fontSize: _fontSize, +// marginVer: _marginVer, +// text: '截图', +// icon: Icons.camera_alt, +// onPress: () { +// //通过上面定义的key,才能准确调用该类型的该对象的方法 +// //_myFijkPanelWidgetBuilderStateKey.currentState.takeSnapshot(); +// //_fijkPanelWidgetBuilder.currentState..takeSnapshot(); +// takeSnapshot(); +// }, +// ), +// getRoundButton( +// diameter: _diameter, +// iconSize: _iconSize, +// fontSize: _fontSize, +// marginVer: _marginVer, +// text: '全屏', +// icon: Icons.fullscreen, +// onPress: () { +// //player.enterFullScreen(); +// _playerController.toFullScreen(); +// }, +// ), +// ]; +// return list; +// } + + void takeSnapshot() { + // player.takeSnapShot().then((v) { + // var provider = MemoryImage(v); + // precacheImage(provider, context).then((_) { + // setState(() { + // myImageProvider = provider; + // }); + // }); + // FijkLog.d("get snapshot succeed"); + // + // //Uint8List fileData; + // String fileFath; + // ImageSaver.save('extended_image_cropped_image.jpg', v).then((value) { + // fileFath = value; + // // var fileFath = await ImagePickerSaver.saveFile(fileData: fileData); + // print('my save fileFath : $fileFath'); + // }); + // }).catchError((e) { + // FijkLog.d("get snapshot failed"); + // }); + } +} diff --git a/lib/widget/my_superplayer.dart b/lib/widget/my_superplayer.dart index 48d0749..9713dc1 100644 --- a/lib/widget/my_superplayer.dart +++ b/lib/widget/my_superplayer.dart @@ -3,6 +3,7 @@ import 'dart:convert'; import 'dart:ui'; import 'dart:io'; +import 'package:fijkplayer/fijkplayer.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_screenutil/screen_util.dart'; @@ -17,34 +18,38 @@ import 'package:fijkplayer/fijkplayer.dart'; import '../components/commonFun.dart'; import '../services/Storage.dart'; +import '../my_fijkPanel_fix/my_panel3.dart'; class SuperPlayerPage extends StatefulWidget { SuperPlayerPage( {@required this.url, + this.urlType = 'mp4', // 视频类型:mp4,rtmp this.id = -2, // 播放点位视频的点位编号,-2 表示播放违章视频 this.dwip = '', // 点位IP,用于点位视频控制球机方向 this.loop = 1, this.title = 'Tencent Player', Key key}) : super(key: key); - String dwip; String url; - String title; - int loop; //设置播放循环,默认播放器的循环次数是1, 即不循环播放。如果设置循环次数0,表示无限循环。 + String urlType; int id; + String dwip; + int loop; //设置播放循环,默认播放器的循环次数是1, 即不循环播放。如果设置循环次数0,表示无限循环。 + String title; @override _SuperPlayerPageState createState() => _SuperPlayerPageState(); } -class _SuperPlayerPageState extends State with SuperPlayerListener { - final SuperPlayerController _playerController = SuperPlayerController(); - final FijkPlayer _ijkPlayer = (){ - var player = FijkPlayer(); - player.setOption(FijkOption.playerCategory, "packet-buffering", 0); - player.setOption(FijkOption.formatCategory, "probesize", 1024); - return player; - }(); +class _SuperPlayerPageState extends State + with WidgetsBindingObserver, AutomaticKeepAliveClientMixin { + // SuperPlayerController _playerController = SuperPlayerController(); + + @override + // TODO: implement wantKeepAlive + bool get wantKeepAlive => true; + + final FijkPlayer _playerController = FijkPlayer(); String _sdkVersion = 'Unknown'; List _logs = []; @@ -55,6 +60,7 @@ class _SuperPlayerPageState extends State with SuperPlayerListe Playing = false; // 云台控制代码:1:停止动作、3:启动雨刷、11:焦距变大、12:焦距变小 setSphericalCameraDio(id: widget.id, dwip: widget.dwip, cmdCode: 1); + _playerController.release(); // 必须显示释放视频播放资源,否则即使退出视频播放页面后台还在获取视频流 super.dispose(); _ijkPlayer.release(); _playerController.resetPlayer(); @@ -65,56 +71,113 @@ class _SuperPlayerPageState extends State with SuperPlayerListe @override void initState() { super.initState(); - init(); + //initPlatformState(); + // Future.delayed(const Duration(milliseconds: 1000), () { + // _playerController.playWithModel(SuperPlayerModel(url: widget.url)); + // setState(() { + // }); + // }); + // init(); + + WidgetsBinding.instance.addObserver(this); + + _playerController.setOption(FijkOption.hostCategory, "enable-snapshot", 1); + _playerController.setOption(FijkOption.playerCategory, "mediacodec-all-videos", 1); + startPlay(); } - Future init() async { - await _playerController.addListener(this); - await initPlatformState(); + void startPlay() async { + // await _playerController.addListener(() {}); + if (!mounted) return; + print('mounted = ${mounted}'); - if (_useIJKPlayer) { - await _playerController.removeListener(this); - _ijkPlayer.addListener(_fijkValueListener); - _ijkPlayer.setDataSource(widget.url, autoPlay: true); - } else { - await _playerController.uiHideDanmu(); - if (0 == widget.loop) { - await _playerController.setLoop(true); - } - await _playerController.playWithModel(testSuperPlayerModel); + await _playerController.setLoop(widget.loop); // 0无限循环,1循环一次,2循环两次,以此类推 + await _playerController.setOption(FijkOption.playerCategory, "mediacodec-all-videos", 1); + await _playerController.setOption(FijkOption.hostCategory, "enable-snapshot", 1); + await _playerController.setOption(FijkOption.hostCategory, "request-screen-on", 1); + await _playerController.setOption(FijkOption.hostCategory, "request-audio-focus", 1); + await _playerController.setOption(FijkOption.hostCategory, "enable-accurate-seek", 1); + // 播放“rmtp”等网络视频设置 + if (widget.urlType != 'mp4') { + /// fijkplayer播放点位视频延迟比较大优化-OK,改完后约1秒钟就能打开 - Being + // 是否开启预缓冲,一般直播项目会开启,达到秒开的效果,不过带来了播放丢帧卡顿的体验 + await _playerController.setOption(FijkOption.playerCategory, "packet-buffering", 0); + // 播放前的探测Size,默认是1024K(1M), 改小一点会出画面更快 + // await player.setOption(FijkOption.formatCategory, "probesize", 200); // 1.3S打开 + await _playerController.setOption(FijkOption.formatCategory, "probesize", 1024); // 避免第一帧花屏 + await _playerController.setOption(FijkOption.formatCategory, 'http-detect-range-support', 0); + // 设置播放前的探测时间 1,达到首屏秒开效果 + await _playerController.setOption(FijkOption.formatCategory, "analyzeduration", 1); + // 缩短播放的rtmp视频延迟在1s内 + await _playerController.setOption(FijkOption.formatCategory, "fflags", "nobuffer"); + // 支持硬解: 1 开启, O 关闭 + await _playerController.setOption(FijkOption.playerCategory, "mediacodec-hevc", 1); + + /// fijkplayer播放点位视频延迟比较大优化-OK,改完后半秒钟就能打开 - End + + // await _playerController.setOption(FijkOption.hostCategory, "request-screen-on", 1); + // await _playerController.setOption(FijkOption.hostCategory, "request-audio-focus", 1); } } - bool get _useIJKPlayer { - return Platform.isIOS && widget.url.startsWith("rtmp"); + await _playerController.setDataSource(widget.url, autoPlay: true).catchError((e) { + print("setDataSource error: $e"); + }); + playerRegionProvide.changePlayerState(bPlaying); } + // Future init() async { + // await _playerController.addListener(this); + // await initPlatformState(); + // if (!mounted) return; + // print('mounted = ${mounted}'); + // // 开启调试日志 + // //await FTXPlayerController.setConsoleEnabled(true); + // // 初始化播放器 + // //await _controller.initialize(onlyAudio: true); + // + // await _playerController.uiHideDanmu(); // 隐藏弹幕 + // //设置播放循环,默认播放器的循环次数是1, 即不循环播放。如果设置循环次数0,表示无限循环。 + // if (0 == widget.loop) { + // await _playerController.setLoop(true); + // } + // + // await _playerController.playWithModel(testSuperPlayerModel); + // //_controller.play("http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"); + // // _controller + // // .play('rtmp://125.64.218.67:9901/rtp/gb_play_34020000001320003016_34020000001320003016'); + // // 设置循环播放 + // //await _controller.setLoop(true); + // // 开始播放 + // //await _controller.play("http://125.64.218.67:9908/video/2_6063_20210409_140608_川Q31715.mp4"); + // } + SuperPlayerModel get testSuperPlayerModel { return SuperPlayerModel(url: widget.url); } // Platform messages are asynchronous, so we initialize in an async method. - Future initPlatformState() async { - String sdkVersion; - // Platform messages may fail, so we use a try/catch PlatformException. - try { - sdkVersion = await FlutterSuperPlayer.sdkVersion; - } on PlatformException { - sdkVersion = 'Failed to get platform version.'; - } - print('sdkVersion = ${sdkVersion}'); - print('mounted = ${mounted}'); - - // If the widget was removed from the tree while the asynchronous platform - // message was in flight, we want to discard the reply rather than calling - // setState to update our non-existent appearance. - if (!mounted) return; - - setState(() { - _sdkVersion = sdkVersion; - }); - } + // Future initPlatformState() async { + // String sdkVersion; + // // Platform messages may fail, so we use a try/catch PlatformException. + // try { + // sdkVersion = await FlutterSuperPlayer.sdkVersion; + // } on PlatformException { + // sdkVersion = 'Failed to get platform version.'; + // } + // print('sdkVersion = ${sdkVersion}'); + // print('mounted = ${mounted}'); + // + // // If the widget was removed from the tree while the asynchronous platform + // // message was in flight, we want to discard the reply rather than calling + // // setState to update our non-existent appearance. + // if (!mounted) return; + // + // setState(() { + // _sdkVersion = sdkVersion; + // }); + // } void _addLog(String method, dynamic data) { _logs.add('>>>$method'); @@ -128,16 +191,20 @@ class _SuperPlayerPageState extends State with SuperPlayerListe PlayerRatioProvide playerRatioProvide; + FijkPanelWidgetBuilder _fijkPanelWidgetBuilder; + @override Widget build(BuildContext context) { + _fijkPanelWidgetBuilder = fijkPanel2Builder3(snapShot: true, player: _playerController); playerRegionProvide = Provider.of(context); playerRatioProvide = Provider.of(context); - // List listData = getDataListControl2(); - double btnHeight1 = 70; //第一按钮行高度 - double btnHeight2 = 160; //第二按钮行高度 - int btnCount = 4; //每行按钮个数 - // int btnCount3 = listData.length; //每行按钮个数 - var mediaSize = MediaQuery.of(context).size; + + // // List listData = getDataListControl2(); + // double btnHeight1 = 70; //第一按钮行高度 + // double btnHeight2 = 160; //第二按钮行高度 + // int btnCount = 4; //每行按钮个数 + // // int btnCount3 = listData.length; //每行按钮个数 + // var mediaSize = MediaQuery.of(context).size; // widget.id:播放点位视频的点位编号,-2 表示播放违章视频 double btn_left = -2 == widget.id ? 347 : 70; //第一按钮行高度 @@ -212,7 +279,30 @@ class _SuperPlayerPageState extends State with SuperPlayerListe child: Stack( children: [ _playState <= 2 ? getMoreWidget(strokeWidth: 3.0) : SizedBox.shrink(), - _useIJKPlayer ? FijkView(player: _ijkPlayer, color: Colors.black) : SuperPlayerView(controller: _playerController) + // SuperPlayerView( + // controller: _playerController, + // controlViewType: _kControlViewTypes[0], + // ), + + FijkView( + //设置视频区域背景颜色-OK + color: Colors.black, + player: _playerController, + // panelBuilder: fijkPanel2Builder(snapShot: true), + panelBuilder: _fijkPanelWidgetBuilder, + fsFit: FijkFit.fill, + // panelBuilder: simplestUI, + // panelBuilder: (FijkPlayer player, BuildContext context, + // Size viewSize, Rect texturePos) { + // return CustomFijkPanel( + // player: player, + // buildContext: context, + // viewSize: viewSize, + // texturePos: texturePos); + // }, + ), + + //_playState < 3 ? SizedBox.shrink() : getMoreWidget(strokeWidth: 3.0), ], ), ), @@ -532,21 +622,26 @@ class _SuperPlayerPageState extends State with SuperPlayerListe bool bPlaying = true; - Future playOrPause() async { + void playOrPause() { + // playerRegionProvide.changePlayerState(bPlaying); + // + // if (bPlaying) { + // _playerController.start(); + // } else { + // _playerController.pause(); + // } + // + // setState(() { + // bPlaying = !bPlaying; + // }); + //state : 1 播放状态,2 暂停状态 - int state; - if (_useIJKPlayer) { - state = _ijkPlayer.state == FijkState.started ? 1 : 2; - } else { - state = await _playerController.getPlayState(); - } - - if (1 == state) { + if (FijkState.started == _playerController.state) { bPlaying = false; - _useIJKPlayer ? _ijkPlayer.pause() : _playerController.pause(); + _playerController.pause(); } else { bPlaying = true; - _useIJKPlayer ? _ijkPlayer.start() : _playerController.resume(); + _playerController.start(); } playerRegionProvide.changePlayerState(bPlaying); Storage.setString('bPlaying', bPlaying ? 'true' : 'false'); @@ -556,13 +651,30 @@ class _SuperPlayerPageState extends State with SuperPlayerListe void restartPlay(String url) async { _playState = 4; bPlaying = true; - if (_useIJKPlayer) { - await _ijkPlayer.reset(); - _ijkPlayer.setDataSource(widget.url, autoPlay: true); - } else { - _playerController.resetPlayer(); - _playerController.resume(); - } + + await _playerController.stop(); + await _playerController.reset(); + startPlay(); + + // _playerController.resetPlayer(); + // _playerController.resume(); + + // //writeCurrentPosFile(); + // await player.stop(); + // await player.reset(); + // await player.setOption(FijkOption.playerCategory, "mediacodec-all-videos", 1); + // await player.setOption(FijkOption.hostCategory, "enable-snapshot", 1); + // await player.setOption(FijkOption.hostCategory, "request-screen-on", 1); + // await player.setOption(FijkOption.hostCategory, "request-audio-focus", 1); + // await player.setOption(FijkOption.hostCategory, "enable-accurate-seek", 1); + // await player.setOption(FijkOption.hostCategory, "max-buffer-size", 500 * 1024); + // await player.setDataSource(url, autoPlay: true).catchError((e) { + // print("setDataSource error: $e"); + // }); + // await player.setLoop(widget.loop); //设置播放循环,默认播放器的循环次数是1, 即不循环播放。如果设置循环次数0,表示无限循环。 + // bPlaying = true; + // setState(() {}); + // playerRegionProvide.changePlayerState(bPlaying); } //生成播放控制区第2行按钮List diff --git a/pubspec.lock b/pubspec.lock index 5eab507..16234e5 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -356,6 +356,13 @@ packages: relative: true source: path version: "0.0.3" + flutter_svg: + dependency: "direct main" + description: + name: flutter_svg + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.19.1" flutter_swiper: dependency: "direct main" description: @@ -387,6 +394,27 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "4.0.1" + geolocator: + dependency: "direct main" + description: + name: geolocator + url: "https://pub.flutter-io.cn" + source: hosted + version: "6.2.1" + geolocator_platform_interface: + dependency: transitive + description: + name: geolocator_platform_interface + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.0.9" + geolocator_web: + dependency: transitive + description: + name: geolocator_web + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.0.1" get_it: dependency: "direct main" description: @@ -457,6 +485,13 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "1.1.1" + map_launcher: + dependency: "direct main" + description: + name: map_launcher + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.1.3+1" matcher: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index f47a770..67b6d86 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -21,7 +21,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.4.33+20220515 +version: 1.6.5+20250527 environment: sdk: ">=2.7.0 <3.0.0" @@ -36,7 +36,7 @@ dependencies: cupertino_icons: ^1.0.0 # hyzp_ybqx00_yibin Project Adds - fijkplayer: ^0.8.7 + fijkplayer: ^0.8.8 path_provider: ^1.6.14 permission_handler: ^5.0.1+1 @@ -108,6 +108,11 @@ dependencies: # 添加水印插件 disable_screenshots: ^0.1.0 + # 导航相关插件 + map_launcher: ^1.1.3+1 + flutter_svg: ^0.19.1 + geolocator: ^6.2.1 + dev_dependencies: flutter_test: sdk: flutter