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

feature: Added settings to force the player into certain orientations #108

Merged
merged 3 commits into from
Nov 2, 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
8 changes: 7 additions & 1 deletion lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -1099,5 +1099,11 @@
"deleteFilterConfirmation": "Are you sure you want to delete this filter?",
"libraryFiltersLimitReached" : "Filter limit reached (10) remove some filters",
"libraryFiltersRemoveAll": "Remove all filters",
"libraryFiltersRemoveAllConfirm": "This will delete all saved filters for every library"
"libraryFiltersRemoveAllConfirm": "This will delete all saved filters for every library",
"playerSettingsOrientationTitle": "Player orientation",
"playerSettingsOrientationDesc": "Force the video player into certain orientations",
"deviceOrientationPortraitUp": "Portrait Up",
"deviceOrientationPortraitDown": "Portrait Down",
"deviceOrientationLandscapeLeft": "Landscape Left",
"deviceOrientationLandscapeRight": "Landscape Right"
}
2 changes: 2 additions & 0 deletions lib/models/settings/video_player_settings.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';

import 'package:freezed_annotation/freezed_annotation.dart';
Expand All @@ -20,6 +21,7 @@ class VideoPlayerSettingsModel with _$VideoPlayerSettingsModel {
@Default(true) bool hardwareAccel,
@Default(false) bool useLibass,
@Default(100) double internalVolume,
Set<DeviceOrientation>? allowedOrientations,
@Default(AutoNextType.static) AutoNextType nextVideoType,
String? audioDevice,
}) = _VideoPlayerSettingsModel;
Expand Down
35 changes: 33 additions & 2 deletions lib/models/settings/video_player_settings.freezed.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ mixin _$VideoPlayerSettingsModel {
bool get hardwareAccel => throw _privateConstructorUsedError;
bool get useLibass => throw _privateConstructorUsedError;
double get internalVolume => throw _privateConstructorUsedError;
Set<DeviceOrientation>? get allowedOrientations =>
throw _privateConstructorUsedError;
AutoNextType get nextVideoType => throw _privateConstructorUsedError;
String? get audioDevice => throw _privateConstructorUsedError;

Expand All @@ -53,6 +55,7 @@ abstract class $VideoPlayerSettingsModelCopyWith<$Res> {
bool hardwareAccel,
bool useLibass,
double internalVolume,
Set<DeviceOrientation>? allowedOrientations,
AutoNextType nextVideoType,
String? audioDevice});
}
Expand All @@ -79,6 +82,7 @@ class _$VideoPlayerSettingsModelCopyWithImpl<$Res,
Object? hardwareAccel = null,
Object? useLibass = null,
Object? internalVolume = null,
Object? allowedOrientations = freezed,
Object? nextVideoType = null,
Object? audioDevice = freezed,
}) {
Expand Down Expand Up @@ -107,6 +111,10 @@ class _$VideoPlayerSettingsModelCopyWithImpl<$Res,
? _value.internalVolume
: internalVolume // ignore: cast_nullable_to_non_nullable
as double,
allowedOrientations: freezed == allowedOrientations
? _value.allowedOrientations
: allowedOrientations // ignore: cast_nullable_to_non_nullable
as Set<DeviceOrientation>?,
nextVideoType: null == nextVideoType
? _value.nextVideoType
: nextVideoType // ignore: cast_nullable_to_non_nullable
Expand Down Expand Up @@ -135,6 +143,7 @@ abstract class _$$VideoPlayerSettingsModelImplCopyWith<$Res>
bool hardwareAccel,
bool useLibass,
double internalVolume,
Set<DeviceOrientation>? allowedOrientations,
AutoNextType nextVideoType,
String? audioDevice});
}
Expand All @@ -160,6 +169,7 @@ class __$$VideoPlayerSettingsModelImplCopyWithImpl<$Res>
Object? hardwareAccel = null,
Object? useLibass = null,
Object? internalVolume = null,
Object? allowedOrientations = freezed,
Object? nextVideoType = null,
Object? audioDevice = freezed,
}) {
Expand Down Expand Up @@ -188,6 +198,10 @@ class __$$VideoPlayerSettingsModelImplCopyWithImpl<$Res>
? _value.internalVolume
: internalVolume // ignore: cast_nullable_to_non_nullable
as double,
allowedOrientations: freezed == allowedOrientations
? _value._allowedOrientations
: allowedOrientations // ignore: cast_nullable_to_non_nullable
as Set<DeviceOrientation>?,
nextVideoType: null == nextVideoType
? _value.nextVideoType
: nextVideoType // ignore: cast_nullable_to_non_nullable
Expand All @@ -211,9 +225,11 @@ class _$VideoPlayerSettingsModelImpl extends _VideoPlayerSettingsModel
this.hardwareAccel = true,
this.useLibass = false,
this.internalVolume = 100,
final Set<DeviceOrientation>? allowedOrientations,
this.nextVideoType = AutoNextType.static,
this.audioDevice})
: super._();
: _allowedOrientations = allowedOrientations,
super._();

factory _$VideoPlayerSettingsModelImpl.fromJson(Map<String, dynamic> json) =>
_$$VideoPlayerSettingsModelImplFromJson(json);
Expand All @@ -235,6 +251,17 @@ class _$VideoPlayerSettingsModelImpl extends _VideoPlayerSettingsModel
@override
@JsonKey()
final double internalVolume;
final Set<DeviceOrientation>? _allowedOrientations;
@override
Set<DeviceOrientation>? get allowedOrientations {
final value = _allowedOrientations;
if (value == null) return null;
if (_allowedOrientations is EqualUnmodifiableSetView)
return _allowedOrientations;
// ignore: implicit_dynamic_type
return EqualUnmodifiableSetView(value);
}

@override
@JsonKey()
final AutoNextType nextVideoType;
Expand All @@ -243,7 +270,7 @@ class _$VideoPlayerSettingsModelImpl extends _VideoPlayerSettingsModel

@override
String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) {
return 'VideoPlayerSettingsModel(screenBrightness: $screenBrightness, videoFit: $videoFit, fillScreen: $fillScreen, hardwareAccel: $hardwareAccel, useLibass: $useLibass, internalVolume: $internalVolume, nextVideoType: $nextVideoType, audioDevice: $audioDevice)';
return 'VideoPlayerSettingsModel(screenBrightness: $screenBrightness, videoFit: $videoFit, fillScreen: $fillScreen, hardwareAccel: $hardwareAccel, useLibass: $useLibass, internalVolume: $internalVolume, allowedOrientations: $allowedOrientations, nextVideoType: $nextVideoType, audioDevice: $audioDevice)';
}

@override
Expand All @@ -257,6 +284,7 @@ class _$VideoPlayerSettingsModelImpl extends _VideoPlayerSettingsModel
..add(DiagnosticsProperty('hardwareAccel', hardwareAccel))
..add(DiagnosticsProperty('useLibass', useLibass))
..add(DiagnosticsProperty('internalVolume', internalVolume))
..add(DiagnosticsProperty('allowedOrientations', allowedOrientations))
..add(DiagnosticsProperty('nextVideoType', nextVideoType))
..add(DiagnosticsProperty('audioDevice', audioDevice));
}
Expand Down Expand Up @@ -286,6 +314,7 @@ abstract class _VideoPlayerSettingsModel extends VideoPlayerSettingsModel {
final bool hardwareAccel,
final bool useLibass,
final double internalVolume,
final Set<DeviceOrientation>? allowedOrientations,
final AutoNextType nextVideoType,
final String? audioDevice}) = _$VideoPlayerSettingsModelImpl;
_VideoPlayerSettingsModel._() : super._();
Expand All @@ -306,6 +335,8 @@ abstract class _VideoPlayerSettingsModel extends VideoPlayerSettingsModel {
@override
double get internalVolume;
@override
Set<DeviceOrientation>? get allowedOrientations;
@override
AutoNextType get nextVideoType;
@override
String? get audioDevice;
Expand Down
13 changes: 13 additions & 0 deletions lib/models/settings/video_player_settings.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion lib/providers/library_filters_provider.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions lib/providers/settings/video_player_settings_provider.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:screen_brightness/screen_brightness.dart';
Expand Down Expand Up @@ -63,4 +64,7 @@ class VideoPlayerSettingsProviderNotifier extends StateNotifier<VideoPlayerSetti
state = state.copyWith(internalVolume: value);
ref.read(videoPlayerProvider).setVolume(value);
}

void toggleOrientation(Set<DeviceOrientation>? orientation) =>
state = state.copyWith(allowedOrientations: orientation);
}
2 changes: 1 addition & 1 deletion lib/providers/user_provider.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions lib/screens/settings/player_settings_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import 'package:fladder/screens/settings/widgets/settings_label_divider.dart';
import 'package:fladder/screens/settings/widgets/settings_message_box.dart';
import 'package:fladder/screens/settings/widgets/subtitle_editor.dart';
import 'package:fladder/screens/shared/animated_fade_size.dart';
import 'package:fladder/screens/video_player/components/video_player_options_sheet.dart';
import 'package:fladder/util/adaptive_layout.dart';
import 'package:fladder/util/box_fit_extension.dart';
import 'package:fladder/util/localization_helper.dart';
Expand Down Expand Up @@ -151,6 +152,12 @@ class _PlayerSettingsPageState extends ConsumerState<PlayerSettingsPage> {
);
},
),
if (!AdaptiveLayout.of(context).isDesktop && !kIsWeb)
SettingsListTile(
label: Text(context.localized.playerSettingsOrientationTitle),
subLabel: Text(context.localized.playerSettingsOrientationDesc),
onTap: () => showOrientationOptions(context, ref),
),
],
),
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

