From a40c3fca0f802e3c1ee3b3237b28a075daffa72f Mon Sep 17 00:00:00 2001 From: office-pc nix root Date: Sat, 5 Oct 2024 11:32:03 +0800 Subject: [PATCH] make screen brightness and volume as a setting make volume and brightness controls relative to current values instead of relative to the position of the finger on the screen only enable the sliders if the user moved at least 5% of the screen add icon change depending on the value fix #604 --- lib/l10n/app_en.arb | 6 +- lib/player/states/player_controls.dart | 61 +++++++------ .../states/player_controls.freezed.dart | 90 ++++++++++++++----- .../views/components/player_controls.dart | 18 ++-- .../components/system_setting_slider.dart | 32 ++++++- lib/settings/models/db/settings.dart | 1 + lib/settings/states/settings.dart | 7 +- lib/settings/views/screens/video_player.dart | 7 ++ pubspec.yaml | 2 +- 9 files changed, 160 insertions(+), 64 deletions(-) diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index ee2ca03c..84103d69 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -1367,7 +1367,7 @@ "customHeadersExplanation": "Set custom headers to be sent to the invidious server", "value": "Value", "testAndAddServer": "Test and add server", - "alsoTestServerConfig":"Also test server configuration, like if thumbnails would display properly", + "alsoTestServerConfig": "Also test server configuration, like if thumbnails would display properly", "serverAlreadyExists": "Server already exists in settings", "wrongThumbnailConfiguration": "The server is reachable but is not configured properly, the video and channel thumbnails will not be displayed properly. Disable the server test configuration if you are OK with this, fix your server otherwise", "openWikiLink": "Open wiki for help", @@ -1379,5 +1379,7 @@ "stopTheVideo": "Stop the video", "stopTheVideoExplanation": "If enabled, the video will be closed, if disabled the video will be simply paused", "setTimer": "Set timer", - "cancelSleepTimer": "Cancel sleep timer" + "cancelSleepTimer": "Cancel sleep timer", + "screenControls": "Screen controls", + "screenControlsExplanation": "When watching a video in full screen, Vertically dragging from the left or the right will adjust the brightness or volume respectively" } diff --git a/lib/player/states/player_controls.dart b/lib/player/states/player_controls.dart index 112a99a3..f8398972 100644 --- a/lib/player/states/player_controls.dart +++ b/lib/player/states/player_controls.dart @@ -1,4 +1,4 @@ -import 'dart:ui'; +import 'dart:math'; import 'package:bloc/bloc.dart'; import 'package:clipious/player/models/media_event.dart'; @@ -194,59 +194,56 @@ class PlayerControlsCubit extends Cubit { }); } - Future startBrightnessAdjustments() async { + Future startBrightnessAdjustments(DragStartDetails details) async { final currentBrightness = await ScreenBrightness().current; emit(state.copyWith( - systemBrightness: currentBrightness, showBrightnessSlider: true)); + systemBrightness: currentBrightness, + screenControlStartValue: currentBrightness, + screenControlStart: details.localPosition.dy)); } Future updateBrightness( DragUpdateDetails details, BoxConstraints constraints) async { - // Set upper and lower bounds (75% of the screen for full brightness, bottom 25% for zero brightness) - double upperBound = 0.25; // top 75% is full brightness - double lowerBound = 0.75; // bottom is 0 brightness + // percentage of the screen we moved since we started dragging + double movedBy = (state.screenControlStart - details.localPosition.dy) / + constraints.maxHeight; - // Clamping the brightness based on the drag position - double normalizedPosition = - (details.localPosition.dy / constraints.maxHeight) - .clamp(upperBound, lowerBound); + if (movedBy.abs() > 0.05 || state.showBrightnessSlider) { + double screenBrightness = + min(1, max(0, state.screenControlStartValue + movedBy)); - // Linearly interpolate between 1 (full brightness) and 0 (no brightness) - final screenBrightness = lerpDouble( - 1, 0, (normalizedPosition - upperBound) / (lowerBound - upperBound))!; - - await ScreenBrightness().setScreenBrightness(screenBrightness); - emit(state.copyWith(systemBrightness: screenBrightness)); + await ScreenBrightness().setScreenBrightness(screenBrightness); + emit(state.copyWith( + systemBrightness: screenBrightness, showBrightnessSlider: true)); + } } void stopBrightnessAdjustments() { - emit(state.copyWith(showBrightnessSlider: false)); + emit(state.copyWith(showBrightnessSlider: false, screenControlStart: 0)); } - Future startVolumeAdjustments() async { + Future startVolumeAdjustments(DragStartDetails details) async { await FlutterVolumeController.updateShowSystemUI(false); final currentVolume = await FlutterVolumeController.getVolume(); emit(state.copyWith( - systemVolume: currentVolume ?? 0, showVolumeSlider: true)); + systemVolume: currentVolume ?? 0, + screenControlStartValue: currentVolume ?? 0, + screenControlStart: details.localPosition.dy)); } Future updateVolume( DragUpdateDetails details, BoxConstraints constraints) async { // Set upper and lower bounds (75% of the screen for full volume, bottom 25% for zero volume) - double upperBound = 0.25; // top 75% is full volume - double lowerBound = 0.75; // bottom is 0 volume + // percentage of the screen we moved since we started dragging + double movedBy = (state.screenControlStart - details.localPosition.dy) / + constraints.maxHeight; - // Clamping the volume based on the drag position - double normalizedPosition = - (details.localPosition.dy / constraints.maxHeight) - .clamp(upperBound, lowerBound); + if (movedBy.abs() > 0.05 || state.showVolumeSlider) { + double volume = min(1, max(0, state.screenControlStartValue + movedBy)); - // Linearly interpolate between 1 (full volume) and 0 (no volume) - final volume = lerpDouble( - 1, 0, (normalizedPosition - upperBound) / (lowerBound - upperBound))!; - - await FlutterVolumeController.setVolume(volume); - emit(state.copyWith(systemVolume: volume)); + await FlutterVolumeController.setVolume(volume); + emit(state.copyWith(systemVolume: volume, showVolumeSlider: true)); + } } void stopVolumeAdjustments() { @@ -270,6 +267,8 @@ class PlayerControlsState with _$PlayerControlsState { @Default(0) double doubleTapRewindedOpacity, @Default(false) bool justDoubleTappedSkip, @Default(false) bool showSponsorBlocked, + @Default(0) double screenControlStart, + @Default(0) double screenControlStartValue, // system setting adjustments @Default(false) bool showBrightnessSlider, @Default(0) double systemBrightness, diff --git a/lib/player/states/player_controls.freezed.dart b/lib/player/states/player_controls.freezed.dart index 76a912a3..f8c8b0d4 100644 --- a/lib/player/states/player_controls.freezed.dart +++ b/lib/player/states/player_controls.freezed.dart @@ -30,6 +30,9 @@ mixin _$PlayerControlsState { double get doubleTapRewindedOpacity => throw _privateConstructorUsedError; bool get justDoubleTappedSkip => throw _privateConstructorUsedError; bool get showSponsorBlocked => throw _privateConstructorUsedError; + double get screenControlStart => throw _privateConstructorUsedError; + double get screenControlStartValue => + throw _privateConstructorUsedError; // system setting adjustments bool get showBrightnessSlider => throw _privateConstructorUsedError; double get systemBrightness => throw _privateConstructorUsedError; bool get showVolumeSlider => throw _privateConstructorUsedError; @@ -62,6 +65,8 @@ abstract class $PlayerControlsStateCopyWith<$Res> { double doubleTapRewindedOpacity, bool justDoubleTappedSkip, bool showSponsorBlocked, + double screenControlStart, + double screenControlStartValue, bool showBrightnessSlider, double systemBrightness, bool showVolumeSlider, @@ -96,6 +101,8 @@ class _$PlayerControlsStateCopyWithImpl<$Res, $Val extends PlayerControlsState> Object? doubleTapRewindedOpacity = null, Object? justDoubleTappedSkip = null, Object? showSponsorBlocked = null, + Object? screenControlStart = null, + Object? screenControlStartValue = null, Object? showBrightnessSlider = null, Object? systemBrightness = null, Object? showVolumeSlider = null, @@ -154,6 +161,14 @@ class _$PlayerControlsStateCopyWithImpl<$Res, $Val extends PlayerControlsState> ? _value.showSponsorBlocked : showSponsorBlocked // ignore: cast_nullable_to_non_nullable as bool, + screenControlStart: null == screenControlStart + ? _value.screenControlStart + : screenControlStart // ignore: cast_nullable_to_non_nullable + as double, + screenControlStartValue: null == screenControlStartValue + ? _value.screenControlStartValue + : screenControlStartValue // ignore: cast_nullable_to_non_nullable + as double, showBrightnessSlider: null == showBrightnessSlider ? _value.showBrightnessSlider : showBrightnessSlider // ignore: cast_nullable_to_non_nullable @@ -196,6 +211,8 @@ abstract class _$$PlayercontrolsStateImplCopyWith<$Res> double doubleTapRewindedOpacity, bool justDoubleTappedSkip, bool showSponsorBlocked, + double screenControlStart, + double screenControlStartValue, bool showBrightnessSlider, double systemBrightness, bool showVolumeSlider, @@ -228,6 +245,8 @@ class __$$PlayercontrolsStateImplCopyWithImpl<$Res> Object? doubleTapRewindedOpacity = null, Object? justDoubleTappedSkip = null, Object? showSponsorBlocked = null, + Object? screenControlStart = null, + Object? screenControlStartValue = null, Object? showBrightnessSlider = null, Object? systemBrightness = null, Object? showVolumeSlider = null, @@ -286,6 +305,14 @@ class __$$PlayercontrolsStateImplCopyWithImpl<$Res> ? _value.showSponsorBlocked : showSponsorBlocked // ignore: cast_nullable_to_non_nullable as bool, + screenControlStart: null == screenControlStart + ? _value.screenControlStart + : screenControlStart // ignore: cast_nullable_to_non_nullable + as double, + screenControlStartValue: null == screenControlStartValue + ? _value.screenControlStartValue + : screenControlStartValue // ignore: cast_nullable_to_non_nullable + as double, showBrightnessSlider: null == showBrightnessSlider ? _value.showBrightnessSlider : showBrightnessSlider // ignore: cast_nullable_to_non_nullable @@ -323,6 +350,8 @@ class _$PlayercontrolsStateImpl implements _PlayercontrolsState { this.doubleTapRewindedOpacity = 0, this.justDoubleTappedSkip = false, this.showSponsorBlocked = false, + this.screenControlStart = 0, + this.screenControlStartValue = 0, this.showBrightnessSlider = false, this.systemBrightness = 0, this.showVolumeSlider = false, @@ -367,6 +396,13 @@ class _$PlayercontrolsStateImpl implements _PlayercontrolsState { @override @JsonKey() final bool showSponsorBlocked; + @override + @JsonKey() + final double screenControlStart; + @override + @JsonKey() + final double screenControlStartValue; +// system setting adjustments @override @JsonKey() final bool showBrightnessSlider; @@ -382,7 +418,7 @@ class _$PlayercontrolsStateImpl implements _PlayercontrolsState { @override String toString() { - return 'PlayerControlsState(errored: $errored, position: $position, duration: $duration, buffer: $buffer, fullScreenState: $fullScreenState, displayControls: $displayControls, muted: $muted, buffering: $buffering, draggingPositionSlider: $draggingPositionSlider, doubleTapFastForwardedOpacity: $doubleTapFastForwardedOpacity, doubleTapRewindedOpacity: $doubleTapRewindedOpacity, justDoubleTappedSkip: $justDoubleTappedSkip, showSponsorBlocked: $showSponsorBlocked, showBrightnessSlider: $showBrightnessSlider, systemBrightness: $systemBrightness, showVolumeSlider: $showVolumeSlider, systemVolume: $systemVolume)'; + return 'PlayerControlsState(errored: $errored, position: $position, duration: $duration, buffer: $buffer, fullScreenState: $fullScreenState, displayControls: $displayControls, muted: $muted, buffering: $buffering, draggingPositionSlider: $draggingPositionSlider, doubleTapFastForwardedOpacity: $doubleTapFastForwardedOpacity, doubleTapRewindedOpacity: $doubleTapRewindedOpacity, justDoubleTappedSkip: $justDoubleTappedSkip, showSponsorBlocked: $showSponsorBlocked, screenControlStart: $screenControlStart, screenControlStartValue: $screenControlStartValue, showBrightnessSlider: $showBrightnessSlider, systemBrightness: $systemBrightness, showVolumeSlider: $showVolumeSlider, systemVolume: $systemVolume)'; } @override @@ -416,6 +452,11 @@ class _$PlayercontrolsStateImpl implements _PlayercontrolsState { other.justDoubleTappedSkip == justDoubleTappedSkip) && (identical(other.showSponsorBlocked, showSponsorBlocked) || other.showSponsorBlocked == showSponsorBlocked) && + (identical(other.screenControlStart, screenControlStart) || + other.screenControlStart == screenControlStart) && + (identical( + other.screenControlStartValue, screenControlStartValue) || + other.screenControlStartValue == screenControlStartValue) && (identical(other.showBrightnessSlider, showBrightnessSlider) || other.showBrightnessSlider == showBrightnessSlider) && (identical(other.systemBrightness, systemBrightness) || @@ -427,25 +468,28 @@ class _$PlayercontrolsStateImpl implements _PlayercontrolsState { } @override - int get hashCode => Object.hash( - runtimeType, - errored, - position, - duration, - buffer, - fullScreenState, - displayControls, - muted, - buffering, - draggingPositionSlider, - doubleTapFastForwardedOpacity, - doubleTapRewindedOpacity, - justDoubleTappedSkip, - showSponsorBlocked, - showBrightnessSlider, - systemBrightness, - showVolumeSlider, - systemVolume); + int get hashCode => Object.hashAll([ + runtimeType, + errored, + position, + duration, + buffer, + fullScreenState, + displayControls, + muted, + buffering, + draggingPositionSlider, + doubleTapFastForwardedOpacity, + doubleTapRewindedOpacity, + justDoubleTappedSkip, + showSponsorBlocked, + screenControlStart, + screenControlStartValue, + showBrightnessSlider, + systemBrightness, + showVolumeSlider, + systemVolume + ]); /// Create a copy of PlayerControlsState /// with the given fields replaced by the non-null parameter values. @@ -472,6 +516,8 @@ abstract class _PlayercontrolsState implements PlayerControlsState { final double doubleTapRewindedOpacity, final bool justDoubleTappedSkip, final bool showSponsorBlocked, + final double screenControlStart, + final double screenControlStartValue, final bool showBrightnessSlider, final double systemBrightness, final bool showVolumeSlider, @@ -504,6 +550,10 @@ abstract class _PlayercontrolsState implements PlayerControlsState { @override bool get showSponsorBlocked; @override + double get screenControlStart; + @override + double get screenControlStartValue; // system setting adjustments + @override bool get showBrightnessSlider; @override double get systemBrightness; diff --git a/lib/player/views/components/player_controls.dart b/lib/player/views/components/player_controls.dart index c539015f..6c4c1b0d 100644 --- a/lib/player/views/components/player_controls.dart +++ b/lib/player/views/components/player_controls.dart @@ -249,6 +249,10 @@ class PlayerControls extends StatelessWidget { .select((PlayerCubit cubit) => cubit.state.totalFastForward); int totalRewind = context.select((PlayerCubit cubit) => cubit.state.totalRewind); + + bool screenControlsEnabled = context.select( + (SettingsCubit settings) => settings.state.screenControls); + String videoTitle = context.select((PlayerCubit cubit) => cubit.state.currentlyPlaying?.title ?? cubit.state.offlineCurrentlyPlaying?.title ?? @@ -265,9 +269,9 @@ class PlayerControls extends StatelessWidget { var cubit = context.read(); // to allow or not dragging to adjust brightness and volume - var canDragToAdjustDeviceSettings = + var canDragToAdjustDeviceSettings = screenControlsEnabled && playerState.fullScreenState == FullScreenState.fullScreen && - !playerState.displayControls; + !playerState.displayControls; return BlocListener( listenWhen: (previous, current) => @@ -352,7 +356,8 @@ class PlayerControls extends StatelessWidget { canDragToAdjustDeviceSettings ? (details) { cubit - .startBrightnessAdjustments(); + .startBrightnessAdjustments( + details); } : null, onVerticalDragUpdate: @@ -386,7 +391,8 @@ class PlayerControls extends StatelessWidget { canDragToAdjustDeviceSettings ? (details) { cubit - .startVolumeAdjustments(); + .startVolumeAdjustments( + details); } : null, onVerticalDragUpdate: @@ -701,7 +707,7 @@ class PlayerControls extends StatelessWidget { bottom: constraints.maxHeight * 0.15, right: 20, child: SystemSettingsSlider( - icon: Icons.brightness_5, + type: SystemSliderType.brightness, value: playerState.systemBrightness)) .animate( target: playerState.showBrightnessSlider ? 1 : 0, @@ -721,7 +727,7 @@ class PlayerControls extends StatelessWidget { bottom: constraints.maxHeight * 0.15, left: 20, child: SystemSettingsSlider( - icon: Icons.volume_up, + type: SystemSliderType.volume, value: playerState.systemVolume)) .animate( target: playerState.showVolumeSlider ? 1 : 0, diff --git a/lib/player/views/components/system_setting_slider.dart b/lib/player/views/components/system_setting_slider.dart index 8604a373..388a30e8 100644 --- a/lib/player/views/components/system_setting_slider.dart +++ b/lib/player/views/components/system_setting_slider.dart @@ -1,12 +1,38 @@ import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; +enum SystemSliderType { + volume, + brightness; + + IconData getIcon(double value) { + switch (this) { + case brightness: + if (value <= 0.3) { + return Icons.brightness_low; + } else if (value <= 0.7) { + return Icons.brightness_medium; + } else { + return Icons.brightness_high; + } + case volume: + if (value == 0) { + return Icons.volume_off; + } else if (value <= 0.7) { + return Icons.volume_down; + } else { + return Icons.volume_up; + } + } + } +} + class SystemSettingsSlider extends StatelessWidget { - final IconData icon; + final SystemSliderType type; final double value; const SystemSettingsSlider( - {super.key, required this.icon, required this.value}); + {super.key, required this.value, required this.type}); @override Widget build(BuildContext context) { @@ -42,7 +68,7 @@ class SystemSettingsSlider extends StatelessWidget { ), const Gap(10), Icon( - icon, + type.getIcon(value), color: colors.primary, size: 12, ) diff --git a/lib/settings/models/db/settings.dart b/lib/settings/models/db/settings.dart index 9f49ba32..2fca4547 100644 --- a/lib/settings/models/db/settings.dart +++ b/lib/settings/models/db/settings.dart @@ -40,6 +40,7 @@ const subtitleBackground = 'subtitle-background'; const dearrowSettingName = 'dearrow'; const dearrowThumbnailsSettingName = "dearrow-thumbnails"; const fullScreenOnLandscapeSettingName = "fullscreen-on-landscape"; +const screenControlsSettingName = "screen-controls"; const onOpenSettingName = "on-open"; diff --git a/lib/settings/states/settings.dart b/lib/settings/states/settings.dart index 742ca342..fbec7307 100644 --- a/lib/settings/states/settings.dart +++ b/lib/settings/states/settings.dart @@ -411,6 +411,8 @@ class SettingsCubit extends Cubit { setFullscreenOnRotate(bool b) async => await _set(fullScreenOnLandscapeSettingName, b); + setScreenControls(bool b) async => await _set(screenControlsSettingName, b); + setReturnYoutubeDislikeUrl(String url) async => await _set(returnYoutubeDislikeUrlSettingName, url); @@ -527,7 +529,7 @@ class SettingsState with _$SettingsState { bool get useDynamicTheme => _get(dynamicTheme)?.value == 'true'; - bool get useDash => _get(useDashSettingName)?.value == 'true'; + bool get useDash => (_get(useDashSettingName)?.value ?? 'true') == 'true'; bool get autoplayVideoOnLoad => _get(playerAutoplayOnLoad)?.value == 'true'; @@ -559,6 +561,9 @@ class SettingsState with _$SettingsState { bool get useSearchHistory => _get(useSearchHistorySettingName)?.value == 'true'; + bool get screenControls => + (_get(screenControlsSettingName)?.value ?? 'true') == 'true'; + List get appLayout { var savedLayout = _get(appLayoutSettingName)?.value; // String? savedLayout; diff --git a/lib/settings/views/screens/video_player.dart b/lib/settings/views/screens/video_player.dart index 726abf71..58138343 100644 --- a/lib/settings/views/screens/video_player.dart +++ b/lib/settings/views/screens/video_player.dart @@ -93,6 +93,13 @@ class VideoPlayerSettingsScreen extends StatelessWidget { title: Text(locals.fillFullscreen), description: Text(locals.fillFullscreenDescription), ), + SettingsTile.switchTile( + leading: const Icon(Icons.tune), + initialValue: state.screenControls, + onToggle: cubit.setScreenControls, + title: Text(locals.screenControls), + description: Text(locals.screenControlsExplanation), + ), ]), SettingsSection(title: Text(locals.seeking), tiles: [ SettingsTile( diff --git a/pubspec.yaml b/pubspec.yaml index 140dd23d..c66fb323 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: clipious -version: 1.22.1+4064 +version: 1.22.2+4065 publish_to: none description: Client for invidious. environment: