mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-06 09:13:48 +08:00
refa: video (#1555)
* refa: video [skip ci] * fix: scroll [skip ci] * mod: only left click * downgrade * refa: background play & wakelock [skip ci] * fix: subtitle [skip ci] * upgrade deps Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me> * mod: long press * tweak Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me> * fix [skip ci] Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me> * use right pos Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me> * delay showing Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me> * fix: null danmaku * remove Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me> --------- Co-authored-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
committed by
GitHub
parent
4cf1c25b36
commit
9d747c8e2c
905
lib/common/widgets/gesture/interactive_viewer.dart
Normal file
905
lib/common/widgets/gesture/interactive_viewer.dart
Normal file
@@ -0,0 +1,905 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:io' show Platform;
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:flutter/foundation.dart' show clampDouble;
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/physics.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:vector_math/vector_math_64.dart' show Quad, Vector3;
|
||||
|
||||
class MouseInteractiveViewer extends StatefulWidget {
|
||||
const MouseInteractiveViewer({
|
||||
super.key,
|
||||
this.clipBehavior = Clip.hardEdge,
|
||||
this.panAxis = PanAxis.free,
|
||||
this.boundaryMargin = EdgeInsets.zero,
|
||||
this.constrained = true,
|
||||
this.maxScale = 2.5,
|
||||
this.minScale = 0.8,
|
||||
this.interactionEndFrictionCoefficient = _kDrag,
|
||||
this.pointerSignalFallback,
|
||||
this.onPointerPanZoomUpdate,
|
||||
this.onPointerPanZoomEnd,
|
||||
this.onPointerDown,
|
||||
this.onInteractionEnd,
|
||||
this.onInteractionStart,
|
||||
this.onInteractionUpdate,
|
||||
this.panEnabled = true,
|
||||
this.scaleEnabled = true,
|
||||
this.scaleFactor = kDefaultMouseScrollToScaleFactor,
|
||||
this.transformationController,
|
||||
this.alignment,
|
||||
this.trackpadScrollCausesScale = false,
|
||||
|
||||
required this.childKey,
|
||||
required this.child,
|
||||
}) : assert(minScale > 0),
|
||||
assert(interactionEndFrictionCoefficient > 0),
|
||||
assert(maxScale > 0),
|
||||
assert(maxScale >= minScale);
|
||||
|
||||
final Alignment? alignment;
|
||||
final Clip clipBehavior;
|
||||
final PanAxis panAxis;
|
||||
final EdgeInsets boundaryMargin;
|
||||
final Widget child;
|
||||
final bool constrained;
|
||||
final bool panEnabled;
|
||||
final bool scaleEnabled;
|
||||
final bool trackpadScrollCausesScale;
|
||||
final double scaleFactor;
|
||||
final double maxScale;
|
||||
final double minScale;
|
||||
final double interactionEndFrictionCoefficient;
|
||||
final PointerSignalEventListener? pointerSignalFallback;
|
||||
final PointerPanZoomUpdateEventListener? onPointerPanZoomUpdate;
|
||||
final PointerPanZoomEndEventListener? onPointerPanZoomEnd;
|
||||
final PointerDownEventListener? onPointerDown;
|
||||
final GestureScaleEndCallback? onInteractionEnd;
|
||||
final GestureScaleStartCallback? onInteractionStart;
|
||||
final GestureScaleUpdateCallback? onInteractionUpdate;
|
||||
final TransformationController? transformationController;
|
||||
final GlobalKey childKey;
|
||||
|
||||
static const double _kDrag = 0.0000135;
|
||||
|
||||
@override
|
||||
State<MouseInteractiveViewer> createState() => _MouseInteractiveViewerState();
|
||||
}
|
||||
|
||||
class _MouseInteractiveViewerState extends State<MouseInteractiveViewer>
|
||||
with TickerProviderStateMixin {
|
||||
late TransformationController _transformer =
|
||||
widget.transformationController ?? TransformationController();
|
||||
|
||||
final GlobalKey _parentKey = GlobalKey();
|
||||
Animation<Offset>? _animation;
|
||||
Animation<double>? _scaleAnimation;
|
||||
late Offset _scaleAnimationFocalPoint;
|
||||
late AnimationController _controller;
|
||||
late AnimationController _scaleController;
|
||||
Axis? _currentAxis;
|
||||
Offset? _referenceFocalPoint;
|
||||
double? _scaleStart;
|
||||
double? _rotationStart = 0.0;
|
||||
double _currentRotation = 0.0;
|
||||
_GestureType? _gestureType;
|
||||
|
||||
static final gestureSettings = DeviceGestureSettings(
|
||||
touchSlop: Platform.isIOS ? 9 : 4,
|
||||
);
|
||||
|
||||
late final _scaleGestureRecognizer =
|
||||
ScaleGestureRecognizer(
|
||||
debugOwner: this,
|
||||
allowedButtonsFilter: (buttons) => buttons == kPrimaryButton,
|
||||
trackpadScrollToScaleFactor: Offset(0, -1 / widget.scaleFactor),
|
||||
trackpadScrollCausesScale: widget.trackpadScrollCausesScale,
|
||||
)
|
||||
..gestureSettings = gestureSettings
|
||||
..onStart = _onScaleStart
|
||||
..onUpdate = _onScaleUpdate
|
||||
..onEnd = _onScaleEnd;
|
||||
|
||||
final bool _rotateEnabled = false;
|
||||
|
||||
Rect get _boundaryRect {
|
||||
assert(widget.childKey.currentContext != null);
|
||||
final RenderBox childRenderBox =
|
||||
widget.childKey.currentContext!.findRenderObject()! as RenderBox;
|
||||
final Size childSize = childRenderBox.size;
|
||||
final Rect boundaryRect = widget.boundaryMargin.inflateRect(
|
||||
Offset.zero & childSize,
|
||||
);
|
||||
assert(
|
||||
!boundaryRect.isEmpty,
|
||||
"InteractiveViewer's child must have nonzero dimensions.",
|
||||
);
|
||||
assert(
|
||||
boundaryRect.isFinite ||
|
||||
(boundaryRect.left.isInfinite &&
|
||||
boundaryRect.top.isInfinite &&
|
||||
boundaryRect.right.isInfinite &&
|
||||
boundaryRect.bottom.isInfinite),
|
||||
'boundaryRect must either be infinite in all directions or finite in all directions.',
|
||||
);
|
||||
return boundaryRect;
|
||||
}
|
||||
|
||||
Rect get _viewport {
|
||||
assert(_parentKey.currentContext != null);
|
||||
final RenderBox parentRenderBox =
|
||||
_parentKey.currentContext!.findRenderObject()! as RenderBox;
|
||||
return Offset.zero & parentRenderBox.size;
|
||||
}
|
||||
|
||||
Matrix4 _matrixTranslate(Matrix4 matrix, Offset translation) {
|
||||
if (translation == Offset.zero) {
|
||||
return matrix.clone();
|
||||
}
|
||||
|
||||
final Offset alignedTranslation;
|
||||
|
||||
if (_currentAxis != null) {
|
||||
alignedTranslation = switch (widget.panAxis) {
|
||||
PanAxis.horizontal => _alignAxis(translation, Axis.horizontal),
|
||||
PanAxis.vertical => _alignAxis(translation, Axis.vertical),
|
||||
PanAxis.aligned => _alignAxis(translation, _currentAxis!),
|
||||
PanAxis.free => translation,
|
||||
};
|
||||
} else {
|
||||
alignedTranslation = translation;
|
||||
}
|
||||
|
||||
final Matrix4 nextMatrix = matrix.clone()
|
||||
..translateByDouble(alignedTranslation.dx, alignedTranslation.dy, 0, 1);
|
||||
|
||||
final Quad nextViewport = _transformViewport(nextMatrix, _viewport);
|
||||
|
||||
if (_boundaryRect.isInfinite) {
|
||||
return nextMatrix;
|
||||
}
|
||||
|
||||
final Quad boundariesAabbQuad = _getAxisAlignedBoundingBoxWithRotation(
|
||||
_boundaryRect,
|
||||
_currentRotation,
|
||||
);
|
||||
|
||||
final Offset offendingDistance = _exceedsBy(
|
||||
boundariesAabbQuad,
|
||||
nextViewport,
|
||||
);
|
||||
if (offendingDistance == Offset.zero) {
|
||||
return nextMatrix;
|
||||
}
|
||||
|
||||
final Offset nextTotalTranslation = _getMatrixTranslation(nextMatrix);
|
||||
final double currentScale = matrix.getMaxScaleOnAxis();
|
||||
final Offset correctedTotalTranslation = Offset(
|
||||
nextTotalTranslation.dx - offendingDistance.dx * currentScale,
|
||||
nextTotalTranslation.dy - offendingDistance.dy * currentScale,
|
||||
);
|
||||
final Matrix4 correctedMatrix = matrix.clone()
|
||||
..setTranslation(
|
||||
Vector3(
|
||||
correctedTotalTranslation.dx,
|
||||
correctedTotalTranslation.dy,
|
||||
0.0,
|
||||
),
|
||||
);
|
||||
|
||||
final Quad correctedViewport = _transformViewport(
|
||||
correctedMatrix,
|
||||
_viewport,
|
||||
);
|
||||
final Offset offendingCorrectedDistance = _exceedsBy(
|
||||
boundariesAabbQuad,
|
||||
correctedViewport,
|
||||
);
|
||||
if (offendingCorrectedDistance == Offset.zero) {
|
||||
return correctedMatrix;
|
||||
}
|
||||
|
||||
if (offendingCorrectedDistance.dx != 0.0 &&
|
||||
offendingCorrectedDistance.dy != 0.0) {
|
||||
return matrix.clone();
|
||||
}
|
||||
|
||||
final Offset unidirectionalCorrectedTotalTranslation = Offset(
|
||||
offendingCorrectedDistance.dx == 0.0 ? correctedTotalTranslation.dx : 0.0,
|
||||
offendingCorrectedDistance.dy == 0.0 ? correctedTotalTranslation.dy : 0.0,
|
||||
);
|
||||
return matrix.clone()..setTranslation(
|
||||
Vector3(
|
||||
unidirectionalCorrectedTotalTranslation.dx,
|
||||
unidirectionalCorrectedTotalTranslation.dy,
|
||||
0.0,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Matrix4 _matrixScale(Matrix4 matrix, double scale) {
|
||||
if (scale == 1.0) {
|
||||
return matrix.clone();
|
||||
}
|
||||
assert(scale != 0.0);
|
||||
|
||||
final double currentScale = _transformer.value.getMaxScaleOnAxis();
|
||||
final double totalScale = math.max(
|
||||
currentScale * scale,
|
||||
math.max(
|
||||
_viewport.width / _boundaryRect.width,
|
||||
_viewport.height / _boundaryRect.height,
|
||||
),
|
||||
);
|
||||
final double clampedTotalScale = clampDouble(
|
||||
totalScale,
|
||||
widget.minScale,
|
||||
widget.maxScale,
|
||||
);
|
||||
final double clampedScale = clampedTotalScale / currentScale;
|
||||
return matrix.clone()
|
||||
..scaleByDouble(clampedScale, clampedScale, clampedScale, 1);
|
||||
}
|
||||
|
||||
Matrix4 _matrixRotate(Matrix4 matrix, double rotation, Offset focalPoint) {
|
||||
if (rotation == 0) {
|
||||
return matrix.clone();
|
||||
}
|
||||
final Offset focalPointScene = _transformer.toScene(focalPoint);
|
||||
return matrix.clone()
|
||||
..translateByDouble(focalPointScene.dx, focalPointScene.dy, 0, 1)
|
||||
..rotateZ(-rotation)
|
||||
..translateByDouble(-focalPointScene.dx, -focalPointScene.dy, 0, 1);
|
||||
}
|
||||
|
||||
bool _gestureIsSupported(_GestureType? gestureType) {
|
||||
return switch (gestureType) {
|
||||
_GestureType.rotate => _rotateEnabled,
|
||||
_GestureType.scale => widget.scaleEnabled,
|
||||
_GestureType.pan || null => widget.panEnabled,
|
||||
};
|
||||
}
|
||||
|
||||
_GestureType _getGestureType(ScaleUpdateDetails details) {
|
||||
final double scale = !widget.scaleEnabled ? 1.0 : details.scale;
|
||||
final double rotation = !_rotateEnabled ? 0.0 : details.rotation;
|
||||
if ((scale - 1).abs() > rotation.abs()) {
|
||||
return _GestureType.scale;
|
||||
} else if (rotation != 0.0) {
|
||||
return _GestureType.rotate;
|
||||
} else {
|
||||
return _GestureType.pan;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle the start of a gesture. All of pan, scale, and rotate are handled
|
||||
// with GestureDetector's scale gesture.
|
||||
void _onScaleStart(ScaleStartDetails details) {
|
||||
widget.onInteractionStart?.call(details);
|
||||
|
||||
if (_controller.isAnimating) {
|
||||
_controller
|
||||
..stop()
|
||||
..reset();
|
||||
_animation?.removeListener(_handleInertiaAnimation);
|
||||
_animation = null;
|
||||
}
|
||||
if (_scaleController.isAnimating) {
|
||||
_scaleController
|
||||
..stop()
|
||||
..reset();
|
||||
_scaleAnimation?.removeListener(_handleScaleAnimation);
|
||||
_scaleAnimation = null;
|
||||
}
|
||||
|
||||
_gestureType = null;
|
||||
_currentAxis = null;
|
||||
_scaleStart = _transformer.value.getMaxScaleOnAxis();
|
||||
_referenceFocalPoint = _transformer.toScene(details.localFocalPoint);
|
||||
_rotationStart = _currentRotation;
|
||||
}
|
||||
|
||||
// Handle an update to an ongoing gesture. All of pan, scale, and rotate are
|
||||
// handled with GestureDetector's scale gesture.
|
||||
void _onScaleUpdate(ScaleUpdateDetails details) {
|
||||
final double scale = _transformer.value.getMaxScaleOnAxis();
|
||||
_scaleAnimationFocalPoint = details.localFocalPoint;
|
||||
final Offset focalPointScene = _transformer.toScene(
|
||||
details.localFocalPoint,
|
||||
);
|
||||
|
||||
if (_gestureType == _GestureType.pan) {
|
||||
// When a gesture first starts, it sometimes has no change in scale and
|
||||
// rotation despite being a two-finger gesture. Here the gesture is
|
||||
// allowed to be reinterpreted as its correct type after originally
|
||||
// being marked as a pan.
|
||||
_gestureType = _getGestureType(details);
|
||||
} else {
|
||||
_gestureType ??= _getGestureType(details);
|
||||
}
|
||||
if (!_gestureIsSupported(_gestureType)) {
|
||||
widget.onInteractionUpdate?.call(details);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (_gestureType!) {
|
||||
case _GestureType.scale:
|
||||
assert(_scaleStart != null);
|
||||
// details.scale gives us the amount to change the scale as of the
|
||||
// start of this gesture, so calculate the amount to scale as of the
|
||||
// previous call to _onScaleUpdate.
|
||||
final double desiredScale = _scaleStart! * details.scale;
|
||||
final double scaleChange = desiredScale / scale;
|
||||
_transformer.value = _matrixScale(_transformer.value, scaleChange);
|
||||
|
||||
// While scaling, translate such that the user's two fingers stay on
|
||||
// the same places in the scene. That means that the focal point of
|
||||
// the scale should be on the same place in the scene before and after
|
||||
// the scale.
|
||||
final Offset focalPointSceneScaled = _transformer.toScene(
|
||||
details.localFocalPoint,
|
||||
);
|
||||
_transformer.value = _matrixTranslate(
|
||||
_transformer.value,
|
||||
focalPointSceneScaled - _referenceFocalPoint!,
|
||||
);
|
||||
|
||||
// details.localFocalPoint should now be at the same location as the
|
||||
// original _referenceFocalPoint point. If it's not, that's because
|
||||
// the translate came in contact with a boundary. In that case, update
|
||||
// _referenceFocalPoint so subsequent updates happen in relation to
|
||||
// the new effective focal point.
|
||||
final Offset focalPointSceneCheck = _transformer.toScene(
|
||||
details.localFocalPoint,
|
||||
);
|
||||
if (_round(_referenceFocalPoint!) != _round(focalPointSceneCheck)) {
|
||||
_referenceFocalPoint = focalPointSceneCheck;
|
||||
}
|
||||
|
||||
case _GestureType.rotate:
|
||||
if (details.rotation == 0.0) {
|
||||
widget.onInteractionUpdate?.call(details);
|
||||
return;
|
||||
}
|
||||
final double desiredRotation = _rotationStart! + details.rotation;
|
||||
_transformer.value = _matrixRotate(
|
||||
_transformer.value,
|
||||
_currentRotation - desiredRotation,
|
||||
details.localFocalPoint,
|
||||
);
|
||||
_currentRotation = desiredRotation;
|
||||
|
||||
case _GestureType.pan:
|
||||
assert(_referenceFocalPoint != null);
|
||||
// details may have a change in scale here when scaleEnabled is false.
|
||||
// In an effort to keep the behavior similar whether or not scaleEnabled
|
||||
// is true, these gestures are thrown away.
|
||||
if (details.scale != 1.0) {
|
||||
widget.onInteractionUpdate?.call(details);
|
||||
return;
|
||||
}
|
||||
_currentAxis ??= _getPanAxis(_referenceFocalPoint!, focalPointScene);
|
||||
// Translate so that the same point in the scene is underneath the
|
||||
// focal point before and after the movement.
|
||||
final Offset translationChange =
|
||||
focalPointScene - _referenceFocalPoint!;
|
||||
_transformer.value = _matrixTranslate(
|
||||
_transformer.value,
|
||||
translationChange,
|
||||
);
|
||||
_referenceFocalPoint = _transformer.toScene(details.localFocalPoint);
|
||||
}
|
||||
widget.onInteractionUpdate?.call(details);
|
||||
}
|
||||
|
||||
// Handle the end of a gesture of _GestureType. All of pan, scale, and rotate
|
||||
// are handled with GestureDetector's scale gesture.
|
||||
void _onScaleEnd(ScaleEndDetails details) {
|
||||
widget.onInteractionEnd?.call(details);
|
||||
_scaleStart = null;
|
||||
_rotationStart = null;
|
||||
_referenceFocalPoint = null;
|
||||
|
||||
_animation?.removeListener(_handleInertiaAnimation);
|
||||
_scaleAnimation?.removeListener(_handleScaleAnimation);
|
||||
_controller.reset();
|
||||
_scaleController.reset();
|
||||
|
||||
if (!_gestureIsSupported(_gestureType)) {
|
||||
_currentAxis = null;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (_gestureType) {
|
||||
case _GestureType.pan:
|
||||
if (details.velocity.pixelsPerSecond.distance < kMinFlingVelocity) {
|
||||
_currentAxis = null;
|
||||
return;
|
||||
}
|
||||
final Vector3 translationVector = _transformer.value.getTranslation();
|
||||
final Offset translation = Offset(
|
||||
translationVector.x,
|
||||
translationVector.y,
|
||||
);
|
||||
final FrictionSimulation frictionSimulationX = FrictionSimulation(
|
||||
widget.interactionEndFrictionCoefficient,
|
||||
translation.dx,
|
||||
details.velocity.pixelsPerSecond.dx,
|
||||
);
|
||||
final FrictionSimulation frictionSimulationY = FrictionSimulation(
|
||||
widget.interactionEndFrictionCoefficient,
|
||||
translation.dy,
|
||||
details.velocity.pixelsPerSecond.dy,
|
||||
);
|
||||
final double tFinal = _getFinalTime(
|
||||
details.velocity.pixelsPerSecond.distance,
|
||||
widget.interactionEndFrictionCoefficient,
|
||||
);
|
||||
_animation =
|
||||
Tween<Offset>(
|
||||
begin: translation,
|
||||
end: Offset(
|
||||
frictionSimulationX.finalX,
|
||||
frictionSimulationY.finalX,
|
||||
),
|
||||
).animate(
|
||||
CurvedAnimation(parent: _controller, curve: Curves.decelerate),
|
||||
)
|
||||
..addListener(_handleInertiaAnimation);
|
||||
_controller
|
||||
..duration = Duration(milliseconds: (tFinal * 1000).round())
|
||||
..forward();
|
||||
case _GestureType.scale:
|
||||
if (details.scaleVelocity.abs() < 0.1) {
|
||||
_currentAxis = null;
|
||||
return;
|
||||
}
|
||||
final double scale = _transformer.value.getMaxScaleOnAxis();
|
||||
final FrictionSimulation frictionSimulation = FrictionSimulation(
|
||||
widget.interactionEndFrictionCoefficient * widget.scaleFactor,
|
||||
scale,
|
||||
details.scaleVelocity / 10,
|
||||
);
|
||||
final double tFinal = _getFinalTime(
|
||||
details.scaleVelocity.abs(),
|
||||
widget.interactionEndFrictionCoefficient,
|
||||
effectivelyMotionless: 0.1,
|
||||
);
|
||||
_scaleAnimation =
|
||||
Tween<double>(
|
||||
begin: scale,
|
||||
end: frictionSimulation.x(tFinal),
|
||||
).animate(
|
||||
CurvedAnimation(
|
||||
parent: _scaleController,
|
||||
curve: Curves.decelerate,
|
||||
),
|
||||
)
|
||||
..addListener(_handleScaleAnimation);
|
||||
_scaleController
|
||||
..duration = Duration(milliseconds: (tFinal * 1000).round())
|
||||
..forward();
|
||||
case _GestureType.rotate || null:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void _receivedPointerSignal(PointerSignalEvent event) {
|
||||
final Offset local = event.localPosition;
|
||||
final Offset global = event.position;
|
||||
final double scaleChange;
|
||||
if (event is PointerScrollEvent) {
|
||||
if (event.kind == PointerDeviceKind.trackpad) {
|
||||
widget.onInteractionStart?.call(
|
||||
ScaleStartDetails(focalPoint: global, localFocalPoint: local),
|
||||
);
|
||||
|
||||
final Offset localDelta = PointerEvent.transformDeltaViaPositions(
|
||||
untransformedEndPosition: global + event.scrollDelta,
|
||||
untransformedDelta: event.scrollDelta,
|
||||
transform: event.transform,
|
||||
);
|
||||
|
||||
final Offset focalPointScene = _transformer.toScene(local);
|
||||
final Offset newFocalPointScene = _transformer.toScene(
|
||||
local - localDelta,
|
||||
);
|
||||
|
||||
_transformer.value = _matrixTranslate(
|
||||
_transformer.value,
|
||||
newFocalPointScene - focalPointScene,
|
||||
);
|
||||
|
||||
widget.onInteractionUpdate?.call(
|
||||
ScaleUpdateDetails(
|
||||
focalPoint: global - event.scrollDelta,
|
||||
localFocalPoint: local - localDelta,
|
||||
focalPointDelta: -localDelta,
|
||||
),
|
||||
);
|
||||
widget.onInteractionEnd?.call(ScaleEndDetails());
|
||||
return;
|
||||
}
|
||||
_handlePointerScrollEvent(event);
|
||||
return;
|
||||
} else if (event is PointerScaleEvent) {
|
||||
scaleChange = event.scale;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
widget.onInteractionStart?.call(
|
||||
ScaleStartDetails(focalPoint: global, localFocalPoint: local),
|
||||
);
|
||||
|
||||
if (!_gestureIsSupported(_GestureType.scale)) {
|
||||
widget.onInteractionUpdate?.call(
|
||||
ScaleUpdateDetails(
|
||||
focalPoint: global,
|
||||
localFocalPoint: local,
|
||||
scale: scaleChange,
|
||||
),
|
||||
);
|
||||
widget.onInteractionEnd?.call(ScaleEndDetails());
|
||||
return;
|
||||
}
|
||||
|
||||
final Offset focalPointScene = _transformer.toScene(local);
|
||||
_transformer.value = _matrixScale(_transformer.value, scaleChange);
|
||||
|
||||
// After scaling, translate such that the event's position is at the
|
||||
// same scene point before and after the scale.
|
||||
final Offset focalPointSceneScaled = _transformer.toScene(local);
|
||||
_transformer.value = _matrixTranslate(
|
||||
_transformer.value,
|
||||
focalPointSceneScaled - focalPointScene,
|
||||
);
|
||||
|
||||
widget.onInteractionUpdate?.call(
|
||||
ScaleUpdateDetails(
|
||||
focalPoint: global,
|
||||
localFocalPoint: local,
|
||||
scale: scaleChange,
|
||||
),
|
||||
);
|
||||
widget.onInteractionEnd?.call(ScaleEndDetails());
|
||||
}
|
||||
|
||||
void _handlePointerScrollEvent(PointerScrollEvent event) {
|
||||
final Offset local = event.localPosition;
|
||||
final Offset global = event.position;
|
||||
|
||||
if (_gestureIsSupported(_GestureType.scale)) {
|
||||
late final shift = HardwareKeyboard.instance.isShiftPressed;
|
||||
if (HardwareKeyboard.instance.isControlPressed) {
|
||||
_handleMouseWheelScale(event, local, global);
|
||||
return;
|
||||
} else if (shift || HardwareKeyboard.instance.isAltPressed) {
|
||||
_handleMouseWheelPanAsScale(event, local, global, shift);
|
||||
return;
|
||||
} else {
|
||||
widget.pointerSignalFallback?.call(event);
|
||||
}
|
||||
}
|
||||
widget.onInteractionUpdate?.call(
|
||||
ScaleUpdateDetails(
|
||||
focalPoint: global,
|
||||
localFocalPoint: local,
|
||||
scale: math.exp(-event.scrollDelta.dy / widget.scaleFactor),
|
||||
),
|
||||
);
|
||||
widget.onInteractionEnd?.call(ScaleEndDetails());
|
||||
}
|
||||
|
||||
void _handleMouseWheelScale(
|
||||
PointerScrollEvent event,
|
||||
Offset local,
|
||||
Offset global,
|
||||
) {
|
||||
final double scaleChange = math.exp(
|
||||
-event.scrollDelta.dy / widget.scaleFactor,
|
||||
);
|
||||
final Offset focalPointScene = _transformer.toScene(local);
|
||||
_transformer.value = _matrixScale(_transformer.value, scaleChange);
|
||||
|
||||
final Offset focalPointSceneScaled = _transformer.toScene(local);
|
||||
_transformer.value = _matrixTranslate(
|
||||
_transformer.value,
|
||||
focalPointSceneScaled - focalPointScene,
|
||||
);
|
||||
|
||||
widget.onInteractionUpdate?.call(
|
||||
ScaleUpdateDetails(
|
||||
focalPoint: global,
|
||||
localFocalPoint: local,
|
||||
scale: scaleChange,
|
||||
),
|
||||
);
|
||||
widget.onInteractionEnd?.call(ScaleEndDetails());
|
||||
}
|
||||
|
||||
void _handleMouseWheelPanAsScale(
|
||||
PointerScrollEvent event,
|
||||
Offset local,
|
||||
Offset global,
|
||||
bool flip,
|
||||
) {
|
||||
final Offset translation = flip
|
||||
? event.scrollDelta.flip
|
||||
: event.scrollDelta;
|
||||
|
||||
final Offset focalPointScene = _transformer.toScene(local);
|
||||
final Offset newFocalPointScene = _transformer.toScene(local - translation);
|
||||
|
||||
_transformer.value = _matrixTranslate(
|
||||
_transformer.value,
|
||||
newFocalPointScene - focalPointScene,
|
||||
);
|
||||
}
|
||||
|
||||
void _handleInertiaAnimation() {
|
||||
if (!_controller.isAnimating) {
|
||||
_currentAxis = null;
|
||||
_animation?.removeListener(_handleInertiaAnimation);
|
||||
_animation = null;
|
||||
_controller.reset();
|
||||
return;
|
||||
}
|
||||
final Vector3 translationVector = _transformer.value.getTranslation();
|
||||
final Offset translation = Offset(translationVector.x, translationVector.y);
|
||||
_transformer.value = _matrixTranslate(
|
||||
_transformer.value,
|
||||
_transformer.toScene(_animation!.value) -
|
||||
_transformer.toScene(translation),
|
||||
);
|
||||
}
|
||||
|
||||
void _handleScaleAnimation() {
|
||||
if (!_scaleController.isAnimating) {
|
||||
_currentAxis = null;
|
||||
_scaleAnimation?.removeListener(_handleScaleAnimation);
|
||||
_scaleAnimation = null;
|
||||
_scaleController.reset();
|
||||
return;
|
||||
}
|
||||
final double desiredScale = _scaleAnimation!.value;
|
||||
final double scaleChange =
|
||||
desiredScale / _transformer.value.getMaxScaleOnAxis();
|
||||
final Offset referenceFocalPoint = _transformer.toScene(
|
||||
_scaleAnimationFocalPoint,
|
||||
);
|
||||
_transformer.value = _matrixScale(_transformer.value, scaleChange);
|
||||
|
||||
final Offset focalPointSceneScaled = _transformer.toScene(
|
||||
_scaleAnimationFocalPoint,
|
||||
);
|
||||
_transformer.value = _matrixTranslate(
|
||||
_transformer.value,
|
||||
focalPointSceneScaled - referenceFocalPoint,
|
||||
);
|
||||
}
|
||||
|
||||
void _handleTransformation() {
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
void _onPointerDown(PointerDownEvent event) {
|
||||
widget.onPointerDown?.call(event);
|
||||
_scaleGestureRecognizer.addPointer(event);
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_controller = AnimationController(vsync: this);
|
||||
_scaleController = AnimationController(vsync: this);
|
||||
|
||||
_transformer.addListener(_handleTransformation);
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(MouseInteractiveViewer oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
|
||||
final TransformationController? newController =
|
||||
widget.transformationController;
|
||||
if (newController == oldWidget.transformationController) {
|
||||
return;
|
||||
}
|
||||
_transformer.removeListener(_handleTransformation);
|
||||
if (oldWidget.transformationController == null) {
|
||||
_transformer.dispose();
|
||||
}
|
||||
_transformer = newController ?? TransformationController();
|
||||
_transformer.addListener(_handleTransformation);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_scaleGestureRecognizer.dispose();
|
||||
_controller.dispose();
|
||||
_scaleController.dispose();
|
||||
_transformer.removeListener(_handleTransformation);
|
||||
if (widget.transformationController == null) {
|
||||
_transformer.dispose();
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
assert(widget.child.key == widget.childKey);
|
||||
|
||||
return Listener(
|
||||
key: _parentKey,
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onPointerSignal: _receivedPointerSignal,
|
||||
onPointerDown: _onPointerDown,
|
||||
onPointerPanZoomStart: _scaleGestureRecognizer.addPointerPanZoom,
|
||||
onPointerPanZoomUpdate: widget.onPointerPanZoomUpdate,
|
||||
onPointerPanZoomEnd: widget.onPointerPanZoomEnd,
|
||||
child: _InteractiveViewerBuilt(
|
||||
childKey: widget.childKey,
|
||||
clipBehavior: widget.clipBehavior,
|
||||
constrained: widget.constrained,
|
||||
matrix: _transformer.value,
|
||||
alignment: widget.alignment,
|
||||
child: widget.child,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _InteractiveViewerBuilt extends StatelessWidget {
|
||||
const _InteractiveViewerBuilt({
|
||||
required this.child,
|
||||
required this.childKey,
|
||||
required this.clipBehavior,
|
||||
required this.constrained,
|
||||
required this.matrix,
|
||||
required this.alignment,
|
||||
});
|
||||
|
||||
final Widget child;
|
||||
final GlobalKey childKey;
|
||||
final Clip clipBehavior;
|
||||
final bool constrained;
|
||||
final Matrix4 matrix;
|
||||
final Alignment? alignment;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Widget child = Transform(
|
||||
transform: matrix,
|
||||
alignment: alignment,
|
||||
child: this.child,
|
||||
);
|
||||
|
||||
if (!constrained) {
|
||||
child = OverflowBox(
|
||||
alignment: Alignment.topLeft,
|
||||
minWidth: 0.0,
|
||||
minHeight: 0.0,
|
||||
maxWidth: double.infinity,
|
||||
maxHeight: double.infinity,
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
|
||||
if (clipBehavior != Clip.none) {
|
||||
child = ClipRect(clipBehavior: clipBehavior, child: child);
|
||||
}
|
||||
|
||||
return child;
|
||||
}
|
||||
}
|
||||
|
||||
enum _GestureType { pan, scale, rotate }
|
||||
|
||||
double _getFinalTime(
|
||||
double velocity,
|
||||
double drag, {
|
||||
double effectivelyMotionless = 10,
|
||||
}) {
|
||||
return math.log(effectivelyMotionless / velocity) / math.log(drag / 100);
|
||||
}
|
||||
|
||||
Offset _getMatrixTranslation(Matrix4 matrix) {
|
||||
final Vector3 nextTranslation = matrix.getTranslation();
|
||||
return Offset(nextTranslation.x, nextTranslation.y);
|
||||
}
|
||||
|
||||
Quad _transformViewport(Matrix4 matrix, Rect viewport) {
|
||||
final Matrix4 inverseMatrix = matrix.clone()..invert();
|
||||
return Quad.points(
|
||||
inverseMatrix.transform3(
|
||||
Vector3(viewport.topLeft.dx, viewport.topLeft.dy, 0.0),
|
||||
),
|
||||
inverseMatrix.transform3(
|
||||
Vector3(viewport.topRight.dx, viewport.topRight.dy, 0.0),
|
||||
),
|
||||
inverseMatrix.transform3(
|
||||
Vector3(viewport.bottomRight.dx, viewport.bottomRight.dy, 0.0),
|
||||
),
|
||||
inverseMatrix.transform3(
|
||||
Vector3(viewport.bottomLeft.dx, viewport.bottomLeft.dy, 0.0),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Quad _getAxisAlignedBoundingBoxWithRotation(Rect rect, double rotation) {
|
||||
final Matrix4 rotationMatrix = Matrix4.identity()
|
||||
..translateByDouble(rect.size.width / 2, rect.size.height / 2, 0, 1)
|
||||
..rotateZ(rotation)
|
||||
..translateByDouble(-rect.size.width / 2, -rect.size.height / 2, 0, 1);
|
||||
final Quad boundariesRotated = Quad.points(
|
||||
rotationMatrix.transform3(Vector3(rect.left, rect.top, 0.0)),
|
||||
rotationMatrix.transform3(Vector3(rect.right, rect.top, 0.0)),
|
||||
rotationMatrix.transform3(Vector3(rect.right, rect.bottom, 0.0)),
|
||||
rotationMatrix.transform3(Vector3(rect.left, rect.bottom, 0.0)),
|
||||
);
|
||||
// ignore: invalid_use_of_visible_for_testing_member
|
||||
return InteractiveViewer.getAxisAlignedBoundingBox(boundariesRotated);
|
||||
}
|
||||
|
||||
Offset _exceedsBy(Quad boundary, Quad viewport) {
|
||||
final List<Vector3> viewportPoints = <Vector3>[
|
||||
viewport.point0,
|
||||
viewport.point1,
|
||||
viewport.point2,
|
||||
viewport.point3,
|
||||
];
|
||||
Offset largestExcess = Offset.zero;
|
||||
for (final Vector3 point in viewportPoints) {
|
||||
// ignore: invalid_use_of_visible_for_testing_member
|
||||
final Vector3 pointInside = InteractiveViewer.getNearestPointInside(
|
||||
point,
|
||||
boundary,
|
||||
);
|
||||
final Offset excess = Offset(
|
||||
pointInside.x - point.x,
|
||||
pointInside.y - point.y,
|
||||
);
|
||||
if (excess.dx.abs() > largestExcess.dx.abs()) {
|
||||
largestExcess = Offset(excess.dx, largestExcess.dy);
|
||||
}
|
||||
if (excess.dy.abs() > largestExcess.dy.abs()) {
|
||||
largestExcess = Offset(largestExcess.dx, excess.dy);
|
||||
}
|
||||
}
|
||||
|
||||
return _round(largestExcess);
|
||||
}
|
||||
|
||||
Offset _round(Offset offset) {
|
||||
return Offset(
|
||||
double.parse(offset.dx.toStringAsFixed(9)),
|
||||
double.parse(offset.dy.toStringAsFixed(9)),
|
||||
);
|
||||
}
|
||||
|
||||
Offset _alignAxis(Offset offset, Axis axis) {
|
||||
return switch (axis) {
|
||||
Axis.horizontal => Offset(offset.dx, 0.0),
|
||||
Axis.vertical => Offset(0.0, offset.dy),
|
||||
};
|
||||
}
|
||||
|
||||
Axis? _getPanAxis(Offset point1, Offset point2) {
|
||||
if (point1 == point2) {
|
||||
return null;
|
||||
}
|
||||
final double x = point2.dx - point1.dx;
|
||||
final double y = point2.dy - point1.dy;
|
||||
return x.abs() > y.abs() ? Axis.horizontal : Axis.vertical;
|
||||
}
|
||||
|
||||
extension on Offset {
|
||||
Offset get flip => Offset(dy, dx);
|
||||
}
|
||||
@@ -277,7 +277,7 @@ class MyApp extends StatelessWidget {
|
||||
|
||||
final plCtr = PlPlayerController.instance;
|
||||
if (plCtr != null) {
|
||||
if (plCtr.isFullScreen.value == true) {
|
||||
if (plCtr.isFullScreen.value) {
|
||||
plCtr
|
||||
..triggerFullScreen(status: false)
|
||||
..controlsLock.value = false;
|
||||
|
||||
@@ -131,7 +131,6 @@ class _PlDanmakuState extends State<PlDanmaku> {
|
||||
e.colorful == DmColorfulType.VipGradualColor,
|
||||
count: e.hasCount() ? e.count : null,
|
||||
selfSend: e.isSelf,
|
||||
extra: VideoDanmaku(id: e.id.toInt(), mid: e.midHash),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -371,11 +371,6 @@ class LiveRoomController extends GetxController {
|
||||
: DmUtils.decimalToColor(extra['color']),
|
||||
type: DmUtils.getPosition(extra['mode']),
|
||||
selfSend: extra['send_from_me'] ?? false,
|
||||
extra: LiveDanmaku(
|
||||
id: extra['id_str'],
|
||||
mid: uid,
|
||||
uname: user['base']['name'],
|
||||
),
|
||||
),
|
||||
);
|
||||
if (!disableAutoScroll.value) {
|
||||
|
||||
@@ -211,8 +211,8 @@ class _LiveRoomPageState extends State<LiveRoomPage>
|
||||
required double width,
|
||||
required double height,
|
||||
bool isPipMode = false,
|
||||
Color? fill,
|
||||
Alignment? alignment,
|
||||
Color fill = Colors.black,
|
||||
Alignment alignment = Alignment.center,
|
||||
bool needDm = true,
|
||||
}) {
|
||||
if (!isFullScreen && !plPlayerController.isDesktopPip) {
|
||||
@@ -472,7 +472,7 @@ class _LiveRoomPageState extends State<LiveRoomPage>
|
||||
height: videoHeight,
|
||||
isFullScreen,
|
||||
needDm: isFullScreen,
|
||||
alignment: isFullScreen ? null : Alignment.topCenter,
|
||||
alignment: isFullScreen ? Alignment.center : Alignment.topCenter,
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
|
||||
@@ -29,13 +29,6 @@ List<SettingsModel> get playSettings => [
|
||||
setKey: SettingBoxKey.enableShowDanmaku,
|
||||
defaultVal: true,
|
||||
),
|
||||
// const SettingsModel(
|
||||
// settingsType: SettingsType.sw1tch,
|
||||
// title: '启用点击弹幕',
|
||||
// leading: Icon(Icons.touch_app_outlined),
|
||||
// setKey: SettingBoxKey.enableTapDm,
|
||||
// defaultVal: false,
|
||||
// ),
|
||||
SettingsModel(
|
||||
settingsType: SettingsType.normal,
|
||||
onTap: (setState) => Get.toNamed('/playSpeedSet'),
|
||||
|
||||
@@ -165,17 +165,18 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
|
||||
@override
|
||||
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||||
late final ctr = videoDetailController.plPlayerController;
|
||||
if (state == AppLifecycleState.resumed) {
|
||||
if (!videoDetailController.plPlayerController.showDanmaku) {
|
||||
if (!ctr.showDanmaku) {
|
||||
introController.startTimer();
|
||||
videoDetailController.plPlayerController.showDanmaku = true;
|
||||
ctr.showDanmaku = true;
|
||||
|
||||
// 修复从后台恢复时全屏状态下屏幕方向错误的问题
|
||||
if (isFullScreen && Platform.isIOS) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
// 根据视频方向重新设置屏幕方向
|
||||
final isVertical = videoDetailController.isVertical.value;
|
||||
final mode = plPlayerController?.mode;
|
||||
final mode = ctr.mode;
|
||||
|
||||
if (!(mode == FullScreenMode.vertical ||
|
||||
(mode == FullScreenMode.auto && isVertical) ||
|
||||
@@ -188,7 +189,7 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
}
|
||||
} else if (state == AppLifecycleState.paused) {
|
||||
introController.canelTimer();
|
||||
videoDetailController.plPlayerController.showDanmaku = false;
|
||||
ctr.showDanmaku = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import 'package:PiliPlus/common/widgets/dialog/report.dart';
|
||||
import 'package:PiliPlus/common/widgets/marquee.dart';
|
||||
import 'package:PiliPlus/http/danmaku.dart';
|
||||
import 'package:PiliPlus/http/danmaku_block.dart';
|
||||
import 'package:PiliPlus/http/init.dart';
|
||||
import 'package:PiliPlus/models/common/super_resolution_type.dart';
|
||||
import 'package:PiliPlus/models/common/video/audio_quality.dart';
|
||||
import 'package:PiliPlus/models/common/video/cdn_type.dart';
|
||||
@@ -30,6 +31,7 @@ import 'package:PiliPlus/plugin/pl_player/models/play_repeat.dart';
|
||||
import 'package:PiliPlus/plugin/pl_player/utils/fullscreen.dart';
|
||||
import 'package:PiliPlus/services/service_locator.dart';
|
||||
import 'package:PiliPlus/utils/accounts.dart';
|
||||
import 'package:PiliPlus/utils/accounts/account.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:PiliPlus/utils/image_utils.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
@@ -987,16 +989,19 @@ class HeaderControlState extends State<HeaderControl> {
|
||||
onTap: () async {
|
||||
Get.back();
|
||||
try {
|
||||
final res = await Dio().get(
|
||||
final res = await Request.dio.get<Uint8List>(
|
||||
item.subtitleUrl!.http2https,
|
||||
options: Options(responseType: ResponseType.bytes),
|
||||
options: Options(
|
||||
responseType: ResponseType.bytes,
|
||||
extra: {'account': const NoAccount()},
|
||||
),
|
||||
);
|
||||
if (res.statusCode == 200) {
|
||||
final Uint8List bytes = res.data;
|
||||
final bytes = res.data!;
|
||||
final name =
|
||||
'${introController.videoDetail.value.title}-${videoDetailCtr.bvid}-${videoDetailCtr.cid.value}-${item.lanDoc}.json';
|
||||
final path = await FilePicker.platform.saveFile(
|
||||
allowedExtensions: ['json'],
|
||||
allowedExtensions: const ['json'],
|
||||
type: FileType.custom,
|
||||
fileName: name,
|
||||
bytes: Utils.isDesktop ? null : bytes,
|
||||
|
||||
@@ -119,10 +119,6 @@ class PlPlayerController {
|
||||
late final RxBool _continuePlayInBackground =
|
||||
Pref.continuePlayInBackground.obs;
|
||||
|
||||
late final RxBool _flipX = false.obs;
|
||||
|
||||
late final RxBool _flipY = false.obs;
|
||||
|
||||
///
|
||||
final RxBool _isSliderMoving = false.obs;
|
||||
PlaylistMode _looping = PlaylistMode.none;
|
||||
@@ -231,9 +227,9 @@ class PlPlayerController {
|
||||
late final RxBool onlyPlayAudio = false.obs;
|
||||
|
||||
/// 镜像
|
||||
RxBool get flipX => _flipX;
|
||||
late final RxBool flipX = false.obs;
|
||||
|
||||
RxBool get flipY => _flipY;
|
||||
late final RxBool flipY = false.obs;
|
||||
|
||||
/// 是否长按倍速
|
||||
RxBool get longPressStatus => _longPressStatus;
|
||||
@@ -324,7 +320,6 @@ class PlPlayerController {
|
||||
}
|
||||
|
||||
/// 弹幕权重
|
||||
late final enableTapDm = Pref.enableTapDm;
|
||||
late int danmakuWeight = Pref.danmakuWeight;
|
||||
late RuleFilter filters = Pref.danmakuFilterRule;
|
||||
// 关联弹幕控制器
|
||||
|
||||
@@ -4,6 +4,7 @@ import 'dart:math' as math;
|
||||
import 'dart:ui' as ui;
|
||||
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/gesture/interactive_viewer.dart';
|
||||
import 'package:PiliPlus/common/widgets/loading_widget.dart';
|
||||
import 'package:PiliPlus/common/widgets/pair.dart';
|
||||
import 'package:PiliPlus/common/widgets/progress_bar/audio_video_progress_bar.dart';
|
||||
@@ -21,12 +22,10 @@ import 'package:PiliPlus/models_new/video/video_detail/section.dart';
|
||||
import 'package:PiliPlus/models_new/video/video_detail/ugc_season.dart';
|
||||
import 'package:PiliPlus/models_new/video/video_shot/data.dart';
|
||||
import 'package:PiliPlus/pages/common/common_intro_controller.dart';
|
||||
import 'package:PiliPlus/pages/danmaku/dnamaku_model.dart';
|
||||
import 'package:PiliPlus/pages/video/controller.dart';
|
||||
import 'package:PiliPlus/pages/video/introduction/pgc/controller.dart';
|
||||
import 'package:PiliPlus/pages/video/post_panel/popup_menu_text.dart';
|
||||
import 'package:PiliPlus/pages/video/post_panel/view.dart';
|
||||
import 'package:PiliPlus/pages/video/widgets/header_control.dart';
|
||||
import 'package:PiliPlus/plugin/pl_player/controller.dart';
|
||||
import 'package:PiliPlus/plugin/pl_player/models/bottom_control_type.dart';
|
||||
import 'package:PiliPlus/plugin/pl_player/models/bottom_progress_behavior.dart';
|
||||
@@ -48,7 +47,6 @@ import 'package:PiliPlus/utils/image_utils.dart';
|
||||
import 'package:PiliPlus/utils/storage.dart';
|
||||
import 'package:PiliPlus/utils/storage_key.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:canvas_danmaku/canvas_danmaku.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:easy_debounce/easy_throttle.dart';
|
||||
import 'package:fl_chart/fl_chart.dart';
|
||||
@@ -64,6 +62,7 @@ import 'package:get/get.dart' hide ContextExtensionss;
|
||||
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
|
||||
import 'package:media_kit_video/media_kit_video.dart';
|
||||
import 'package:screen_brightness_platform_interface/screen_brightness_platform_interface.dart';
|
||||
import 'package:wakelock_plus/wakelock_plus.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
|
||||
class PLVideoPlayer extends StatefulWidget {
|
||||
@@ -76,12 +75,10 @@ class PLVideoPlayer extends StatefulWidget {
|
||||
required this.headerControl,
|
||||
this.bottomControl,
|
||||
this.danmuWidget,
|
||||
this.customWidget,
|
||||
this.customWidgets,
|
||||
this.showEpisodes,
|
||||
this.showViewPoints,
|
||||
this.fill,
|
||||
this.alignment,
|
||||
this.fill = Colors.black,
|
||||
this.alignment = Alignment.center,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@@ -93,31 +90,26 @@ class PLVideoPlayer extends StatefulWidget {
|
||||
final Widget headerControl;
|
||||
final Widget? bottomControl;
|
||||
final Widget? danmuWidget;
|
||||
|
||||
// List<Widget> or Widget
|
||||
|
||||
final Widget? customWidget;
|
||||
final List<Widget>? customWidgets;
|
||||
final void Function([int?, UgcSeason?, dynamic, String?, int?, int?])?
|
||||
showEpisodes;
|
||||
final VoidCallback? showViewPoints;
|
||||
final Color? fill;
|
||||
final Alignment? alignment;
|
||||
final Color fill;
|
||||
final Alignment alignment;
|
||||
|
||||
@override
|
||||
State<PLVideoPlayer> createState() => _PLVideoPlayerState();
|
||||
}
|
||||
|
||||
class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
with TickerProviderStateMixin {
|
||||
with WidgetsBindingObserver, TickerProviderStateMixin {
|
||||
late AnimationController animationController;
|
||||
late VideoController videoController;
|
||||
late final CommonIntroController introController = widget.introController!;
|
||||
late final VideoDetailController videoDetailController =
|
||||
widget.videoDetailController!;
|
||||
|
||||
final GlobalKey _playerKey = GlobalKey();
|
||||
final GlobalKey<VideoState> key = GlobalKey<VideoState>();
|
||||
final _playerKey = GlobalKey();
|
||||
final _videoKey = GlobalKey();
|
||||
|
||||
final RxDouble _brightnessValue = 0.0.obs;
|
||||
final RxBool _brightnessIndicator = false.obs;
|
||||
@@ -139,19 +131,28 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
StreamSubscription? _listener;
|
||||
StreamSubscription? _controlsListener;
|
||||
|
||||
@override
|
||||
void didUpdateWidget(PLVideoPlayer oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (plPlayerController.enableTapDm &&
|
||||
(widget.maxWidth != oldWidget.maxWidth ||
|
||||
widget.maxHeight != maxHeight)) {
|
||||
_removeOverlay();
|
||||
}
|
||||
}
|
||||
bool _pauseDueToPauseUponEnteringBackgroundMode = false;
|
||||
StreamSubscription<bool>? wakeLock;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
|
||||
late final player = plPlayerController.videoController?.player;
|
||||
if (player != null && player.state.playing) {
|
||||
WakelockPlus.enable();
|
||||
}
|
||||
wakeLock = player?.stream.playing.listen(
|
||||
(value) {
|
||||
if (value) {
|
||||
WakelockPlus.enable();
|
||||
} else {
|
||||
WakelockPlus.disable();
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
_controlsListener = plPlayerController.showControls.listen((bool val) {
|
||||
final visible = val && !plPlayerController.controlsLock.value;
|
||||
if (widget.videoDetailController?.headerCtrKey.currentState?.provider
|
||||
@@ -218,6 +219,27 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
..onDoubleTapDown = onDoubleTapDown;
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||||
if (!plPlayerController.continuePlayInBackground.value) {
|
||||
late final player = plPlayerController.videoController?.player;
|
||||
if (const [
|
||||
AppLifecycleState.paused,
|
||||
AppLifecycleState.detached,
|
||||
].contains(state)) {
|
||||
if (player != null && player.state.playing) {
|
||||
_pauseDueToPauseUponEnteringBackgroundMode = true;
|
||||
player.pause();
|
||||
}
|
||||
} else {
|
||||
if (_pauseDueToPauseUponEnteringBackgroundMode) {
|
||||
_pauseDueToPauseUponEnteringBackgroundMode = false;
|
||||
player?.play();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> setBrightness(double value) async {
|
||||
try {
|
||||
await ScreenBrightnessPlatform.instance.setApplicationScreenBrightness(
|
||||
@@ -236,6 +258,11 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
WidgetsBinding.instance.removeObserver(this);
|
||||
wakeLock?.cancel();
|
||||
WakelockPlus.enabled.then((i) {
|
||||
if (i) WakelockPlus.disable();
|
||||
});
|
||||
_tapGestureRecognizer.dispose();
|
||||
_longPressRecognizer?.dispose();
|
||||
_doubleTapGestureRecognizer.dispose();
|
||||
@@ -831,7 +858,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
if (details.localFocalPoint.dx < 40) return;
|
||||
if (details.localFocalPoint.dx > maxWidth - 40) return;
|
||||
if (details.localFocalPoint.dy > maxHeight - 40) return;
|
||||
if (details.pointerCount == 2) {
|
||||
if (details.pointerCount > 1) {
|
||||
interacting = true;
|
||||
}
|
||||
plPlayerController.initialFocalPoint = details.localFocalPoint;
|
||||
@@ -848,7 +875,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
}
|
||||
Offset cumulativeDelta =
|
||||
details.localFocalPoint - plPlayerController.initialFocalPoint;
|
||||
if (details.pointerCount == 2 && cumulativeDelta.distance < 1.5) {
|
||||
if (details.pointerCount > 1 && cumulativeDelta.distance < 1.5) {
|
||||
interacting = true;
|
||||
_gestureType = null;
|
||||
return;
|
||||
@@ -1072,25 +1099,10 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
|
||||
void onTapUp(TapUpDetails details) {
|
||||
switch (details.kind) {
|
||||
case ui.PointerDeviceKind.mouse when (Utils.isDesktop):
|
||||
case ui.PointerDeviceKind.mouse when Utils.isDesktop:
|
||||
onTapDesktop();
|
||||
break;
|
||||
default:
|
||||
if (kDebugMode && isMobile) {
|
||||
final ctr = plPlayerController.danmakuController;
|
||||
if (ctr != null) {
|
||||
final item = ctr.findSingleDanmaku(details.localPosition);
|
||||
if (item == null) {
|
||||
if (_suspendedDm.value != null) {
|
||||
_removeOverlay();
|
||||
break;
|
||||
}
|
||||
} else if (item != _suspendedDm.value?.item) {
|
||||
_showOverlay(item, details, ctr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
plPlayerController.controls = !plPlayerController.showControls.value;
|
||||
break;
|
||||
}
|
||||
@@ -1098,7 +1110,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
|
||||
void onDoubleTapDown(TapDownDetails details) {
|
||||
switch (details.kind) {
|
||||
case ui.PointerDeviceKind.mouse when !isMobile:
|
||||
case ui.PointerDeviceKind.mouse when Utils.isDesktop:
|
||||
onDoubleTapDesktop();
|
||||
break;
|
||||
default:
|
||||
@@ -1113,12 +1125,11 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
_longPressRecognizer ??= LongPressGestureRecognizer()
|
||||
..onLongPressStart = ((_) =>
|
||||
plPlayerController.setLongPressStatus(true))
|
||||
..onLongPressEnd = ((_) =>
|
||||
plPlayerController.setLongPressStatus(false));
|
||||
..onLongPressEnd = (_) => plPlayerController.setLongPressStatus(false);
|
||||
late final TapGestureRecognizer _tapGestureRecognizer;
|
||||
late final DoubleTapGestureRecognizer _doubleTapGestureRecognizer;
|
||||
|
||||
void onPointerDown(PointerDownEvent event) {
|
||||
void _onPointerDown(PointerDownEvent event) {
|
||||
if (!isMobile) {
|
||||
final buttons = event.buttons;
|
||||
final isSecondaryBtn = buttons == kSecondaryMouseButton;
|
||||
@@ -1135,11 +1146,11 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
}
|
||||
}
|
||||
|
||||
_tapGestureRecognizer.addPointer(event);
|
||||
_doubleTapGestureRecognizer.addPointer(event);
|
||||
if (!plPlayerController.isLive) {
|
||||
longPressRecognizer.addPointer(event);
|
||||
}
|
||||
_tapGestureRecognizer.addPointer(event);
|
||||
_doubleTapGestureRecognizer.addPointer(event);
|
||||
}
|
||||
|
||||
void _showControlsIfNeeded() {
|
||||
@@ -1155,7 +1166,7 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
}
|
||||
}
|
||||
|
||||
void onPointerPanZoomUpdate(PointerPanZoomUpdateEvent event) {
|
||||
void _onPointerPanZoomUpdate(PointerPanZoomUpdateEvent event) {
|
||||
if (plPlayerController.controlsLock.value) return;
|
||||
if (_gestureType == null) {
|
||||
final pan = event.pan;
|
||||
@@ -1213,11 +1224,11 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
}
|
||||
}
|
||||
|
||||
void onPointerPanZoomEnd(PointerPanZoomEndEvent event) {
|
||||
void _onPointerPanZoomEnd(PointerPanZoomEndEvent event) {
|
||||
_gestureType = null;
|
||||
}
|
||||
|
||||
void onPointerSignal(PointerSignalEvent event) {
|
||||
void _onPointerSignal(PointerSignalEvent event) {
|
||||
if (event is PointerScrollEvent) {
|
||||
final offset = -event.scrollDelta.dy / 4000;
|
||||
final volume = clampDouble(
|
||||
@@ -1245,58 +1256,29 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
final isFullScreen = this.isFullScreen;
|
||||
final isLive = plPlayerController.isLive;
|
||||
|
||||
final gestureWidget = Listener(
|
||||
behavior: HitTestBehavior.translucent,
|
||||
onPointerDown: onPointerDown,
|
||||
onPointerPanZoomUpdate: isMobile ? null : onPointerPanZoomUpdate,
|
||||
onPointerPanZoomEnd: isMobile ? null : onPointerPanZoomEnd,
|
||||
onPointerSignal: isMobile ? null : onPointerSignal,
|
||||
);
|
||||
|
||||
final child = Stack(
|
||||
fit: StackFit.passthrough,
|
||||
key: _playerKey,
|
||||
children: <Widget>[
|
||||
Obx(
|
||||
() {
|
||||
final videoFit = plPlayerController.videoFit.value;
|
||||
return Video(
|
||||
key: key,
|
||||
width: maxWidth,
|
||||
height: maxHeight,
|
||||
fill: widget.fill ?? Colors.black,
|
||||
alignment: widget.alignment ?? Alignment.center,
|
||||
controller: videoController,
|
||||
controls: NoVideoControls,
|
||||
pauseUponEnteringBackgroundMode:
|
||||
!plPlayerController.continuePlayInBackground.value,
|
||||
resumeUponEnteringForegroundMode: true,
|
||||
// 字幕尺寸调节
|
||||
subtitleViewConfiguration: isLive
|
||||
? const SubtitleViewConfiguration()
|
||||
: plPlayerController.subtitleConfig.value,
|
||||
fit: videoFit.boxFit,
|
||||
aspectRatio: videoFit.aspectRatio,
|
||||
dmWidget: widget.danmuWidget,
|
||||
transformationController: transformationController,
|
||||
scaleEnabled: isMobile && !plPlayerController.controlsLock.value,
|
||||
enableShrinkVideoSize:
|
||||
isMobile && plPlayerController.enableShrinkVideoSize,
|
||||
onInteractionStart: _onInteractionStart, // TODO: refa gesture
|
||||
onInteractionUpdate: _onInteractionUpdate,
|
||||
onInteractionEnd: _onInteractionEnd,
|
||||
flipX: plPlayerController.flipX.value,
|
||||
flipY: plPlayerController.flipY.value,
|
||||
gestureWidget: gestureWidget,
|
||||
enableDragSubtitle: plPlayerController.enableDragSubtitle,
|
||||
onUpdatePadding: plPlayerController.onUpdatePadding,
|
||||
);
|
||||
},
|
||||
),
|
||||
_videoWidget,
|
||||
|
||||
// /// 弹幕面板
|
||||
// if (widget.danmuWidget != null)
|
||||
// Positioned.fill(top: 4, child: widget.danmuWidget!),
|
||||
if (widget.danmuWidget case final danmaku?)
|
||||
Positioned.fill(child: danmaku),
|
||||
|
||||
if (!isLive)
|
||||
Positioned.fill(
|
||||
child: IgnorePointer(
|
||||
ignoring: !plPlayerController.enableDragSubtitle,
|
||||
child: Obx(
|
||||
() => SubtitleView(
|
||||
controller: videoController,
|
||||
configuration: plPlayerController.subtitleConfig.value,
|
||||
enableDragSubtitle: plPlayerController.enableDragSubtitle,
|
||||
onUpdatePadding: plPlayerController.onUpdatePadding,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
/// 长按倍速 toast
|
||||
if (!isLive)
|
||||
@@ -1928,102 +1910,9 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
)
|
||||
: const SizedBox.shrink();
|
||||
}),
|
||||
|
||||
Obx(() {
|
||||
if (_suspendedDm.value case final suspendedDm?) {
|
||||
final offset = suspendedDm.offset;
|
||||
final item = suspendedDm.item;
|
||||
final extra = item.content.extra as VideoDanmaku;
|
||||
return Positioned(
|
||||
left: offset.dx,
|
||||
top: offset.dy,
|
||||
child: Column(
|
||||
children: [
|
||||
const CustomPaint(
|
||||
painter: _TrianglePainter(Colors.black54),
|
||||
size: Size(12, 6),
|
||||
),
|
||||
Container(
|
||||
width: overlayWidth,
|
||||
height: overlayHeight,
|
||||
decoration: const BoxDecoration(
|
||||
color: Colors.black54,
|
||||
borderRadius: BorderRadius.all(Radius.circular(18)),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
_overlayItem(
|
||||
Icon(
|
||||
size: 20,
|
||||
extra.isLike
|
||||
? Icons.thumb_up_off_alt_sharp
|
||||
: Icons.thumb_up_off_alt_outlined,
|
||||
color: Colors.white,
|
||||
),
|
||||
onTap: () {
|
||||
_removeOverlay();
|
||||
HeaderControl.likeDanmaku(
|
||||
extra,
|
||||
plPlayerController.cid!,
|
||||
);
|
||||
},
|
||||
),
|
||||
_overlayItem(
|
||||
const Icon(
|
||||
size: 20,
|
||||
Icons.copy,
|
||||
color: Colors.white,
|
||||
),
|
||||
onTap: () {
|
||||
_removeOverlay();
|
||||
Utils.copyText(item.content.text);
|
||||
},
|
||||
),
|
||||
if (item.content.selfSend)
|
||||
_overlayItem(
|
||||
const Icon(
|
||||
size: 20,
|
||||
Icons.delete,
|
||||
color: Colors.white,
|
||||
),
|
||||
onTap: () {
|
||||
_removeOverlay();
|
||||
HeaderControl.deleteDanmaku(
|
||||
extra.id,
|
||||
plPlayerController.cid!,
|
||||
);
|
||||
},
|
||||
)
|
||||
else
|
||||
_overlayItem(
|
||||
const Icon(
|
||||
size: 20,
|
||||
Icons.report_problem_outlined,
|
||||
color: Colors.white,
|
||||
),
|
||||
onTap: () {
|
||||
_removeOverlay();
|
||||
HeaderControl.reportDanmaku(
|
||||
extra,
|
||||
context,
|
||||
plPlayerController,
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
return const SizedBox.shrink();
|
||||
}),
|
||||
],
|
||||
);
|
||||
if (!isMobile) {
|
||||
if (Utils.isDesktop) {
|
||||
return Obx(
|
||||
() => MouseRegion(
|
||||
cursor: !plPlayerController.showControls.value && isFullScreen
|
||||
@@ -2040,6 +1929,58 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
return child;
|
||||
}
|
||||
|
||||
Widget get _videoWidget {
|
||||
return Container(
|
||||
clipBehavior: Clip.none,
|
||||
width: maxWidth,
|
||||
height: maxHeight,
|
||||
color: widget.fill,
|
||||
child: Obx(
|
||||
() => MouseInteractiveViewer(
|
||||
scaleEnabled: !plPlayerController.controlsLock.value,
|
||||
pointerSignalFallback: _onPointerSignal,
|
||||
onPointerPanZoomUpdate: _onPointerPanZoomUpdate,
|
||||
onPointerPanZoomEnd: _onPointerPanZoomEnd,
|
||||
onPointerDown: _onPointerDown,
|
||||
onInteractionStart: _onInteractionStart,
|
||||
onInteractionUpdate: _onInteractionUpdate,
|
||||
onInteractionEnd: _onInteractionEnd,
|
||||
panEnabled: false,
|
||||
minScale: plPlayerController.enableShrinkVideoSize ? 0.75 : 1,
|
||||
maxScale: 2.0,
|
||||
boundaryMargin: plPlayerController.enableShrinkVideoSize
|
||||
? const EdgeInsets.all(double.infinity)
|
||||
: EdgeInsets.zero,
|
||||
panAxis: PanAxis.aligned,
|
||||
transformationController: transformationController,
|
||||
childKey: _videoKey,
|
||||
child: RepaintBoundary(
|
||||
key: _videoKey,
|
||||
child: Obx(
|
||||
() {
|
||||
final videoFit = plPlayerController.videoFit.value;
|
||||
return Transform.flip(
|
||||
flipX: plPlayerController.flipX.value,
|
||||
flipY: plPlayerController.flipY.value,
|
||||
filterQuality: FilterQuality.low,
|
||||
child: FittedBox(
|
||||
fit: videoFit.boxFit,
|
||||
alignment: widget.alignment,
|
||||
child: SimpleVideo(
|
||||
controller: plPlayerController.videoController!,
|
||||
fill: widget.fill,
|
||||
aspectRatio: videoFit.aspectRatio,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
late final segment = Pair(
|
||||
first: plPlayerController.position.value.inMilliseconds / 1000.0,
|
||||
second: plPlayerController.position.value.inMilliseconds / 1000.0,
|
||||
@@ -2183,54 +2124,6 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
static const overlaySpacing = 10.0;
|
||||
static const overlayWidth = 130.0;
|
||||
static const overlayHeight = 35.0;
|
||||
|
||||
final Rx<({Offset offset, DanmakuItem item})?> _suspendedDm =
|
||||
Rx<({Offset offset, DanmakuItem item})?>(null);
|
||||
|
||||
void _removeOverlay() {
|
||||
_suspendedDm
|
||||
..value?.item.suspend = false
|
||||
..value = null;
|
||||
}
|
||||
|
||||
Widget _overlayItem(Widget child, {required VoidCallback onTap}) {
|
||||
return GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: onTap,
|
||||
child: SizedBox(
|
||||
height: overlayHeight,
|
||||
width: overlayWidth / 3,
|
||||
child: Center(
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showOverlay(
|
||||
DanmakuItem<DanmakuExtra> item,
|
||||
TapUpDetails event,
|
||||
DanmakuController<DanmakuExtra> ctr,
|
||||
) {
|
||||
_removeOverlay();
|
||||
item.suspend = true;
|
||||
|
||||
final dy = item.content.type == DanmakuItemType.bottom
|
||||
? ctr.viewHeight - item.yPosition - item.height
|
||||
: item.yPosition;
|
||||
|
||||
final top = dy + item.height;
|
||||
final left = clampDouble(
|
||||
event.localPosition.dx - overlayWidth / 2,
|
||||
overlaySpacing,
|
||||
ctr.viewWidth - overlayWidth - overlaySpacing,
|
||||
);
|
||||
_suspendedDm.value = (offset: Offset(left, top), item: item);
|
||||
}
|
||||
}
|
||||
|
||||
Widget buildDmChart(
|
||||
@@ -2575,27 +2468,3 @@ Widget buildViewPointWidget(
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
class _TrianglePainter extends CustomPainter {
|
||||
const _TrianglePainter(this.color);
|
||||
final Color color;
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
final paint = Paint()
|
||||
..color = color
|
||||
..style = PaintingStyle.fill;
|
||||
|
||||
final path = Path()
|
||||
..moveTo(0, size.height)
|
||||
..lineTo(size.width, size.height)
|
||||
..lineTo(size.width / 2, 0)
|
||||
..close();
|
||||
|
||||
canvas.drawPath(path, paint);
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRepaint(covariant _TrianglePainter oldDelegate) =>
|
||||
color != oldDelegate.color;
|
||||
}
|
||||
|
||||
@@ -226,7 +226,7 @@ class LiveMessageStream {
|
||||
}
|
||||
_processingData(decompressedData);
|
||||
} catch (e) {
|
||||
if (kDebugMode) logger.i(e);
|
||||
if (kDebugMode) rethrow;
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -256,7 +256,7 @@ class LiveMessageStream {
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
if (kDebugMode) logger.i('ParseHeader错误: $e');
|
||||
if (kDebugMode) rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -141,8 +141,7 @@ abstract class SettingBoxKey {
|
||||
showFsLockBtn = 'showFsLockBtn',
|
||||
silentDownImg = 'silentDownImg',
|
||||
showMemberShop = 'showMemberShop',
|
||||
enablePlayAll = 'enablePlayAll',
|
||||
enableTapDm = 'enableTapDm';
|
||||
enablePlayAll = 'enablePlayAll';
|
||||
|
||||
static const String minimizeOnExit = 'minimizeOnExit',
|
||||
windowSize = 'windowSize',
|
||||
|
||||
@@ -858,7 +858,4 @@ abstract class Pref {
|
||||
|
||||
static bool get enablePlayAll =>
|
||||
_setting.get(SettingBoxKey.enablePlayAll, defaultValue: true);
|
||||
|
||||
static bool get enableTapDm =>
|
||||
_setting.get(SettingBoxKey.enableTapDm, defaultValue: false);
|
||||
}
|
||||
|
||||
14
pubspec.lock
14
pubspec.lock
@@ -223,7 +223,7 @@ packages:
|
||||
description:
|
||||
path: "."
|
||||
ref: main
|
||||
resolved-ref: "454790117e05e96782b05ee3d28d0283741b3cde"
|
||||
resolved-ref: "5bbf0b0903447862fb04927f13530db701e2cfcf"
|
||||
url: "https://github.com/bggRGjQaUbCoE/canvas_danmaku.git"
|
||||
source: git
|
||||
version: "0.2.6"
|
||||
@@ -1097,7 +1097,7 @@ packages:
|
||||
description:
|
||||
path: media_kit
|
||||
ref: "version_1.2.5"
|
||||
resolved-ref: "8f15c273f3e5c05476d8b19881719a1bd906c4f2"
|
||||
resolved-ref: ebc4b1a4a3ec3170898e2a502e17c0a25289eb53
|
||||
url: "https://github.com/bggRGjQaUbCoE/media-kit.git"
|
||||
source: git
|
||||
version: "1.1.11"
|
||||
@@ -1106,7 +1106,7 @@ packages:
|
||||
description:
|
||||
path: "libs/android/media_kit_libs_android_video"
|
||||
ref: "version_1.2.5"
|
||||
resolved-ref: "8f15c273f3e5c05476d8b19881719a1bd906c4f2"
|
||||
resolved-ref: ebc4b1a4a3ec3170898e2a502e17c0a25289eb53
|
||||
url: "https://github.com/bggRGjQaUbCoE/media-kit.git"
|
||||
source: git
|
||||
version: "1.3.7"
|
||||
@@ -1139,7 +1139,7 @@ packages:
|
||||
description:
|
||||
path: "libs/universal/media_kit_libs_video"
|
||||
ref: "version_1.2.5"
|
||||
resolved-ref: "8f15c273f3e5c05476d8b19881719a1bd906c4f2"
|
||||
resolved-ref: ebc4b1a4a3ec3170898e2a502e17c0a25289eb53
|
||||
url: "https://github.com/bggRGjQaUbCoE/media-kit.git"
|
||||
source: git
|
||||
version: "1.0.5"
|
||||
@@ -1148,7 +1148,7 @@ packages:
|
||||
description:
|
||||
path: "libs/windows/media_kit_libs_windows_video"
|
||||
ref: "version_1.2.5"
|
||||
resolved-ref: "8f15c273f3e5c05476d8b19881719a1bd906c4f2"
|
||||
resolved-ref: ebc4b1a4a3ec3170898e2a502e17c0a25289eb53
|
||||
url: "https://github.com/bggRGjQaUbCoE/media-kit.git"
|
||||
source: git
|
||||
version: "1.0.10"
|
||||
@@ -1157,7 +1157,7 @@ packages:
|
||||
description:
|
||||
path: media_kit_native_event_loop
|
||||
ref: "version_1.2.5"
|
||||
resolved-ref: "8f15c273f3e5c05476d8b19881719a1bd906c4f2"
|
||||
resolved-ref: ebc4b1a4a3ec3170898e2a502e17c0a25289eb53
|
||||
url: "https://github.com/bggRGjQaUbCoE/media-kit.git"
|
||||
source: git
|
||||
version: "1.0.9"
|
||||
@@ -1166,7 +1166,7 @@ packages:
|
||||
description:
|
||||
path: media_kit_video
|
||||
ref: "version_1.2.5"
|
||||
resolved-ref: "8f15c273f3e5c05476d8b19881719a1bd906c4f2"
|
||||
resolved-ref: ebc4b1a4a3ec3170898e2a502e17c0a25289eb53
|
||||
url: "https://github.com/bggRGjQaUbCoE/media-kit.git"
|
||||
source: git
|
||||
version: "1.2.5"
|
||||
|
||||
Reference in New Issue
Block a user