Skip to content
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
## New
- Tilt for fullscreen setting: turn the phone sideways to enter fullscreen even with rotation locked, off by default (#1219)
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ class PlayerPreferences(
val QUEUE_AUTOPLAY_ENABLED = booleanPreferencesKey("queue_autoplay_enabled")
val START_VIDEOS_PAUSED = booleanPreferencesKey("start_videos_paused")
val OPEN_VIDEOS_FULLSCREEN = booleanPreferencesKey("open_videos_fullscreen")
val TILT_FULLSCREEN_ROTATION_LOCKED = booleanPreferencesKey("tilt_fullscreen_rotation_locked")
val RESUME_FROM_HEADPHONES = booleanPreferencesKey("resume_from_headphones")
val MUSIC_ENDLESS_RADIO_ENABLED = booleanPreferencesKey("music_endless_radio_enabled")
val AUTOPLAY_COUNTDOWN_SECONDS = intPreferencesKey("autoplay_countdown_seconds")
Expand Down Expand Up @@ -1641,6 +1642,17 @@ class PlayerPreferences(
}
}

/** Turning the phone sideways enters fullscreen even while the system rotation lock is on. */
val tiltFullscreenRotationLocked: Flow<Boolean> =
context.playerPreferencesDataStore.data
.map { preferences -> preferences[Keys.TILT_FULLSCREEN_ROTATION_LOCKED] ?: false }

suspend fun setTiltFullscreenRotationLocked(enabled: Boolean) {
context.playerPreferencesDataStore.edit { preferences ->
preferences[Keys.TILT_FULLSCREEN_ROTATION_LOCKED] = enabled
}
}

/** A Play button on headphones may start the last songs after Flow was closed. */
val resumeFromHeadphones: Flow<Boolean> =
context.playerPreferencesDataStore.data
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,8 @@ fun VideoPlayerHost(
isFullscreen = screenState.isFullscreen,
videoAspectRatio = effectiveVideoAspectRatio,
isPortraitFullscreen = screenState.isFullscreenPortrait,
ignoreRotationLock =
prefs.tiltFullscreenRotationLocked && !isLargeWindow && !context.orientationRequestIgnored(config),
onEnterFullscreen = { screenState.isFullscreen = true },
onExitFullscreen = {
screenState.isFullscreen = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ internal fun OrientationListenerEffect(
isFullscreen: Boolean,
videoAspectRatio: Float = 16f / 9f,
isPortraitFullscreen: Boolean = false,
ignoreRotationLock: Boolean = false,
onEnterFullscreen: () -> Unit,
onExitFullscreen: () -> Unit,
) {
Expand All @@ -197,6 +198,7 @@ internal fun OrientationListenerEffect(
val currentIsFullscreen by rememberUpdatedState(isFullscreen)
val currentAspectRatio by rememberUpdatedState(videoAspectRatio)
val currentIsPortraitFullscreen by rememberUpdatedState(isPortraitFullscreen)
val currentIgnoreRotationLock by rememberUpdatedState(ignoreRotationLock)
val currentEnter by rememberUpdatedState(onEnterFullscreen)
val currentExit by rememberUpdatedState(onExitFullscreen)

Expand All @@ -206,14 +208,15 @@ internal fun OrientationListenerEffect(
override fun onOrientationChanged(orientation: Int) {
if (orientation == ORIENTATION_UNKNOWN) return

val autoRotateOn =
val autoRotateOn = {
try {
Settings.System.getInt(context.contentResolver, Settings.System.ACCELEROMETER_ROTATION) == 1
} catch (e: Exception) {
true
}
}

if (!autoRotateOn) return
if (!followsTilt(currentIgnoreRotationLock, autoRotateOn)) return

val newOrientation = heldOrientation(orientation, physicalOrientation)

Expand All @@ -233,18 +236,18 @@ internal fun OrientationListenerEffect(

LaunchedEffect(physicalOrientation, isExpanded) {
delay(150)
if (physicalOrientation != 1 || !isExpanded) return@LaunchedEffect
if (currentIsPortraitFullscreen || currentAspectRatio < 1f) return@LaunchedEffect
if (!currentIsFullscreen) currentEnter()
}

if (physicalOrientation == -1) return@LaunchedEffect

val isVerticalVideo = currentAspectRatio < 1f

if (currentIsPortraitFullscreen) return@LaunchedEffect

if (physicalOrientation == 1 && isExpanded && !currentIsFullscreen && !isVerticalVideo) {
currentEnter()
} else if (physicalOrientation == 0 && currentIsFullscreen && !isVerticalVideo) {
currentExit()
}
// Exit answers only the phone turning upright, not the sheet expanding: "Open videos in
// fullscreen" enters as the sheet settles, and a re-run on that flip left it again 150 ms later.
LaunchedEffect(physicalOrientation) {
delay(150)
if (physicalOrientation != 0) return@LaunchedEffect
if (currentIsPortraitFullscreen || currentAspectRatio < 1f) return@LaunchedEffect
if (currentIsFullscreen) currentExit()
}
}

Expand All @@ -262,3 +265,13 @@ internal fun heldOrientation(
in 0..30, in 330..359 -> 0
else -> previous
}

/**
* Whether a tilt may move the player in or out of fullscreen. Fullscreen asks for
* SENSOR_LANDSCAPE, which turns the window even under the system rotation lock, so the lock only
* matters when the viewer has not asked to tilt through it (#1219).
*/
internal fun followsTilt(
ignoreRotationLock: Boolean,
autoRotateOn: () -> Boolean,
): Boolean = ignoreRotationLock || autoRotateOn()
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ internal class VideoPlayerPreferencesState(
val relatedCardStyle: PlayerRelatedCardStyle,
val feedColumns: HomeFeedColumns,
val deArrowEnabled: Boolean,
val tiltFullscreenRotationLocked: Boolean,
)

@Composable
Expand Down Expand Up @@ -91,6 +92,7 @@ internal fun rememberVideoPlayerPreferences(context: Context): VideoPlayerPrefer
val relatedCardStyle by playerPreferences.playerRelatedCardStyle.collectAsState(initial = PlayerRelatedCardStyle.FULL_WIDTH)
val feedColumns by playerPreferences.homeFeedColumns.collectAsState(initial = HomeFeedColumns.AUTO)
val deArrowEnabled by playerPreferences.deArrowEnabled.collectAsState(initial = false)
val tiltFullscreenRotationLocked by playerPreferences.tiltFullscreenRotationLocked.collectAsState(initial = false)

return VideoPlayerPreferencesState(
preferences = playerPreferences,
Expand Down Expand Up @@ -124,5 +126,6 @@ internal fun rememberVideoPlayerPreferences(context: Context): VideoPlayerPrefer
relatedCardStyle = relatedCardStyle,
feedColumns = feedColumns,
deArrowEnabled = deArrowEnabled,
tiltFullscreenRotationLocked = tiltFullscreenRotationLocked,
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package io.github.aedev.flow.ui.screens.settings.index

import io.github.aedev.flow.R
import io.github.aedev.flow.ui.components.settings.SettingEntry
import io.github.aedev.flow.ui.components.settings.SettingsDestination

internal object PlaybackGesturesIndex {
private val page = SettingsDestination.PLAYBACK

val doubleTapSeek =
SettingEntry(
key = "playback.double_tap_seek",
title = R.string.player_settings_double_tap_seek,
keywords = R.string.settings_keywords_gestures,
section = R.string.player_appearance_gestures_header,
destination = page,
)
val seekZoneWidth =
SettingEntry(
key = "playback.seek_zone_width",
title = R.string.player_settings_seek_zone_width,
keywords = R.string.settings_keywords_gestures,
section = R.string.player_appearance_gestures_header,
destination = page,
)
val brightnessGesture =
SettingEntry(
key = "playback.brightness_gesture",
title = R.string.player_appearance_brightness_gesture_title,
summary = R.string.player_appearance_brightness_gesture_subtitle,
keywords = R.string.settings_keywords_gestures,
section = R.string.player_appearance_gestures_header,
destination = page,
)
val rememberBrightness =
SettingEntry(
key = "playback.remember_brightness",
title = R.string.player_appearance_remember_brightness_title,
summary = R.string.player_appearance_remember_brightness_subtitle,
section = R.string.player_appearance_gestures_header,
destination = page,
)
val volumeGesture =
SettingEntry(
key = "playback.volume_gesture",
title = R.string.player_appearance_volume_gesture_title,
summary = R.string.player_appearance_volume_gesture_subtitle,
keywords = R.string.settings_keywords_gestures,
section = R.string.player_appearance_gestures_header,
destination = page,
)
val volumeBoost =
SettingEntry(
key = "playback.volume_boost",
title = R.string.player_appearance_volume_boost_title,
summary = R.string.player_appearance_volume_boost_subtitle,
section = R.string.player_appearance_gestures_header,
destination = page,
)
val speedSwipe =
SettingEntry(
key = "playback.speed_swipe",
title = R.string.player_settings_speed_swipe,
keywords = R.string.settings_keywords_gestures,
section = R.string.player_appearance_gestures_header,
destination = page,
)
val speedSwipeStep =
SettingEntry(
key = "playback.speed_swipe_step",
title = R.string.player_settings_speed_swipe_step,
section = R.string.player_appearance_gestures_header,
destination = page,
)
val seekGesture =
SettingEntry(
key = "playback.seek_gesture",
title = R.string.player_appearance_seek_gesture_title,
summary = R.string.player_appearance_seek_gesture_subtitle,
keywords = R.string.settings_keywords_gestures,
section = R.string.player_appearance_gestures_header,
destination = page,
)
val haptics =
SettingEntry(
key = "playback.haptics",
title = R.string.player_appearance_haptics_title,
summary = R.string.player_appearance_haptics_subtitle,
section = R.string.player_appearance_gestures_header,
destination = page,
)
val controlsWhileLoading =
SettingEntry(
key = "playback.controls_while_loading",
title = R.string.player_appearance_controls_while_loading_title,
summary = R.string.player_appearance_controls_while_loading_subtitle,
section = R.string.player_appearance_gestures_header,
destination = page,
)

val all =
listOf(
doubleTapSeek,
seekZoneWidth,
brightnessGesture,
rememberBrightness,
volumeGesture,
volumeBoost,
speedSwipe,
speedSwipeStep,
seekGesture,
haptics,
controlsWhileLoading,
)
}
Loading
Loading