Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Video Player preferences playing mode (InApp, InAppBrowser,External) #1563

Merged
merged 4 commits into from
Nov 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions lib/core/enums/local_settings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,7 @@ enum LocalSettings {
videoAutoFullscreen(name: 'video_auto_fullscreen', key: 'videoAutoFullscreen', category: LocalSettingsCategories.videoPlayer, subCategory: LocalSettingsSubCategories.videoPlayer),
videoAutoLoop(name: 'video_auto_loop', key: '', category: LocalSettingsCategories.videoPlayer, subCategory: LocalSettingsSubCategories.videoPlayer),
videoAutoPlay(name: 'video_auto_play', key: '', category: LocalSettingsCategories.videoPlayer, subCategory: LocalSettingsSubCategories.videoPlayer),
videoPlayerMode(name: 'setting_video_player_mode', key: 'videoPlayerMode', category: LocalSettingsCategories.videoPlayer, subCategory: LocalSettingsSubCategories.videoPlayer),

// Searchable settings
// The settings under this section do not correspond to settings that we persist in SharedPreferences.
Expand Down Expand Up @@ -522,6 +523,7 @@ extension LocalizationExt on AppLocalizations {
'videoAutoLoop': videoAutoLoop,
'videoAutoPlay': videoAutoPlay,
'videoDefaultPlaybackSpeed': videoDefaultPlaybackSpeed,
'videoPlayerMode': videoPlayerMode,
'userLabels': userLabels,
'accountDisplayName': displayName,
'accountProfileBio': profileBio,
Expand Down
5 changes: 5 additions & 0 deletions lib/core/enums/video_player_mode.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
enum VideoPlayerMode {
inApp,
customTabs,
externalPlayer,
}
12 changes: 12 additions & 0 deletions lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -2787,6 +2787,18 @@
"@videoDefaultPlaybackSpeed": {
"description": "Option to select the default video playback Speed"
},
"videoLinkHandlingExternal": "Play video with an external app",
"@videoLinkHandlingExternal": {
"description": "Description to Open video with an external application"
},
"videoPlayerInApp": "Use Thunder built-in player",
"@videoPlayerInApp": {
"description": "Description for using in-app video player"
},
"videoPlayerMode": "Player Mode",
"@videoPlayerMode": {
"description": "Option to select video player mode (in-app or external) "
},
"viewAll": "View all",
"@viewAll": {
"description": "A title for viewing all of something (e.g., instances)"
Expand Down
32 changes: 31 additions & 1 deletion lib/settings/pages/video_player_settings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:thunder/core/enums/local_settings.dart';
import 'package:thunder/core/enums/video_auto_play.dart';
import 'package:thunder/core/enums/video_playback_speed.dart';
import 'package:thunder/core/enums/video_player_mode.dart';
import 'package:thunder/core/singletons/preferences.dart';
import 'package:thunder/settings/widgets/list_option.dart';
import 'package:thunder/settings/widgets/toggle_option.dart';
Expand Down Expand Up @@ -45,6 +46,9 @@ class _VideoPlayerSettingsPageState extends State<VideoPlayerSettingsPage> {
/// Option as to how fast the video playback speed should be (.25,.5 ... 2)
VideoPlayBackSpeed videoDefaultPlaybackSpeed = VideoPlayBackSpeed.normal;

/// Option to select video player mode (in-app or external)
VideoPlayerMode videoPlayerMode = VideoPlayerMode.inApp;

@override
void initState() {
WidgetsBinding.instance.addPostFrameCallback((_) {
Expand Down Expand Up @@ -94,11 +98,14 @@ class _VideoPlayerSettingsPageState extends State<VideoPlayerSettingsPage> {
await prefs.setString(LocalSettings.videoAutoPlay.name, value);
setState(() => videoAutoPlay = VideoAutoPlay.values.byName(value ?? VideoAutoPlay.never));
break;

case LocalSettings.videoDefaultPlaybackSpeed:
await prefs.setString(LocalSettings.videoDefaultPlaybackSpeed.name, value);
setState(() => videoDefaultPlaybackSpeed = VideoPlayBackSpeed.values.byName(value ?? VideoPlayBackSpeed.normal));
break;
case LocalSettings.videoPlayerMode:
await prefs.setString(LocalSettings.videoPlayerMode.name, value);
setState(() => videoPlayerMode = VideoPlayerMode.values.byName(value ?? VideoPlayerMode.inApp));
break;
default:
}

Expand All @@ -115,6 +122,7 @@ class _VideoPlayerSettingsPageState extends State<VideoPlayerSettingsPage> {
videoAutoLoop = prefs.getBool(LocalSettings.videoAutoLoop.name) ?? false;
videoAutoPlay = VideoAutoPlay.values.byName(prefs.getString(LocalSettings.videoAutoPlay.name) ?? VideoAutoPlay.never.name);
videoDefaultPlaybackSpeed = VideoPlayBackSpeed.values.byName(prefs.getString(LocalSettings.videoDefaultPlaybackSpeed.name) ?? VideoPlayBackSpeed.normal.name);
videoPlayerMode = VideoPlayerMode.values.byName(prefs.getString(LocalSettings.videoPlayerMode.name) ?? VideoPlayerMode.inApp.name);
isLoading = false;
});
}
Expand Down Expand Up @@ -202,6 +210,28 @@ class _VideoPlayerSettingsPageState extends State<VideoPlayerSettingsPage> {
setting: LocalSettings.videoDefaultPlaybackSpeed,
highlightedSetting: settingToHighlight,
),
ListOption(
description: l10n.videoPlayerMode,
value: ListPickerItem(
label: switch (videoPlayerMode) {
VideoPlayerMode.inApp => l10n.videoPlayerInApp,
VideoPlayerMode.customTabs => l10n.linkHandlingCustomTabsShort,
VideoPlayerMode.externalPlayer => l10n.linkHandlingExternalShort,
},
payload: videoPlayerMode,
capitalizeLabel: false,
),
options: [
ListPickerItem(label: l10n.videoPlayerInApp, icon: Icons.play_circle_fill, payload: VideoPlayerMode.inApp),
ListPickerItem(label: l10n.linkHandlingCustomTabs, icon: Icons.language_rounded, payload: VideoPlayerMode.customTabs),
ListPickerItem(label: l10n.videoLinkHandlingExternal, icon: Icons.open_in_browser_rounded, payload: VideoPlayerMode.externalPlayer),
],
icon: Icons.video_label_outlined,
onChanged: (value) => setPreferences(LocalSettings.videoPlayerMode, value.payload.name),
highlightKey: settingToHighlightKey,
setting: LocalSettings.videoPlayerMode,
highlightedSetting: settingToHighlight,
),
],
),
),
Expand Down
3 changes: 3 additions & 0 deletions lib/thunder/bloc/thunder_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import 'package:thunder/core/enums/full_name.dart';
import 'package:thunder/core/enums/image_caching_mode.dart';
import 'package:thunder/core/enums/local_settings.dart';
import 'package:thunder/core/enums/nested_comment_indicator.dart';
import 'package:thunder/core/enums/video_player_mode.dart';
import 'package:thunder/notification/enums/notification_type.dart';
import 'package:thunder/core/enums/post_body_view_type.dart';
import 'package:thunder/core/enums/swipe_action.dart';
Expand Down Expand Up @@ -261,6 +262,7 @@ class ThunderBloc extends Bloc<ThunderEvent, ThunderState> {
bool videoAutoMute = prefs.getBool(LocalSettings.videoAutoMute.name) ?? true;
VideoAutoPlay videoAutoPlay = VideoAutoPlay.values.byName(prefs.getString(LocalSettings.videoAutoPlay.name) ?? VideoAutoPlay.never.name);
VideoPlayBackSpeed videoDefaultPlaybackSpeed = VideoPlayBackSpeed.values.byName(prefs.getString(LocalSettings.videoDefaultPlaybackSpeed.name) ?? VideoPlayBackSpeed.normal.name);
VideoPlayerMode videoPlayerMode = VideoPlayerMode.values.byName(prefs.getString(LocalSettings.videoPlayerMode.name) ?? VideoPlayerMode.inApp.name);

String currentAnonymousInstance = prefs.getString(LocalSettings.currentAnonymousInstance.name) ?? 'lemmy.ml';

Expand Down Expand Up @@ -429,6 +431,7 @@ class ThunderBloc extends Bloc<ThunderEvent, ThunderState> {
videoAutoLoop: videoAutoLoop,
videoAutoPlay: videoAutoPlay,
videoDefaultPlaybackSpeed: videoDefaultPlaybackSpeed,
videoPlayerMode: videoPlayerMode,

currentAnonymousInstance: currentAnonymousInstance,
));
Expand Down
5 changes: 5 additions & 0 deletions lib/thunder/bloc/thunder_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ class ThunderState extends Equatable {
this.videoAutoMute = true,
this.videoAutoPlay = VideoAutoPlay.never,
this.videoDefaultPlaybackSpeed = VideoPlayBackSpeed.normal,
this.videoPlayerMode = VideoPlayerMode.inApp,

/// -------------------------- Accessibility Related Settings --------------------------
this.reduceAnimations = false,
Expand Down Expand Up @@ -349,6 +350,7 @@ class ThunderState extends Equatable {
final bool videoAutoMute;
final VideoAutoPlay videoAutoPlay;
final VideoPlayBackSpeed videoDefaultPlaybackSpeed;
final VideoPlayerMode videoPlayerMode;

/// --------------------------------- UI Events ---------------------------------
// Expand/Close FAB event
Expand Down Expand Up @@ -522,6 +524,7 @@ class ThunderState extends Equatable {
bool? videoAutoMute,
VideoAutoPlay? videoAutoPlay,
VideoPlayBackSpeed? videoDefaultPlaybackSpeed,
VideoPlayerMode? videoPlayerMode,

/// --------------------------------- UI Events ---------------------------------
// Expand/Close FAB event
Expand Down Expand Up @@ -698,6 +701,7 @@ class ThunderState extends Equatable {
videoAutoMute: videoAutoMute ?? this.videoAutoMute,
videoAutoPlay: videoAutoPlay ?? this.videoAutoPlay,
videoDefaultPlaybackSpeed: videoDefaultPlaybackSpeed ?? this.videoDefaultPlaybackSpeed,
videoPlayerMode: videoPlayerMode ?? this.videoPlayerMode,
currentAnonymousInstance: currentAnonymousInstance,

/// ------------------ Video Player ------------------------
Expand Down Expand Up @@ -872,6 +876,7 @@ class ThunderState extends Equatable {
videoAutoMute,
videoAutoPlay,
videoDefaultPlaybackSpeed,
videoPlayerMode,

/// -------------------------- Accessibility Related Settings --------------------------
reduceAnimations,
Expand Down
31 changes: 28 additions & 3 deletions lib/utils/links.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import 'package:link_preview_generator/link_preview_generator.dart';
import 'package:share_plus/share_plus.dart';
import 'package:swipeable_page_route/swipeable_page_route.dart';
import 'package:thunder/core/enums/browser_mode.dart';
import 'package:thunder/core/enums/video_player_mode.dart';
import 'package:thunder/feed/bloc/feed_bloc.dart';
import 'package:thunder/instances.dart';
import 'package:thunder/modlog/utils/navigate_modlog.dart';
Expand Down Expand Up @@ -73,14 +74,31 @@ Future<LinkInfo> getLinkInfo(String url) async {
}
}

void _openLink(BuildContext context, {required String url}) async {
void _openLink(BuildContext context, {required String url, bool isVideo = false}) async {
ThunderState state = context.read<ThunderBloc>().state;

if (state.browserMode == BrowserMode.external || (!kIsWeb && !Platform.isAndroid && !Platform.isIOS)) {
bool launchInExternalApp = false;
bool launchInCustomTab = false;

if (isVideo && state.videoPlayerMode == VideoPlayerMode.externalPlayer) {
launchInExternalApp = true;
} else if (!isVideo && state.browserMode == BrowserMode.external) {
launchInExternalApp = true;
}

if (isVideo && state.videoPlayerMode == VideoPlayerMode.customTabs) {
launchInCustomTab = true;
} else if (!isVideo && state.browserMode == BrowserMode.customTabs) {
launchInCustomTab = true;
}

if (launchInExternalApp || (!kIsWeb && !Platform.isAndroid && !Platform.isIOS)) {
hideLoadingPage(context, delay: true);
url_launcher.launchUrl(Uri.parse(url), mode: url_launcher.LaunchMode.externalApplication);
} else if (state.browserMode == BrowserMode.customTabs) {
} else if (launchInCustomTab) {
// Launches the link within a custom tab
hideLoadingPage(context, delay: true);

launchUrl(
Uri.parse(url),
customTabsOptions: CustomTabsOptions(
Expand All @@ -105,8 +123,10 @@ void _openLink(BuildContext context, {required String url}) async {
),
);
} else if (state.browserMode == BrowserMode.inApp) {
// Launches the link within the in-app browser if possible
// Check if the scheme is not https, in which case the in-app browser can't handle it
Uri? uri = Uri.tryParse(url);

if (uri != null && uri.scheme != 'https') {
// Although a non-https scheme is an indication that this link is intended for another app,
// we actually have to change it back to https in order for the intent to be properly passed to another app.
Expand Down Expand Up @@ -254,6 +274,11 @@ void handleLink(BuildContext context, {required String url, bool forceOpenInBrow
}
}

/// A universal way of handling video links by opening them in the browser/external player
void handleVideoLink(BuildContext context, {required String url}) async {
_openLink(context, url: url, isVideo: isVideoUrl(url));
}

/// This is a helper method which helps [handleLink] determine whether a link refers to a valid Lemmy community.
/// If the passed in link is not a valid URI, then there's no point in doing any fallback, so assume it passes.
/// If the passed in [instance] is a known Lemmy instance, then it passes.
Expand Down
40 changes: 27 additions & 13 deletions lib/utils/media/video.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:thunder/core/enums/video_player_mode.dart';
import 'package:thunder/thunder/bloc/thunder_bloc.dart';
import 'package:thunder/utils/links.dart';

import 'package:youtube_player_flutter/youtube_player_flutter.dart';

Expand Down Expand Up @@ -38,17 +42,27 @@ void showVideoPlayer(BuildContext context, {String? url, int? postId}) {

String? videoId = YoutubePlayer.convertUrlToId(url);

Navigator.of(context).push(
PageRouteBuilder(
opaque: false,
transitionDuration: const Duration(milliseconds: 100),
reverseTransitionDuration: const Duration(milliseconds: 50),
pageBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
return (videoId != null) ? ThunderYoutubePlayer(videoUrl: url, postId: postId) : ThunderVideoPlayer(videoUrl: url, postId: postId);
},
transitionsBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child) {
return Align(child: FadeTransition(opacity: animation, child: child));
},
),
);
final thunderState = context.read<ThunderBloc>().state;

switch (thunderState.videoPlayerMode) {
case VideoPlayerMode.inApp:
Navigator.of(context).push(
PageRouteBuilder(
opaque: false,
transitionDuration: const Duration(milliseconds: 100),
reverseTransitionDuration: const Duration(milliseconds: 50),
pageBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
return (videoId != null) ? ThunderYoutubePlayer(videoUrl: url, postId: postId) : ThunderVideoPlayer(videoUrl: url, postId: postId);
},
transitionsBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child) {
return Align(child: FadeTransition(opacity: animation, child: child));
},
),
);
break;
case VideoPlayerMode.externalPlayer:
handleVideoLink(context, url: url);
case VideoPlayerMode.customTabs:
handleVideoLink(context, url: url);
}
}