import 'package:collection/collection.dart';
import 'package:ficonsax/ficonsax.dart';
Expand All @@ -19,6 +21,7 @@ import 'package:fladder/screens/playlists/add_to_playlists.dart';
import 'package:fladder/screens/video_player/components/video_player_queue.dart';
import 'package:fladder/screens/video_player/components/video_subtitle_controls.dart';
import 'package:fladder/util/adaptive_layout.dart';
import 'package:fladder/util/device_orientation_extension.dart';
import 'package:fladder/util/list_padding.dart';
import 'package:fladder/util/localization_helper.dart';
import 'package:fladder/util/refresh_state.dart';
Expand Down Expand Up @@ -176,6 +179,11 @@ class _VideoOptionsMobileState extends ConsumerState<VideoOptions> {
],
),
),
if (!AdaptiveLayout.of(context).isDesktop && !kIsWeb)
SpacedListTile(
title: Text(context.localized.playerSettingsOrientationTitle),
onTap: () => showOrientationOptions(context, ref),
),
ListTile(
onTap: () {
Navigator.of(context).pop();
Expand Down Expand Up @@ -492,3 +500,70 @@ Future<void> showPlaybackSpeed(BuildContext context) {
},
);
}

Future<void> showOrientationOptions(BuildContext context, WidgetRef ref) async {
Set<DeviceOrientation> orientations = ref
.read(videoPlayerSettingsProvider
.select((value) => value.allowedOrientations ?? Set.from(DeviceOrientation.values)))
.toSet();

void toggleOrientation(DeviceOrientation orientation) {
if (orientations.contains(orientation) && orientations.length > 1) {
orientations.remove(orientation);
} else {
orientations.add(orientation);
}
}

await showDialog(
context: context,
builder: (context) {
return StatefulBuilder(builder: (context, state) {
return SimpleDialog(
contentPadding: const EdgeInsets.only(top: 8, bottom: 24),
title: Row(children: [Text(context.localized.playbackRate)]),
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 12).copyWith(top: 6),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Divider(),
...DeviceOrientation.values.map(
(orientation) => CheckboxListTile.adaptive(
title: Text(orientation.label(context)),
value: orientations.contains(orientation),
onChanged: (value) {
state(() => toggleOrientation(orientation));
},
),
),
const Divider(),
Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.end,
children: [
ElevatedButton(
onPressed: () => Navigator.of(context).pop(),
child: Text(context.localized.cancel),
),
FilledButton(
onPressed: () {
Navigator.of(context).pop();
ref.read(videoPlayerSettingsProvider.notifier).toggleOrientation(orientations);
},
child: Text(context.localized.save),
),
].addInBetween(const SizedBox(width: 8)),
)
].addInBetween(const SizedBox(width: 8)),
),
)
],
);
});
},
);
}
14 changes: 14 additions & 0 deletions lib/screens/video_player/video_player.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'dart:async';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:media_kit_video/media_kit_video.dart';
Expand Down Expand Up @@ -49,6 +50,7 @@ class _VideoPlayerState extends ConsumerState<VideoPlayer> with WidgetsBindingOb
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
SystemChrome.setPreferredOrientations(DeviceOrientation.values);
super.dispose();
}

Expand All @@ -58,6 +60,9 @@ class _VideoPlayerState extends ConsumerState<VideoPlayer> with WidgetsBindingOb
WidgetsBinding.instance.addObserver(this);
Future.microtask(() {
ref.read(mediaPlaybackProvider.notifier).update((state) => state.copyWith(state: VideoPlayerState.fullScreen));
final orientations = ref.read(videoPlayerSettingsProvider.select((value) => value.allowedOrientations));
SystemChrome.setPreferredOrientations(
orientations?.isNotEmpty == true ? orientations!.toList() : DeviceOrientation.values);
return ref.read(videoPlayerSettingsProvider.notifier).setSavedBrightness();
});
}
Expand All @@ -70,6 +75,15 @@ class _VideoPlayerState extends ConsumerState<VideoPlayer> with WidgetsBindingOb

final playerController = ref.watch(videoPlayerProvider.select((value) => value.controller));

ref.listen(
videoPlayerSettingsProvider.select((value) => value.allowedOrientations),
(previous, next) {
if (previous != next) {
SystemChrome.setPreferredOrientations(next?.isNotEmpty == true ? next!.toList() : DeviceOrientation.values);
}
},
);

return Material(
color: Colors.black,
child: Theme(
Expand Down
13 changes: 13 additions & 0 deletions lib/util/device_orientation_extension.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

import 'package:fladder/util/localization_helper.dart';

extension DeviceOrientationExtension on DeviceOrientation {
String label(BuildContext context) => switch (this) {
DeviceOrientation.portraitUp => context.localized.deviceOrientationPortraitUp,
DeviceOrientation.landscapeLeft => context.localized.deviceOrientationLandscapeLeft,
DeviceOrientation.portraitDown => context.localized.deviceOrientationPortraitDown,
DeviceOrientation.landscapeRight => context.localized.deviceOrientationLandscapeRight,
};
}
Loading