enable native video player on Android
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
d048875490
commit
8d5ccb699e
7 changed files with 72 additions and 11 deletions
|
|
@ -79,7 +79,7 @@ class _VideoChatState extends State<VideoChat>
|
|||
);
|
||||
|
||||
late final VideoPlayerInterface _videoStore =
|
||||
Platform.isIOS && context.settingsStore.useNativePlayer
|
||||
context.settingsStore.useNativePlayer
|
||||
? NativeVideoStore(
|
||||
userLogin: widget.userLogin,
|
||||
userId: widget.userId,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:better_native_video_player/better_native_video_player.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
|
@ -13,6 +14,7 @@ import 'package:frosty/screens/settings/stores/auth_store.dart';
|
|||
import 'package:frosty/screens/settings/stores/settings_store.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:simple_pip_mode/simple_pip.dart';
|
||||
|
||||
part 'native_video_store.g.dart';
|
||||
|
||||
|
|
@ -43,6 +45,9 @@ abstract class NativeVideoStoreBase with Store implements VideoPlayerInterface {
|
|||
var _firstTimeSettingQuality = true;
|
||||
int? _pendingQualityIndex;
|
||||
|
||||
final _pip = SimplePip();
|
||||
ReactionDisposer? _disposeAndroidAutoPipReaction;
|
||||
|
||||
StreamSubscription<bool>? _pipSub;
|
||||
StreamSubscription<List<NativeVideoPlayerQuality>>? _qualitiesSub;
|
||||
|
||||
|
|
@ -96,6 +101,16 @@ abstract class NativeVideoStoreBase with Store implements VideoPlayerInterface {
|
|||
_scheduleOverlayHide();
|
||||
updateStreamInfo();
|
||||
_initPlayer();
|
||||
|
||||
if (Platform.isAndroid) {
|
||||
_disposeAndroidAutoPipReaction = autorun((_) async {
|
||||
if (settingsStore.showVideo && await SimplePip.isAutoPipAvailable) {
|
||||
_pip.setAutoPipMode();
|
||||
} else {
|
||||
_pip.setAutoPipMode(autoEnter: false);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
|
|
@ -337,16 +352,20 @@ abstract class NativeVideoStoreBase with Store implements VideoPlayerInterface {
|
|||
|
||||
@override
|
||||
void requestPictureInPicture() {
|
||||
_controller?.enterPictureInPicture();
|
||||
if (Platform.isAndroid) {
|
||||
_pip.enterPipMode(autoEnter: true);
|
||||
} else {
|
||||
_controller?.enterPictureInPicture();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
@action
|
||||
void togglePictureInPicture() {
|
||||
if (_isInPipMode) {
|
||||
if (Platform.isIOS && _isInPipMode) {
|
||||
_controller?.exitPictureInPicture();
|
||||
} else {
|
||||
_controller?.enterPictureInPicture();
|
||||
requestPictureInPicture();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -434,10 +453,17 @@ abstract class NativeVideoStoreBase with Store implements VideoPlayerInterface {
|
|||
@override
|
||||
@action
|
||||
void dispose() {
|
||||
if (Platform.isAndroid) {
|
||||
SimplePip.isAutoPipAvailable.then((isAutoPipAvailable) {
|
||||
if (isAutoPipAvailable) _pip.setAutoPipMode(autoEnter: false);
|
||||
});
|
||||
}
|
||||
|
||||
_overlayTimer?.cancel();
|
||||
_latencyTimer?.cancel();
|
||||
_pipSub?.cancel();
|
||||
_qualitiesSub?.cancel();
|
||||
_disposeAndroidAutoPipReaction?.call();
|
||||
|
||||
_controller?.removeActivityListener(_handleActivityEvent);
|
||||
_controller?.dispose();
|
||||
|
|
|
|||
|
|
@ -24,11 +24,11 @@ class VideoSettings extends StatelessWidget {
|
|||
value: settingsStore.showVideo,
|
||||
onChanged: (newValue) => settingsStore.showVideo = newValue,
|
||||
),
|
||||
if (Platform.isIOS && settingsStore.showVideo)
|
||||
if (settingsStore.showVideo)
|
||||
SettingsListSwitch(
|
||||
title: 'Native player (experimental)',
|
||||
subtitle: const Text(
|
||||
'Native video player with auto picture-in-picture and quality selection.',
|
||||
'More performant video player with auto picture-in-picture and quality selection.',
|
||||
),
|
||||
value: settingsStore.useNativePlayer,
|
||||
onChanged: (newValue) =>
|
||||
|
|
@ -74,7 +74,8 @@ class VideoSettings extends StatelessWidget {
|
|||
'Allows switching between Twitch\'s overlay and the custom overlay.',
|
||||
),
|
||||
value: settingsStore.toggleableOverlay,
|
||||
onChanged: (newValue) => settingsStore.toggleableOverlay = newValue,
|
||||
onChanged: (newValue) =>
|
||||
settingsStore.toggleableOverlay = newValue,
|
||||
),
|
||||
SettingsListSwitch(
|
||||
title: 'Show latency',
|
||||
|
|
|
|||
|
|
@ -1,12 +1,9 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
class CookieExtractor {
|
||||
static const _channel = MethodChannel('frosty/cookie_extractor');
|
||||
|
||||
static Future<String?> extractTwitchAuthToken() async {
|
||||
if (!Platform.isIOS) return null;
|
||||
return await _channel.invokeMethod<String>('extractTwitchAuthToken');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue