Add ability to unblur backdrops (#5216)

* Add preference for enabling background blur on backdrops

* value for strings.xml for enabling backdrop blur feature

* Respect preference option for enabling backdrop blur feature

* update new customization screen

* Added Enum for backdrop state

* Added a new screen for CustomizationBackdrops

* Combined backdrop enabled and blur into a single behavior

modified type to enum , removed checkbox, instead added a subroute, updated BackgroundService to reflect new type values

* rename pref variable and run migration

* Misc fixes

---------

Co-authored-by: Niels van Velzen <git@ndat.nl>
This commit is contained in:
Akhil
2026-04-05 10:56:17 -05:00
committed by GitHub
parent 48b5d262bc
commit de74f641ce
7 changed files with 92 additions and 11 deletions

View File

@@ -15,6 +15,7 @@ import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.jellyfin.androidtv.auth.model.Server import org.jellyfin.androidtv.auth.model.Server
import org.jellyfin.androidtv.preference.UserPreferences import org.jellyfin.androidtv.preference.UserPreferences
import org.jellyfin.androidtv.preference.constant.BackdropBehavior
import org.jellyfin.androidtv.util.apiclient.getUrl import org.jellyfin.androidtv.util.apiclient.getUrl
import org.jellyfin.androidtv.util.apiclient.itemBackdropImages import org.jellyfin.androidtv.util.apiclient.itemBackdropImages
import org.jellyfin.androidtv.util.apiclient.parentBackdropImages import org.jellyfin.androidtv.util.apiclient.parentBackdropImages
@@ -59,12 +60,14 @@ class BackgroundService(
* Use all available backdrops from [baseItem] as background. * Use all available backdrops from [baseItem] as background.
*/ */
fun setBackground(baseItem: BaseItemDto?) { fun setBackground(baseItem: BaseItemDto?) {
val backdropBehavior = userPreferences[UserPreferences.backdropBehavior]
// Check if item is set and backgrounds are enabled // Check if item is set and backgrounds are enabled
if (baseItem == null || !userPreferences[UserPreferences.backdropEnabled]) if (baseItem == null || backdropBehavior == BackdropBehavior.DISABLED)
return clearBackgrounds() return clearBackgrounds()
// Enable blur for backdrops // Enable blur for backdrops
_blurBackground.value = true _blurBackground.value = backdropBehavior == BackdropBehavior.BACKDROP_WITH_BLUR
// Get all backdrop urls // Get all backdrop urls
val backdropUrls = (baseItem.itemBackdropImages + baseItem.parentBackdropImages) val backdropUrls = (baseItem.itemBackdropImages + baseItem.parentBackdropImages)
@@ -79,7 +82,7 @@ class BackgroundService(
*/ */
fun setBackground(server: Server) { fun setBackground(server: Server) {
// Check if item is set and backgrounds are enabled // Check if item is set and backgrounds are enabled
if (!userPreferences[UserPreferences.backdropEnabled]) if (userPreferences[UserPreferences.backdropBehavior] == BackdropBehavior.DISABLED)
return clearBackgrounds() return clearBackgrounds()
// Check if splashscreen is enabled in (cached) branding options // Check if splashscreen is enabled in (cached) branding options

View File

@@ -5,6 +5,7 @@ import androidx.preference.PreferenceManager
import org.jellyfin.androidtv.preference.UserPreferences.Companion.screensaverInAppEnabled import org.jellyfin.androidtv.preference.UserPreferences.Companion.screensaverInAppEnabled
import org.jellyfin.androidtv.preference.constant.AppTheme import org.jellyfin.androidtv.preference.constant.AppTheme
import org.jellyfin.androidtv.preference.constant.AudioBehavior import org.jellyfin.androidtv.preference.constant.AudioBehavior
import org.jellyfin.androidtv.preference.constant.BackdropBehavior
import org.jellyfin.androidtv.preference.constant.ClockBehavior import org.jellyfin.androidtv.preference.constant.ClockBehavior
import org.jellyfin.androidtv.preference.constant.NextUpBehavior import org.jellyfin.androidtv.preference.constant.NextUpBehavior
import org.jellyfin.androidtv.preference.constant.RefreshRateSwitchingBehavior import org.jellyfin.androidtv.preference.constant.RefreshRateSwitchingBehavior
@@ -40,9 +41,9 @@ class UserPreferences(context: Context) : SharedPreferenceStore(
var appTheme = enumPreference("app_theme", AppTheme.DARK) var appTheme = enumPreference("app_theme", AppTheme.DARK)
/** /**
* Enable background images while browsing * Behavior of app background while browsing
*/ */
var backdropEnabled = booleanPreference("pref_show_backdrop", true) var backdropBehavior = enumPreference("backdrop_behavior", BackdropBehavior.BACKDROP_WITH_BLUR)
/* Playback - General*/ /* Playback - General*/
/** /**
@@ -232,7 +233,7 @@ class UserPreferences(context: Context) : SharedPreferenceStore(
var assDirectPlay = booleanPreference("libass_enabled", false) var assDirectPlay = booleanPreference("libass_enabled", false)
/** /**
* Enable PGS subtitle direct-play. * Enable PGS subtitle direct-play.
*/ */
var pgsDirectPlay = booleanPreference("pgs_enabled", true) var pgsDirectPlay = booleanPreference("pgs_enabled", true)
} }
@@ -263,6 +264,13 @@ class UserPreferences(context: Context) : SharedPreferenceStore(
migration(toVersion = 9) { migration(toVersion = 9) {
// Reset subtitle text size as we changed from fractional sizing to absolute sizing // Reset subtitle text size as we changed from fractional sizing to absolute sizing
remove("subtitles_text_size") remove("subtitles_text_size")
// Set the BackdropBehavior if it was enabled in a previous version
val backdropEnabled = it.getBoolean("pref_show_backdrop", true)
putString(
"backdrop_behavior",
if (backdropEnabled) BackdropBehavior.BACKDROP_WITH_BLUR.name else BackdropBehavior.DISABLED.name
)
} }
} }
} }

View File

@@ -0,0 +1,20 @@
package org.jellyfin.androidtv.preference.constant
import org.jellyfin.androidtv.R
import org.jellyfin.preference.PreferenceEnum
enum class BackdropBehavior(
override val nameRes: Int,
) : PreferenceEnum {
DISABLED(R.string.state_disabled),
/**
* When selected, backdrop is shown with a blur effect.
*/
BACKDROP_WITH_BLUR(R.string.backdrop_behavior_with_blur),
/**
* When selected, backdrop is shown without any blur effect.
*/
BACKDROP_WITHOUT_BLUR(R.string.backdrop_behavior_without_blur),
}

View File

@@ -10,6 +10,7 @@ import org.jellyfin.androidtv.ui.settings.screen.authentication.SettingsAuthenti
import org.jellyfin.androidtv.ui.settings.screen.authentication.SettingsAuthenticationServerScreen import org.jellyfin.androidtv.ui.settings.screen.authentication.SettingsAuthenticationServerScreen
import org.jellyfin.androidtv.ui.settings.screen.authentication.SettingsAuthenticationServerUserScreen import org.jellyfin.androidtv.ui.settings.screen.authentication.SettingsAuthenticationServerUserScreen
import org.jellyfin.androidtv.ui.settings.screen.authentication.SettingsAuthenticationSortByScreen import org.jellyfin.androidtv.ui.settings.screen.authentication.SettingsAuthenticationSortByScreen
import org.jellyfin.androidtv.ui.settings.screen.customization.SettingsCustomizationBackdropScreen
import org.jellyfin.androidtv.ui.settings.screen.customization.SettingsCustomizationClockScreen import org.jellyfin.androidtv.ui.settings.screen.customization.SettingsCustomizationClockScreen
import org.jellyfin.androidtv.ui.settings.screen.customization.SettingsCustomizationScreen import org.jellyfin.androidtv.ui.settings.screen.customization.SettingsCustomizationScreen
import org.jellyfin.androidtv.ui.settings.screen.customization.SettingsCustomizationThemeScreen import org.jellyfin.androidtv.ui.settings.screen.customization.SettingsCustomizationThemeScreen
@@ -62,6 +63,7 @@ object Routes {
const val CUSTOMIZATION_THEME = "/customization/theme" const val CUSTOMIZATION_THEME = "/customization/theme"
const val CUSTOMIZATION_CLOCK = "/customization/clock" const val CUSTOMIZATION_CLOCK = "/customization/clock"
const val CUSTOMIZATION_WATCHED_INDICATOR = "/customization/watch-indicators" const val CUSTOMIZATION_WATCHED_INDICATOR = "/customization/watch-indicators"
const val CUSTOMIZATION_BACKDROP = "/customization/backdrop"
const val CUSTOMIZATION_SCREENSAVER = "/customization/screensaver" const val CUSTOMIZATION_SCREENSAVER = "/customization/screensaver"
const val CUSTOMIZATION_SCREENSAVER_TIMEOUT = "/customization/screensaver/timeout" const val CUSTOMIZATION_SCREENSAVER_TIMEOUT = "/customization/screensaver/timeout"
const val CUSTOMIZATION_SCREENSAVER_AGE_RATING = "/customization/screensaver/age-rating" const val CUSTOMIZATION_SCREENSAVER_AGE_RATING = "/customization/screensaver/age-rating"
@@ -139,6 +141,9 @@ val routes = mapOf<String, RouteComposable>(
Routes.CUSTOMIZATION_WATCHED_INDICATOR to { Routes.CUSTOMIZATION_WATCHED_INDICATOR to {
SettingsCustomizationWatchedIndicatorScreen() SettingsCustomizationWatchedIndicatorScreen()
}, },
Routes.CUSTOMIZATION_BACKDROP to {
SettingsCustomizationBackdropScreen()
},
Routes.CUSTOMIZATION_SCREENSAVER to { Routes.CUSTOMIZATION_SCREENSAVER to {
SettingsScreensaverScreen() SettingsScreensaverScreen()
}, },

View File

@@ -0,0 +1,45 @@
package org.jellyfin.androidtv.ui.settings.screen.customization
import androidx.compose.foundation.lazy.items
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
import androidx.compose.ui.res.stringResource
import org.jellyfin.androidtv.R
import org.jellyfin.androidtv.preference.UserPreferences
import org.jellyfin.androidtv.preference.constant.BackdropBehavior
import org.jellyfin.androidtv.ui.base.Text
import org.jellyfin.androidtv.ui.base.form.RadioButton
import org.jellyfin.androidtv.ui.base.list.ListButton
import org.jellyfin.androidtv.ui.base.list.ListSection
import org.jellyfin.androidtv.ui.navigation.LocalRouter
import org.jellyfin.androidtv.ui.settings.compat.rememberPreference
import org.jellyfin.androidtv.ui.settings.composable.SettingsColumn
import org.koin.compose.koinInject
@Composable
fun SettingsCustomizationBackdropScreen() {
val router = LocalRouter.current
val userPreferences = koinInject<UserPreferences>()
var backdropBehavior by rememberPreference(userPreferences, UserPreferences.backdropBehavior)
SettingsColumn {
item {
ListSection(
overlineContent = { Text(stringResource(R.string.pref_customization).uppercase()) },
headingContent = { Text(stringResource(R.string.lbl_show_backdrop)) },
)
}
items(BackdropBehavior.entries) { entry ->
ListButton(
headingContent = { Text(stringResource(entry.nameRes)) },
trailingContent = { RadioButton(checked = backdropBehavior == entry) },
onClick = {
backdropBehavior = entry
router.back()
}
)
}
}
}

View File

@@ -62,13 +62,12 @@ fun SettingsCustomizationScreen() {
} }
item { item {
var backdropEnabled by rememberPreference(userPreferences, UserPreferences.backdropEnabled) var backdropBehavior by rememberPreference(userPreferences, UserPreferences.backdropBehavior)
ListButton( ListButton(
headingContent = { Text(stringResource(R.string.lbl_show_backdrop)) }, headingContent = { Text(stringResource(R.string.lbl_show_backdrop)) },
trailingContent = { Checkbox(checked = backdropEnabled) }, captionContent = { Text(stringResource(backdropBehavior.nameRes)) },
captionContent = { Text(stringResource(R.string.pref_show_backdrop_description)) }, onClick = { router.push(Routes.CUSTOMIZATION_BACKDROP) }
onClick = { backdropEnabled = !backdropEnabled }
) )
} }

View File

@@ -195,7 +195,6 @@
<string name="lbl_play_next_up">Play next up</string> <string name="lbl_play_next_up">Play next up</string>
<string name="lbl_open">Open</string> <string name="lbl_open">Open</string>
<string name="lbl_show_backdrop">Show backdrops</string> <string name="lbl_show_backdrop">Show backdrops</string>
<string name="pref_show_backdrop_description">Change background image to selected items</string>
<string name="lbl_show_premieres">Show season premieres</string> <string name="lbl_show_premieres">Show season premieres</string>
<string name="desc_premieres">Show a row of new episode pilots for any series you watch</string> <string name="desc_premieres">Show a row of new episode pilots for any series you watch</string>
<string name="lbl_in_x_days">In %d days</string> <string name="lbl_in_x_days">In %d days</string>
@@ -578,6 +577,8 @@
<string name="video_player_external">External app</string> <string name="video_player_external">External app</string>
<string name="playback_video_player_external_empty">You don\'t have any video player apps installed on your device that can open Jellyfin videos.</string> <string name="playback_video_player_external_empty">You don\'t have any video player apps installed on your device that can open Jellyfin videos.</string>
<string name="filters">Filters</string> <string name="filters">Filters</string>
<string name="backdrop_behavior_with_blur">Backdrop with blur</string>
<string name="backdrop_behavior_without_blur">Backdrop without blur</string>
<plurals name="seconds"> <plurals name="seconds">
<item quantity="one">%1$s second</item> <item quantity="one">%1$s second</item>
<item quantity="other">%1$s seconds</item> <item quantity="other">%1$s seconds</item>