From 90590faaaf3379c2c24d97312a2759c17c8688ff Mon Sep 17 00:00:00 2001 From: dom Date: Sun, 31 May 2026 18:40:29 +0800 Subject: [PATCH] refa pip Signed-off-by: dom --- .../com/example/piliplus/AndroidHelper.java | 28 ++++++-- .../live_room/widgets/header_control.dart | 3 +- lib/plugin/pl_player/controller.dart | 19 +++--- lib/plugin/pl_player/view/view.dart | 9 +++ lib/utils/android/android_helper.dart | 12 +++- lib/utils/android/bindings.g.dart | 65 +++++++++++++++---- lib/utils/extension/extension.dart | 14 ---- lib/utils/page_utils.dart | 37 ++++++----- pubspec.lock | 9 --- pubspec.yaml | 4 -- 10 files changed, 126 insertions(+), 74 deletions(-) delete mode 100644 lib/utils/extension/extension.dart diff --git a/android/app/src/main/java/com/example/piliplus/AndroidHelper.java b/android/app/src/main/java/com/example/piliplus/AndroidHelper.java index fbd0f568e..0ac096708 100644 --- a/android/app/src/main/java/com/example/piliplus/AndroidHelper.java +++ b/android/app/src/main/java/com/example/piliplus/AndroidHelper.java @@ -1,5 +1,6 @@ package com.example.piliplus; +import android.app.Activity; import android.app.PendingIntent; import android.app.PictureInPictureParams; import android.app.SearchManager; @@ -18,6 +19,7 @@ import android.net.Uri; import android.os.Build; import android.provider.MediaStore; import android.provider.Settings; +import android.util.Rational; import android.view.WindowManager; import androidx.annotation.Keep; @@ -142,12 +144,30 @@ public final class AndroidHelper { return false; } - public static void setPipAutoEnterEnabled(boolean autoEnable, long engineId) { + public static void enterPip(int width, int height, boolean autoEnter, long engineId) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + PictureInPictureParams.Builder builder = new PictureInPictureParams.Builder() + .setAspectRatio(new Rational(width, height)); + Activity activity = JniFlutterPlugin.getActivity(engineId); + assert activity != null; + if (autoEnter) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + builder.setAutoEnterEnabled(true); + activity.setPictureInPictureParams(builder.build()); + } + } else { + activity.enterPictureInPictureMode(builder.build()); + } + } + } + + public static void disableAutoEnterPip(long engineId) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - PictureInPictureParams params = new PictureInPictureParams.Builder().setAutoEnterEnabled(autoEnable).build(); - android.app.Activity activity = JniFlutterPlugin.getActivity(engineId); + PictureInPictureParams.Builder builder = new PictureInPictureParams.Builder() + .setAutoEnterEnabled(false); + Activity activity = JniFlutterPlugin.getActivity(engineId); if (activity != null) { - activity.setPictureInPictureParams(params); + activity.setPictureInPictureParams(builder.build()); } } } diff --git a/lib/pages/live_room/widgets/header_control.dart b/lib/pages/live_room/widgets/header_control.dart index 61175e83c..5e768a4f2 100644 --- a/lib/pages/live_room/widgets/header_control.dart +++ b/lib/pages/live_room/widgets/header_control.dart @@ -9,7 +9,6 @@ import 'package:PiliPlus/services/shutdown_timer_service.dart' show shutdownTimerService; import 'package:PiliPlus/utils/android/bindings.g.dart'; import 'package:PiliPlus/utils/platform_utils.dart'; -import 'package:floating/floating.dart'; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; @@ -160,7 +159,7 @@ class _LiveHeaderControlState extends State ComBtn( height: 30, tooltip: '画中画', - onTap: () { + onTap: () { if (PlatformUtils.isDesktop) { plPlayerController.toggleDesktopPip(); return; diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index 047266477..c8d5bfbf4 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -275,7 +275,7 @@ class PlPlayerController with BlockConfigMixin { } } - late bool _shouldSetPip = false; + late bool _isAutoEnterPip = false; static bool get _isCurrVideoPage { final routing = Get.routing; @@ -289,12 +289,11 @@ class PlPlayerController with BlockConfigMixin { return routeName == '/videoV' || routeName == '/liveRoom'; } - void enterPip({bool isAuto = false}) { + void enterPip({bool autoEnter = false}) { if (videoPlayerController != null) { - controls = false; final state = videoPlayerController!.state; PageUtils.enterPip( - isAuto: isAuto, + autoEnter: autoEnter, width: state.width == 0 ? width : state.width, height: state.height == 0 ? height : state.height, ); @@ -302,8 +301,8 @@ class PlPlayerController with BlockConfigMixin { } void _disableAutoEnterPip() { - if (_shouldSetPip) { - PiliAndroidHelper.setPipAutoEnterEnabled(false); + if (_isAutoEnterPip) { + PiliAndroidHelper.disableAutoEnterPip(); } } @@ -578,12 +577,12 @@ class PlPlayerController with BlockConfigMixin { } if (Platform.isAndroid && autoPiP) { - if (DeviceUtils.sdkInt < 36) { + if (DeviceUtils.sdkInt < 31) { AndroidHelper$ToDart.onUserLeaveHint = Runnable.implement( $Runnable(run: _onUserLeaveHint), ); } else { - _shouldSetPip = true; + _isAutoEnterPip = true; } } } @@ -942,9 +941,9 @@ class PlPlayerController with BlockConfigMixin { stream.playing.listen((event) { WakelockPlus.toggle(enable: event); if (event) { - if (_shouldSetPip) { + if (_isAutoEnterPip) { if (_isCurrVideoPage) { - enterPip(isAuto: true); + enterPip(autoEnter: true); } else { _disableAutoEnterPip(); } diff --git a/lib/plugin/pl_player/view/view.dart b/lib/plugin/pl_player/view/view.dart index b4a8b165e..c7b9c5354 100644 --- a/lib/plugin/pl_player/view/view.dart +++ b/lib/plugin/pl_player/view/view.dart @@ -52,6 +52,7 @@ import 'package:PiliPlus/plugin/pl_player/widgets/common_btn.dart'; import 'package:PiliPlus/plugin/pl_player/widgets/forward_seek.dart'; import 'package:PiliPlus/plugin/pl_player/widgets/mpv_convert_webp.dart'; import 'package:PiliPlus/plugin/pl_player/widgets/play_pause_btn.dart'; +import 'package:PiliPlus/utils/android/bindings.g.dart'; import 'package:PiliPlus/utils/connectivity_utils.dart'; import 'package:PiliPlus/utils/duration_utils.dart'; import 'package:PiliPlus/utils/extension/num_ext.dart'; @@ -967,6 +968,14 @@ class _PLVideoPlayerState extends State colorScheme = ColorScheme.of(context); } + @override + void didUpdateWidget(covariant PLVideoPlayer oldWidget) { + super.didUpdateWidget(oldWidget); + if (Platform.isAndroid && AndroidHelper.isPipMode) { + plPlayerController.controls = false; + } + } + void _onPanStart(ScaleStartDetails details) { _gestureType = null; _initialFocalPoint = details.localFocalPoint; diff --git a/lib/utils/android/android_helper.dart b/lib/utils/android/android_helper.dart index 0cbac42b4..4229b31e1 100644 --- a/lib/utils/android/android_helper.dart +++ b/lib/utils/android/android_helper.dart @@ -73,12 +73,18 @@ abstract final class PiliAndroidHelper { } @pragma('vm:prefer-inline') - static void setPipAutoEnterEnabled(bool autoEnable) => - AndroidHelper.setPipAutoEnterEnabled( - autoEnable, + static void enterPip(int width, int height, bool autoEnter) => + AndroidHelper.enterPip( + width, + height, + autoEnter, PlatformDispatcher.instance.engineId!, ); + @pragma('vm:prefer-inline') + static void disableAutoEnterPip() => + AndroidHelper.disableAutoEnterPip(PlatformDispatcher.instance.engineId!); + static (int, int)? maxScreenSize() { final jIArr = AndroidHelper.maxScreenSize(); if (jIArr != null) { diff --git a/lib/utils/android/bindings.g.dart b/lib/utils/android/bindings.g.dart index e8965ddc1..35cfc5e9e 100644 --- a/lib/utils/android/bindings.g.dart +++ b/lib/utils/android/bindings.g.dart @@ -453,18 +453,20 @@ extension type AndroidHelper._(jni$_.JObject _$this) implements jni$_.JObject { ).boolean; } - static final _id_setPipAutoEnterEnabled = _class.staticMethodId( - r'setPipAutoEnterEnabled', - r'(ZJ)V', + static final _id_enterPip = _class.staticMethodId( + r'enterPip', + r'(IIZJ)V', ); - static final _setPipAutoEnterEnabled = + static final _enterPip = jni$_.ProtectedJniExtensions.lookup< jni$_.NativeFunction< jni$_.JThrowablePtr Function( jni$_.Pointer, jni$_.JMethodIDPtr, - jni$_.VarArgs<(jni$_.Int32, jni$_.Int64)>, + jni$_.VarArgs< + (jni$_.Int32, jni$_.Int32, jni$_.Int32, jni$_.Int64) + >, ) > >('globalEnv_CallStaticVoidMethod') @@ -474,19 +476,60 @@ extension type AndroidHelper._(jni$_.JObject _$this) implements jni$_.JObject { jni$_.JMethodIDPtr, core$_.int, core$_.int, + core$_.int, + core$_.int, ) >(); - /// from: `static public void setPipAutoEnterEnabled(boolean autoEnable, long engineId)` - static void setPipAutoEnterEnabled( - core$_.bool autoEnable, + /// from: `static public void enterPip(int width, int height, boolean autoEnter, long engineId)` + static void enterPip( + core$_.int width, + core$_.int height, + core$_.bool autoEnter, core$_.int engineId, ) { final _$$classRef = _class.reference; - _setPipAutoEnterEnabled( + _enterPip( _$$classRef.pointer, - _id_setPipAutoEnterEnabled.pointer, - autoEnable ? 1 : 0, + _id_enterPip.pointer, + width, + height, + autoEnter ? 1 : 0, + engineId, + ).check(); + } + + static final _id_disableAutoEnterPip = _class.staticMethodId( + r'disableAutoEnterPip', + r'(J)V', + ); + + static final _disableAutoEnterPip = + jni$_.ProtectedJniExtensions.lookup< + jni$_.NativeFunction< + jni$_.JThrowablePtr Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + jni$_.VarArgs<(jni$_.Int64,)>, + ) + > + >('globalEnv_CallStaticVoidMethod') + .asFunction< + jni$_.JThrowablePtr Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + core$_.int, + ) + >(); + + /// from: `static public void disableAutoEnterPip(long engineId)` + static void disableAutoEnterPip( + core$_.int engineId, + ) { + final _$$classRef = _class.reference; + _disableAutoEnterPip( + _$$classRef.pointer, + _id_disableAutoEnterPip.pointer, engineId, ).check(); } diff --git a/lib/utils/extension/extension.dart b/lib/utils/extension/extension.dart deleted file mode 100644 index 086c4d898..000000000 --- a/lib/utils/extension/extension.dart +++ /dev/null @@ -1,14 +0,0 @@ -import 'package:floating/floating.dart'; - -extension RationalExt on Rational { - /// Checks whether given [Rational] instance fits into Android requirements - /// or not. - /// - /// Android docs specified boundaries as inclusive. - bool get fitsInAndroidRequirements { - final aspectRatio = numerator / denominator; - const min = 1 / 2.39; - const max = 2.39; - return (min <= aspectRatio) && (aspectRatio <= max); - } -} diff --git a/lib/utils/page_utils.dart b/lib/utils/page_utils.dart index 8dc7ff666..be553733c 100644 --- a/lib/utils/page_utils.dart +++ b/lib/utils/page_utils.dart @@ -17,9 +17,9 @@ import 'package:PiliPlus/pages/common/publish/publish_route.dart'; import 'package:PiliPlus/pages/contact/view.dart'; import 'package:PiliPlus/pages/fav_panel/view.dart'; import 'package:PiliPlus/pages/share/view.dart'; +import 'package:PiliPlus/utils/android/android_helper.dart'; import 'package:PiliPlus/utils/app_scheme.dart'; import 'package:PiliPlus/utils/extension/context_ext.dart'; -import 'package:PiliPlus/utils/extension/extension.dart'; import 'package:PiliPlus/utils/extension/size_ext.dart'; import 'package:PiliPlus/utils/extension/string_ext.dart'; import 'package:PiliPlus/utils/feed_back.dart'; @@ -30,7 +30,6 @@ import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:PiliPlus/utils/url_utils.dart'; import 'package:PiliPlus/utils/utils.dart'; import 'package:collection/collection.dart'; -import 'package:floating/floating.dart'; import 'package:flutter/foundation.dart' show kDebugMode; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; @@ -187,22 +186,26 @@ abstract final class PageUtils { ); } - static void enterPip({int? width, int? height, bool isAuto = false}) { - if (width != null && height != null) { - Rational aspectRatio = Rational(width, height); - aspectRatio = aspectRatio.fitsInAndroidRequirements - ? aspectRatio - : height > width - ? const Rational.vertical() - : const Rational.landscape(); - Floating.enable( - isAuto - ? AutoEnable(aspectRatio: aspectRatio) - : EnableManual(aspectRatio: aspectRatio), - ); - } else { - Floating.enable(isAuto ? const AutoEnable() : const EnableManual()); + static bool _fitsInAndroidRequirements(int width, int height) { + final aspectRatio = width / height; + const min = 1 / 2.39; + const max = 2.39; + return (min <= aspectRatio) && (aspectRatio <= max); + } + + static void enterPip({int? width, int? height, bool autoEnter = false}) { + if (width != null && + height != null && + !_fitsInAndroidRequirements(width, height)) { + if (height > width) { + width = 9; + height = 16; + } else { + width = 16; + height = 9; + } } + PiliAndroidHelper.enterPip(width ?? 16, height ?? 9, autoEnter); } static Future pushDynDetail( diff --git a/pubspec.lock b/pubspec.lock index d844373cb..b65f4747f 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -591,15 +591,6 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.1" - floating: - dependency: "direct main" - description: - path: "." - ref: dev - resolved-ref: fdd44a501d9717ebab04e8004fca2818a6bed236 - url: "https://github.com/bggRGjQaUbCoE/floating.git" - source: git - version: "3.0.0" flutter: dependency: "direct main" description: flutter diff --git a/pubspec.yaml b/pubspec.yaml index ef83b48b5..df7f469ff 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -83,10 +83,6 @@ dependencies: fixnum: ^1.1.1 fl_chart: ^1.0.0 flex_seed_scheme: ^4.0.1 - floating: - git: - url: https://github.com/bggRGjQaUbCoE/floating.git - ref: dev flutter_cache_manager: ^3.4.1 flutter_displaymode: ^0.7.0 flutter_html: ^3.0.0-beta.2