You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

829 lines
24 KiB
Dart

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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<StatefulWidget> 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<StatefulWidget> createState() {
MyFijkPanelWidgetBuilderState createState() {
return MyFijkPanelWidgetBuilderState(player);
}
}
class MyFijkPanelWidgetBuilderState extends State<MyFijkPanelWidgetBuilder>
with SingleTickerProviderStateMixin {
FijkPlayer player;
MyFijkPanelWidgetBuilderState(@required this.player) {
_oldFullScreen = player.value.fullScreen;
}
PlayerRatioProvide playerRatioProvide;
AnimationController _controller;
Animation<Offset> _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<double> _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: <Widget>[
buildPlayButton(context, height),
buildTimeText(context, height),
Expanded(child: buildSlider(context)),
buildFullScreenButton(context, height),
],
);
} else {
return Row(
children: <Widget>[
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: <Widget>[
Expanded(child: centerChild),
Padding(
padding: EdgeInsets.only(left: 10, right: 10, top: 8, bottom: 8),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
IconButton(
padding: EdgeInsets.all(0),
color: Color(0xFFFFFFFF),
icon: Icon(Icons.camera_alt),
onPressed: () {
takeSnapshot();
},
),
],
),
)
],
);
}
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
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<Offset>(
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<PlayerRatioProvide>(context);
Rect rect = panelRect();
List ws = <Widget>[];
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),
);
}
}