mirror of
https://github.com/bggRGjQaUbCoE/PiliPlus.git
synced 2026-06-01 16:48:16 +08:00
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<LiveHeaderControl>
|
||||
ComBtn(
|
||||
height: 30,
|
||||
tooltip: '画中画',
|
||||
onTap: () {
|
||||
onTap: () {
|
||||
if (PlatformUtils.isDesktop) {
|
||||
plPlayerController.toggleDesktopPip();
|
||||
return;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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<PLVideoPlayer>
|
||||
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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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$_.Void>,
|
||||
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$_.Void>,
|
||||
jni$_.JMethodIDPtr,
|
||||
jni$_.VarArgs<(jni$_.Int64,)>,
|
||||
)
|
||||
>
|
||||
>('globalEnv_CallStaticVoidMethod')
|
||||
.asFunction<
|
||||
jni$_.JThrowablePtr Function(
|
||||
jni$_.Pointer<jni$_.Void>,
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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<void> pushDynDetail(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user