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 org.jellyfin.androidtv.auth.model.Server
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.itemBackdropImages
import org.jellyfin.androidtv.util.apiclient.parentBackdropImages
@@ -59,12 +60,14 @@ class BackgroundService(
* Use all available backdrops from [baseItem] as background.
*/
fun setBackground(baseItem: BaseItemDto?) {
val backdropBehavior = userPreferences[UserPreferences.backdropBehavior]
// Check if item is set and backgrounds are enabled
if (baseItem == null || !userPreferences[UserPreferences.backdropEnabled])
if (baseItem == null || backdropBehavior == BackdropBehavior.DISABLED)
return clearBackgrounds()
// Enable blur for backdrops
_blurBackground.value = true
_blurBackground.value = backdropBehavior == BackdropBehavior.BACKDROP_WITH_BLUR
// Get all backdrop urls
val backdropUrls = (baseItem.itemBackdropImages + baseItem.parentBackdropImages)
@@ -79,7 +82,7 @@ class BackgroundService(
*/
fun setBackground(server: Server) {
// Check if item is set and backgrounds are enabled
if (!userPreferences[UserPreferences.backdropEnabled])
if (userPreferences[UserPreferences.backdropBehavior] == BackdropBehavior.DISABLED)
return clearBackgrounds()
// 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.constant.AppTheme
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.NextUpBehavior
import org.jellyfin.androidtv.preference.constant.RefreshRateSwitchingBehavior
@@ -40,9 +41,9 @@ class UserPreferences(context: Context) : SharedPreferenceStore(
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*/
/**
@@ -232,7 +233,7 @@ class UserPreferences(context: Context) : SharedPreferenceStore(
var assDirectPlay = booleanPreference("libass_enabled", false)
/**
* Enable PGS subtitle direct-play.
* Enable PGS subtitle direct-play.
*/
var pgsDirectPlay = booleanPreference("pgs_enabled", true)
}
@@ -263,6 +264,13 @@ class UserPreferences(context: Context) : SharedPreferenceStore(
migration(toVersion = 9) {
// Reset subtitle text size as we changed from fractional sizing to absolute sizing
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.SettingsAuthenticationServerUserScreen
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.SettingsCustomizationScreen
import org.jellyfin.androidtv.ui.settings.screen.customization.SettingsCustomizationThemeScreen
@@ -62,6 +63,7 @@ object Routes {
const val CUSTOMIZATION_THEME = "/customization/theme"
const val CUSTOMIZATION_CLOCK = "/customization/clock"
const val CUSTOMIZATION_WATCHED_INDICATOR = "/customization/watch-indicators"
const val CUSTOMIZATION_BACKDROP = "/customization/backdrop"
const val CUSTOMIZATION_SCREENSAVER = "/customization/screensaver"
const val CUSTOMIZATION_SCREENSAVER_TIMEOUT = "/customization/screensaver/timeout"
const val CUSTOMIZATION_SCREENSAVER_AGE_RATING = "/customization/screensaver/age-rating"
@@ -139,6 +141,9 @@ val routes = mapOf<String, RouteComposable>(
Routes.CUSTOMIZATION_WATCHED_INDICATOR to {
SettingsCustomizationWatchedIndicatorScreen()
},
Routes.CUSTOMIZATION_BACKDROP to {
SettingsCustomizationBackdropScreen()
},
Routes.CUSTOMIZATION_SCREENSAVER to {
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 {
var backdropEnabled by rememberPreference(userPreferences, UserPreferences.backdropEnabled)
var backdropBehavior by rememberPreference(userPreferences, UserPreferences.backdropBehavior)
ListButton(
headingContent = { Text(stringResource(R.string.lbl_show_backdrop)) },
trailingContent = { Checkbox(checked = backdropEnabled) },
captionContent = { Text(stringResource(R.string.pref_show_backdrop_description)) },
onClick = { backdropEnabled = !backdropEnabled }
captionContent = { Text(stringResource(backdropBehavior.nameRes)) },
onClick = { router.push(Routes.CUSTOMIZATION_BACKDROP) }
)
}

View File

@@ -195,7 +195,6 @@
<string name="lbl_play_next_up">Play next up</string>
<string name="lbl_open">Open</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="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>
@@ -578,6 +577,8 @@
<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="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">
<item quantity="one">%1$s second</item>
<item quantity="other">%1$s seconds</item>