diff --git a/lib/main.dart b/lib/main.dart index 5cf1e0b5f..123cc78bf 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -7,7 +7,6 @@ import 'package:PiliPlus/common/widgets/mouse_back.dart'; import 'package:PiliPlus/common/widgets/scale_app.dart'; import 'package:PiliPlus/http/init.dart'; import 'package:PiliPlus/models/common/theme/theme_color_type.dart'; -import 'package:PiliPlus/plugin/pl_player/controller.dart'; import 'package:PiliPlus/router/app_pages.dart'; import 'package:PiliPlus/services/account_service.dart'; import 'package:PiliPlus/services/download/download_service.dart'; @@ -231,33 +230,18 @@ class MyApp extends StatelessWidget { return; } - if (Get.routing.route is! GetPageRoute) { - Get.back(); - return; - } - - final plCtr = PlPlayerController.instance; - if (plCtr != null) { - if (plCtr.isFullScreen.value) { - plCtr - ..triggerFullScreen(status: false) - ..controlsLock.value = false - ..showControls.value = false; - return; - } - - if (plCtr.isDesktopPip) { - plCtr - ..exitDesktopPip().whenComplete( - () => plCtr.initialFocalPoint = Offset.zero, - ) - ..controlsLock.value = false - ..showControls.value = false; + final route = Get.routing.route; + if (route is GetPageRoute) { + if (route.popDisposition == .doNotPop) { + route.onPopInvokedWithResult(false, null); return; } } - Get.back(); + final navigator = Get.key.currentState; + if (navigator?.canPop() ?? false) { + navigator!.pop(); + } } @override diff --git a/lib/pages/live_room/view.dart b/lib/pages/live_room/view.dart index d33d3826b..a93dcbafc 100644 --- a/lib/pages/live_room/view.dart +++ b/lib/pages/live_room/view.dart @@ -352,16 +352,8 @@ class _LiveRoomPageState extends State ); } return PopScope( - canPop: !isFullScreen, - onPopInvokedWithResult: (bool didPop, Object? result) { - if (plPlayerController.controlsLock.value) { - plPlayerController.onLockControl(false); - return; - } - if (isFullScreen) { - plPlayerController.triggerFullScreen(status: false); - } - }, + canPop: !isFullScreen && !plPlayerController.isDesktopPip, + onPopInvokedWithResult: plPlayerController.onPopInvokedWithResult, child: player, ); } diff --git a/lib/pages/video/view.dart b/lib/pages/video/view.dart index 272ed6dd5..8ce2012b1 100644 --- a/lib/pages/video/view.dart +++ b/lib/pages/video/view.dart @@ -1343,42 +1343,49 @@ class _VideoDetailPageVState extends State required double width, required double height, bool isPipMode = false, - }) => Obx( - key: videoDetailController.videoPlayerKey, - () => - videoDetailController.videoState.value is! Success || - !videoDetailController.autoPlay.value || - plPlayerController?.videoController == null - ? const SizedBox.shrink() - : PLVideoPlayer( - maxWidth: width, - maxHeight: height, - plPlayerController: plPlayerController!, - videoDetailController: videoDetailController, - introController: introController, - headerControl: HeaderControl( - key: videoDetailController.headerCtrKey, - isPortrait: isPortrait, - controller: videoDetailController.plPlayerController, - videoDetailCtr: videoDetailController, - heroTag: heroTag, - ), - danmuWidget: isPipMode && pipNoDanmaku - ? null - : Obx( - () => PlDanmaku( - key: ValueKey(videoDetailController.cid.value), - isPipMode: isPipMode, - cid: videoDetailController.cid.value, - playerController: plPlayerController!, - isFullScreen: plPlayerController!.isFullScreen.value, - isFileSource: videoDetailController.isFileSource, - size: Size(width, height), + }) => PopScope( + canPop: + !isFullScreen && + !videoDetailController.plPlayerController.isDesktopPip && + (videoDetailController.horizontalScreen || isPortrait), + onPopInvokedWithResult: _onPopInvokedWithResult, + child: Obx( + key: videoDetailController.videoPlayerKey, + () => + videoDetailController.videoState.value is! Success || + !videoDetailController.autoPlay.value || + plPlayerController?.videoController == null + ? const SizedBox.shrink() + : PLVideoPlayer( + maxWidth: width, + maxHeight: height, + plPlayerController: plPlayerController!, + videoDetailController: videoDetailController, + introController: introController, + headerControl: HeaderControl( + key: videoDetailController.headerCtrKey, + isPortrait: isPortrait, + controller: videoDetailController.plPlayerController, + videoDetailCtr: videoDetailController, + heroTag: heroTag, + ), + danmuWidget: isPipMode && pipNoDanmaku + ? null + : Obx( + () => PlDanmaku( + key: ValueKey(videoDetailController.cid.value), + isPipMode: isPipMode, + cid: videoDetailController.cid.value, + playerController: plPlayerController!, + isFullScreen: plPlayerController!.isFullScreen.value, + isFileSource: videoDetailController.isFileSource, + size: Size(width, height), + ), ), - ), - showEpisodes: showEpisodes, - showViewPoints: showViewPoints, - ), + showEpisodes: showEpisodes, + showViewPoints: showViewPoints, + ), + ), ); late ThemeData themeData; @@ -1577,165 +1584,158 @@ class _VideoDetailPageVState extends State Widget videoPlayer({required double width, required double height}) { final isFullScreen = this.isFullScreen; - return PopScope( - canPop: - !isFullScreen && - (videoDetailController.horizontalScreen || isPortrait), - onPopInvokedWithResult: _onPopInvokedWithResult, - child: Stack( - clipBehavior: Clip.none, - children: [ - const Positioned.fill(child: ColoredBox(color: Colors.black)), + return Stack( + clipBehavior: Clip.none, + children: [ + const Positioned.fill(child: ColoredBox(color: Colors.black)), - if (isShowing) plPlayer(width: width, height: height), + if (isShowing) plPlayer(width: width, height: height), - Obx(() { - if (!videoDetailController.autoPlay.value) { - return Positioned.fill( - child: GestureDetector( - onTap: handlePlay, - behavior: .opaque, - child: Obx( - () => NetworkImgLayer( - type: .emote, - quality: 60, - src: videoDetailController.cover.value, - width: width, - height: height, - cacheWidth: true, - getPlaceHolder: () => Center( - child: Image.asset('assets/images/loading.png'), - ), + Obx(() { + if (!videoDetailController.autoPlay.value) { + return Positioned.fill( + child: GestureDetector( + onTap: handlePlay, + behavior: .opaque, + child: Obx( + () => NetworkImgLayer( + type: .emote, + quality: 60, + src: videoDetailController.cover.value, + width: width, + height: height, + cacheWidth: true, + getPlaceHolder: () => Center( + child: Image.asset('assets/images/loading.png'), ), ), ), - ); + ), + ); + } + return const SizedBox.shrink(); + }), + manualPlayerWidget, + + if (videoDetailController.plPlayerController.enableBlock || + videoDetailController.continuePlayingPart) + Positioned( + left: 16, + bottom: isFullScreen ? max(75, maxHeight * 0.25) : 75, + width: MediaQuery.textScalerOf(context).scale(120), + child: AnimatedList( + padding: EdgeInsets.zero, + key: videoDetailController.listKey, + reverse: true, + shrinkWrap: true, + initialItemCount: videoDetailController.listData.length, + itemBuilder: (context, index, animation) { + return videoDetailController.buildItem( + videoDetailController.listData[index], + animation, + ); + }, + ), + ), + + // for debug + // Positioned( + // right: 16, + // bottom: 75, + // child: FilledButton.tonal( + // onPressed: () { + // videoDetailController.onAddItem( + // SegmentModel( + // UUID: '', + // segmentType: + // SegmentType.values[Utils.random.nextInt( + // SegmentType.values.length, + // )], + // segment: Pair(first: 0, second: 0), + // skipType: SkipType.alwaysSkip, + // ), + // ); + // }, + // child: const Text('skip'), + // ), + // ), + // Positioned( + // right: 16, + // bottom: 120, + // child: FilledButton.tonal( + // onPressed: () { + // videoDetailController.onAddItem(2); + // }, + // child: const Text('index'), + // ), + // ), + Obx( + () { + if (videoDetailController.showSteinEdgeInfo.value) { + try { + return Align( + alignment: Alignment.bottomCenter, + child: Padding( + padding: EdgeInsets.only( + left: 16, + right: 16, + bottom: plPlayerController?.showControls.value == true + ? 75 + : 16, + ), + child: Wrap( + spacing: 25, + runSpacing: 10, + children: videoDetailController + .steinEdgeInfo! + .edges! + .questions! + .first + .choices! + .map((item) { + return FilledButton.tonal( + style: FilledButton.styleFrom( + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(6), + ), + ), + backgroundColor: themeData + .colorScheme + .secondaryContainer + .withValues(alpha: 0.8), + padding: const EdgeInsets.symmetric( + horizontal: 15, + vertical: 10, + ), + visualDensity: VisualDensity.compact, + tapTargetSize: MaterialTapTargetSize.shrinkWrap, + ), + onPressed: () { + ugcIntroController.onChangeEpisode( + item, + isStein: true, + ); + videoDetailController.getSteinEdgeInfo( + item.id, + ); + }, + child: Text(item.option!), + ); + }) + .toList(), + ), + ), + ); + } catch (e) { + if (kDebugMode) debugPrint('build stein edges: $e'); + return const SizedBox.shrink(); + } } return const SizedBox.shrink(); - }), - manualPlayerWidget, - - if (videoDetailController.plPlayerController.enableBlock || - videoDetailController.continuePlayingPart) - Positioned( - left: 16, - bottom: isFullScreen ? max(75, maxHeight * 0.25) : 75, - width: MediaQuery.textScalerOf(context).scale(120), - child: AnimatedList( - padding: EdgeInsets.zero, - key: videoDetailController.listKey, - reverse: true, - shrinkWrap: true, - initialItemCount: videoDetailController.listData.length, - itemBuilder: (context, index, animation) { - return videoDetailController.buildItem( - videoDetailController.listData[index], - animation, - ); - }, - ), - ), - - // for debug - // Positioned( - // right: 16, - // bottom: 75, - // child: FilledButton.tonal( - // onPressed: () { - // videoDetailController.onAddItem( - // SegmentModel( - // UUID: '', - // segmentType: - // SegmentType.values[Utils.random.nextInt( - // SegmentType.values.length, - // )], - // segment: Pair(first: 0, second: 0), - // skipType: SkipType.alwaysSkip, - // ), - // ); - // }, - // child: const Text('skip'), - // ), - // ), - // Positioned( - // right: 16, - // bottom: 120, - // child: FilledButton.tonal( - // onPressed: () { - // videoDetailController.onAddItem(2); - // }, - // child: const Text('index'), - // ), - // ), - Obx( - () { - if (videoDetailController.showSteinEdgeInfo.value) { - try { - return Align( - alignment: Alignment.bottomCenter, - child: Padding( - padding: EdgeInsets.only( - left: 16, - right: 16, - bottom: plPlayerController?.showControls.value == true - ? 75 - : 16, - ), - child: Wrap( - spacing: 25, - runSpacing: 10, - children: videoDetailController - .steinEdgeInfo! - .edges! - .questions! - .first - .choices! - .map((item) { - return FilledButton.tonal( - style: FilledButton.styleFrom( - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(6), - ), - ), - backgroundColor: themeData - .colorScheme - .secondaryContainer - .withValues(alpha: 0.8), - padding: const EdgeInsets.symmetric( - horizontal: 15, - vertical: 10, - ), - visualDensity: VisualDensity.compact, - tapTargetSize: - MaterialTapTargetSize.shrinkWrap, - ), - onPressed: () { - ugcIntroController.onChangeEpisode( - item, - isStein: true, - ); - videoDetailController.getSteinEdgeInfo( - item.id, - ); - }, - child: Text(item.option!), - ); - }) - .toList(), - ), - ), - ); - } catch (e) { - if (kDebugMode) debugPrint('build stein edges: $e'); - return const SizedBox.shrink(); - } - } - return const SizedBox.shrink(); - }, - ), - ], - ), + }, + ), + ], ); } @@ -2183,20 +2183,13 @@ class _VideoDetailPageVState extends State } void _onPopInvokedWithResult(bool didPop, result) { - if (didPop) { - videoDetailController.plPlayerController.disableAutoEnterPipIfNeeded(); - } - if (plPlayerController?.controlsLock.value == true) { - plPlayerController?.onLockControl(false); + if (plPlayerController?.onPopInvokedWithResult(didPop, result) ?? false) { return; } - if (isFullScreen) { - videoDetailController.plPlayerController.triggerFullScreen(status: false); - return; - } - if (!videoDetailController.horizontalScreen && !isPortrait) { + if (PlatformUtils.isMobile && + !videoDetailController.horizontalScreen && + !isPortrait) { verticalScreenForTwoSeconds(); - return; } } diff --git a/lib/pages/video/widgets/header_control.dart b/lib/pages/video/widgets/header_control.dart index 885004781..cdb043c16 100644 --- a/lib/pages/video/widgets/header_control.dart +++ b/lib/pages/video/widgets/header_control.dart @@ -1848,11 +1848,13 @@ class HeaderControlState extends State color: Colors.white, ), onPressed: () { - if (plPlayerController.isDesktopPip) { - plPlayerController.exitDesktopPip(); - } else if (isFullScreen) { - plPlayerController.triggerFullScreen(status: false); - } else if (PlatformUtils.isMobile && + if (plPlayerController.onPopInvokedWithResult( + false, + null, + )) { + return; + } + if (PlatformUtils.isMobile && !horizontalScreen && !isPortrait) { verticalScreenForTwoSeconds(); diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index 3ff3a63ed..d4d9aa808 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -1,6 +1,6 @@ -import 'dart:async'; -import 'dart:convert'; -import 'dart:io'; +import 'dart:async' show StreamSubscription, Timer; +import 'dart:convert' show ascii; +import 'dart:io' show Platform, File, Directory; import 'dart:math' show max, min; import 'dart:ui' as ui; @@ -36,7 +36,7 @@ import 'package:PiliPlus/utils/extension/num_ext.dart'; import 'package:PiliPlus/utils/extension/string_ext.dart'; import 'package:PiliPlus/utils/feed_back.dart'; import 'package:PiliPlus/utils/image_utils.dart'; -import 'package:PiliPlus/utils/page_utils.dart' show PageUtils; +import 'package:PiliPlus/utils/page_utils.dart'; import 'package:PiliPlus/utils/path_utils.dart'; import 'package:PiliPlus/utils/platform_utils.dart'; import 'package:PiliPlus/utils/storage.dart'; @@ -50,7 +50,8 @@ import 'package:easy_debounce/easy_throttle.dart'; import 'package:floating/floating.dart'; import 'package:flutter/foundation.dart' show kDebugMode; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; +import 'package:flutter/services.dart' + show rootBundle, HapticFeedback, Uint8List; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:flutter_volume_controller/flutter_volume_controller.dart'; import 'package:get/get.dart'; @@ -235,8 +236,6 @@ class PlPlayerController { return windowManager.setAlwaysOnTop(value); } - Offset initialFocalPoint = Offset.zero; - Future exitDesktopPip() { isDesktopPip = false; return Future.wait([ @@ -307,13 +306,13 @@ class PlPlayerController { } } - void disableAutoEnterPipIfNeeded() { + void _disableAutoEnterPipIfNeeded() { if (!_isPreviousVideoPage) { - disableAutoEnterPip(); + _disableAutoEnterPip(); } } - void disableAutoEnterPip() { + void _disableAutoEnterPip() { if (_shouldSetPip) { Utils.channel.invokeMethod('setPipAutoEnterEnabled', { 'autoEnable': false, @@ -1003,12 +1002,12 @@ class PlPlayerController { if (_isCurrVideoPage) { enterPip(isAuto: true); } else { - disableAutoEnterPip(); + _disableAutoEnterPip(); } } playerStatus.value = PlayerStatus.playing; } else { - disableAutoEnterPip(); + _disableAutoEnterPip(); playerStatus.value = PlayerStatus.paused; } videoPlayerServiceHandler?.onStatusChange( @@ -1698,7 +1697,7 @@ class PlPlayerController { danmakuController = null; _stopListenerForVideoFit(); _stopListenerForEnterFullScreen(); - disableAutoEnterPip(); + _disableAutoEnterPip(); setPlayCallBack(null); dmState.clear(); if (showSeekPreview) { @@ -1873,4 +1872,23 @@ class PlPlayerController { } }); } + + bool onPopInvokedWithResult(bool didPop, Object? result) { + if (Platform.isAndroid && didPop) { + _disableAutoEnterPipIfNeeded(); + } + if (controlsLock.value) { + onLockControl(false); + return true; + } + if (isDesktopPip) { + exitDesktopPip(); + return true; + } + if (isFullScreen.value) { + triggerFullScreen(status: false); + return true; + } + return false; + } } diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index be205ffc5..eee4b41d0 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -140,6 +140,8 @@ class _PLVideoPlayerState extends State GestureType? _gestureType; + Offset initialFocalPoint = Offset.zero; + //播放器放缩 bool interacting = false; @@ -941,7 +943,7 @@ class _PLVideoPlayerState extends State if (details.pointerCount > 1) { interacting = true; } - plPlayerController.initialFocalPoint = localFocalPoint; + initialFocalPoint = localFocalPoint; // if (kDebugMode) { // debugPrint("_initialFocalPoint$_initialFocalPoint"); // } @@ -951,11 +953,10 @@ class _PLVideoPlayerState extends State void _onInteractionUpdate(ScaleUpdateDetails details) { showRestoreScaleBtn.value = transformationController.value.storage[0] != 1.0; - if (interacting || plPlayerController.initialFocalPoint == Offset.zero) { + if (interacting || initialFocalPoint == Offset.zero) { return; } - Offset cumulativeDelta = - details.localFocalPoint - plPlayerController.initialFocalPoint; + Offset cumulativeDelta = details.localFocalPoint - initialFocalPoint; if (details.pointerCount > 1 && cumulativeDelta.distanceSquared < 2.25) { interacting = true; _gestureType = null; @@ -1083,8 +1084,7 @@ class _PLVideoPlayerState extends State } else if (_gestureType == GestureType.center) { // 全屏 const double threshold = 2.5; // 滑动阈值 - double cumulativeDy = - details.localFocalPoint.dy - plPlayerController.initialFocalPoint.dy; + double cumulativeDy = details.localFocalPoint.dy - initialFocalPoint.dy; void fullScreenTrigger(bool status) { plPlayerController.triggerFullScreen(status: status); @@ -1143,7 +1143,7 @@ class _PLVideoPlayerState extends State plPlayerController.onChangedSliderEnd(); } interacting = false; - plPlayerController.initialFocalPoint = Offset.zero; + initialFocalPoint = Offset.zero; _gestureType = null; } @@ -1261,9 +1261,7 @@ class _PLVideoPlayerState extends State status: !isFullScreen, inAppFullScreen: isSecondaryBtn, ) - .whenComplete( - () => plPlayerController.initialFocalPoint = Offset.zero, - ); + .whenComplete(() => initialFocalPoint = Offset.zero); return; } }