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
Expand Up @@ -59,6 +59,7 @@ class CloudSyncRepository @Inject constructor(
val autoPlaySingleSource: Boolean = true,
val autoPlayMinQuality: String = "Any",
val trailerAutoPlay: Boolean = false,
val showBudget: Boolean = true,
val includeSpecials: Boolean = false,
val iptvHiddenGroups: String = "",
val iptvGroupOrder: String = ""
Expand All @@ -70,6 +71,8 @@ class CloudSyncRepository @Inject constructor(
profileManager.profileStringKeyFor(profileId, "content_language")
private fun trailerAutoPlayKeyFor(profileId: String) =
profileManager.profileBooleanKeyFor(profileId, "trailer_auto_play")
private fun showBudgetKeyFor(profileId: String) =
profileManager.profileBooleanKeyFor(profileId, "show_budget_on_home")

private fun subtitleSizeKeyFor(profileId: String) =
profileManager.profileStringKeyFor(profileId, "subtitle_size")
Expand Down Expand Up @@ -151,6 +154,7 @@ class CloudSyncRepository @Inject constructor(
contentLanguage = prefs[contentLanguageKeyFor(profile.id)] ?: "en-US",

trailerAutoPlay = prefs[trailerAutoPlayKeyFor(profile.id)] ?: false,
showBudget = prefs[showBudgetKeyFor(profile.id)] ?: true,
subtitleSize = prefs[subtitleSizeKeyFor(profile.id)] ?: "Medium",
subtitleColor = prefs[subtitleColorKeyFor(profile.id)] ?: "White",
iptvHiddenGroups = prefs[iptvHiddenGroupsKeyFor(profile.id)] ?: "",
Expand Down Expand Up @@ -370,6 +374,7 @@ class CloudSyncRepository @Inject constructor(
prefs[contentLanguageKeyFor(profileId)] = state.contentLanguage

prefs[trailerAutoPlayKeyFor(profileId)] = state.trailerAutoPlay
prefs[showBudgetKeyFor(profileId)] = state.showBudget
prefs[subtitleSizeKeyFor(profileId)] = state.subtitleSize
prefs[subtitleColorKeyFor(profileId)] = state.subtitleColor
if (state.iptvHiddenGroups.isNotBlank()) prefs[iptvHiddenGroupsKeyFor(profileId)] = state.iptvHiddenGroups
Expand Down
13 changes: 12 additions & 1 deletion app/src/main/kotlin/com/arflix/tv/ui/screens/home/HomeScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,7 @@ fun HomeScreen(
heroOverviewOverride = displayHeroOverview,
contentStartPadding = contentStartPadding,
isMobile = isMobile,
showBudget = uiState.showBudget,
onNavigateToDetails = onNavigateToDetails,
onNavigateToTv = { channelId, streamUrl -> onNavigateToTv(channelId, streamUrl) },
isIptvItem = { item -> viewModel.isIptvItem(item) },
Expand Down Expand Up @@ -733,6 +734,11 @@ private fun HeroSection(
item: MediaItem,
logoUrl: String?,
overviewOverride: String? = null,
// Hide the Budget line on the hero metadata row when false. Plumbed from
// HomeUiState.showBudget, which is loaded from the per-profile
// `show_budget_on_home` DataStore key and defaults to true so existing
// users see no behavior change. Issue #72.
showBudget: Boolean = true,
modifier: Modifier = Modifier
) {
val context = LocalContext.current
Expand Down Expand Up @@ -994,7 +1000,10 @@ private fun HeroSection(
}
}

if (!budgetText.isNullOrBlank()) {
// Budget line can be hidden via Settings -> General -> Show Budget on Home.
// Default is shown (showBudget = true) to preserve existing behavior.
// Issue #72.
if (showBudget && !budgetText.isNullOrBlank()) {
if (displayDate.isNotEmpty() || hasGenre || hasDuration || ratingValue > 0f) {
Text(
text = "|",
Expand Down Expand Up @@ -1070,6 +1079,7 @@ private fun HomeHeroLayer(
heroOverviewOverride: String?,
contentStartPadding: androidx.compose.ui.unit.Dp,
isMobile: Boolean = false,
showBudget: Boolean = true,
onNavigateToDetails: (MediaType, Int, Int?, Int?) -> Unit = { _, _, _, _ -> },
onNavigateToTv: (channelId: String?, streamUrl: String?) -> Unit = { _, _ -> },
isIptvItem: (MediaItem) -> Boolean = { false },
Expand Down Expand Up @@ -1098,6 +1108,7 @@ private fun HomeHeroLayer(
item = item,
logoUrl = heroLogoUrl,
overviewOverride = heroOverviewOverride,
showBudget = showBudget,
modifier = Modifier
.align(Alignment.BottomStart)
.padding(start = contentStartPadding, end = 400.dp)
Expand Down
16 changes: 14 additions & 2 deletions app/src/main/kotlin/com/arflix/tv/ui/screens/home/HomeViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ data class HomeUiState(
val heroLogoUrl: String? = null,
val heroTrailerKey: String? = null,
val trailerAutoPlay: Boolean = false,
// Home hero metadata visibility toggles (issue #72)
val showBudget: Boolean = true,
val heroOverviewOverride: String? = null,
val cardLogoUrls: Map<String, String> = emptyMap(),
// Previous hero for crossfade (Phase 2.1)
Expand Down Expand Up @@ -640,15 +642,25 @@ class HomeViewModel @Inject constructor(
}

init {
// Load trailer auto-play setting
// Load trailer auto-play and show-budget settings
viewModelScope.launch {
try {
val prefs = context.settingsDataStore.data.first()
// Search for any profile key that matches trailer_auto_play
val trailerEnabled = prefs.asMap().any { (key, value) ->
key.name.endsWith("_trailer_auto_play") && value == true
}
_uiState.value = _uiState.value.copy(trailerAutoPlay = trailerEnabled)
// show_budget_on_home defaults to TRUE so existing users see no change
// until they explicitly disable it. We check any active-profile key; if
// none exist yet the default of true is preserved. Issue #72.
val showBudgetExplicit = prefs.asMap().entries
.firstOrNull { (key, _) -> key.name.endsWith("_show_budget_on_home") }
?.value as? Boolean
val showBudget = showBudgetExplicit ?: true
_uiState.value = _uiState.value.copy(
trailerAutoPlay = trailerEnabled,
showBudget = showBudget
)
} catch (_: Exception) {}
}
// Restore logo URL cache from disk for instant clearlogos on cold start
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ fun SettingsScreen(
if (scrollState.maxValue <= 0) return@LaunchedEffect

val maxIndex = when (sectionIndex) {
0 -> 13 // General: 14 items
0 -> 14 // General: 15 items (added Show Budget toggle for #72)
1 -> 3 // IPTV: Configure + Refresh + Delete + Stalker
2 -> uiState.catalogs.size // Catalogs
3 -> uiState.addons.size // Addons
Expand Down Expand Up @@ -433,7 +433,7 @@ fun SettingsScreen(
Zone.CONTENT -> {
// Dynamic max based on current section
val maxIndex = when (sectionIndex) {
0 -> 13 // General: 14 items
0 -> 14 // General: 15 items (added Show Budget toggle for #72)
1 -> 3 // IPTV: Configure + Refresh + Delete + Stalker
2 -> uiState.catalogs.size // Catalogs: Add + N catalogs
3 -> uiState.addons.size // Addons: N addons + "Add Custom" button
Expand Down Expand Up @@ -486,7 +486,8 @@ fun SettingsScreen(
10 -> viewModel.toggleCardLayoutMode()
11 -> { val next = when (uiState.deviceModeOverride) { "auto" -> "tv"; "tv" -> "tablet"; "tablet" -> "phone"; else -> "auto" }; viewModel.setDeviceModeOverride(next) }
12 -> viewModel.setSkipProfileSelection(!uiState.skipProfileSelection)
13 -> openDnsProviderPicker()
13 -> viewModel.setShowBudget(!uiState.showBudget)
14 -> openDnsProviderPicker()
}
}
1 -> { // IPTV
Expand Down Expand Up @@ -615,6 +616,7 @@ fun SettingsScreen(
subtitleColor = uiState.subtitleColor,
deviceModeOverride = uiState.deviceModeOverride,
skipProfileSelection = uiState.skipProfileSelection,
showBudget = uiState.showBudget,
focusedIndex = -1,
onSubtitleClick = openSubtitlePicker,
onAudioLanguageClick = openAudioLanguagePicker,
Expand All @@ -633,6 +635,7 @@ fun SettingsScreen(
onContentLanguageClick = openContentLanguagePicker,
onSubtitleSizeClick = { viewModel.cycleSubtitleSize() },
onSkipProfileSelectionToggle = { viewModel.setSkipProfileSelection(it) },
onShowBudgetToggle = { viewModel.setShowBudget(it) },
onSubtitleColorClick = { viewModel.cycleSubtitleColor() }
)
"iptv" -> IptvSettings(
Expand Down Expand Up @@ -787,6 +790,7 @@ fun SettingsScreen(
subtitleColor = uiState.subtitleColor,
deviceModeOverride = uiState.deviceModeOverride,
skipProfileSelection = uiState.skipProfileSelection,
showBudget = uiState.showBudget,
focusedIndex = if (activeZone == Zone.CONTENT) contentFocusIndex else -1,
onSubtitleClick = openSubtitlePicker,
onAudioLanguageClick = openAudioLanguagePicker,
Expand All @@ -804,6 +808,7 @@ fun SettingsScreen(
},
onContentLanguageClick = openContentLanguagePicker,
onSkipProfileSelectionToggle = { viewModel.setSkipProfileSelection(it) },
onShowBudgetToggle = { viewModel.setShowBudget(it) },
onSubtitleSizeClick = { viewModel.cycleSubtitleSize() },
onSubtitleColorClick = { viewModel.cycleSubtitleColor() }
)
Expand Down Expand Up @@ -2115,6 +2120,7 @@ private fun GeneralSettings(
subtitleColor: String = "White",
deviceModeOverride: String = "auto",
skipProfileSelection: Boolean = false,
showBudget: Boolean = true,
focusedIndex: Int,
onSubtitleClick: () -> Unit,
onAudioLanguageClick: () -> Unit,
Expand All @@ -2127,6 +2133,7 @@ private fun GeneralSettings(
onDeviceModeClick: () -> Unit = {},
onContentLanguageClick: () -> Unit = {},
onSkipProfileSelectionToggle: (Boolean) -> Unit = {},
onShowBudgetToggle: (Boolean) -> Unit = {},
trailerAutoPlay: Boolean = false,
onSubtitleSizeClick: () -> Unit = {},
onSubtitleColorClick: () -> Unit = {},
Expand Down Expand Up @@ -2276,6 +2283,16 @@ private fun GeneralSettings(
isFocused = focusedIndex == 12,
onToggle = onSkipProfileSelectionToggle
)
Spacer(modifier = Modifier.height(10.dp))
// Home hero controls — issue #72. The movie Budget line on the hero banner
// makes the metadata row noisy on small screens and some users want to hide it.
SettingsToggleRow(
title = "Show Budget on Home",
subtitle = "Display the movie budget on the home hero banner",
isEnabled = showBudget,
isFocused = focusedIndex == 13,
onToggle = onShowBudgetToggle
)

// ── Network ──
Spacer(modifier = Modifier.height(24.dp))
Expand All @@ -2291,7 +2308,7 @@ private fun GeneralSettings(
title = "DNS Provider",
subtitle = "Resolve API and stream requests",
value = dnsProvider,
isFocused = focusedIndex == 13,
isFocused = focusedIndex == 14,
onClick = onDnsProviderClick
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ data class SettingsUiState(
val subtitleSize: String = "Medium",
val subtitleColor: String = "White",
val trailerAutoPlay: Boolean = false,
val showBudget: Boolean = true,
val includeSpecials: Boolean = false,
val isLoggedIn: Boolean = false,
val accountEmail: String? = null,
Expand Down Expand Up @@ -178,6 +179,7 @@ class SettingsViewModel @Inject constructor(
private fun autoPlayMinQualityKey() = profileManager.profileStringKey("auto_play_min_quality")
private fun autoPlayMinQualityKeyFor(profileId: String) = profileManager.profileStringKeyFor(profileId, "auto_play_min_quality")
private fun trailerAutoPlayKey() = profileManager.profileBooleanKey("trailer_auto_play")
private fun showBudgetKey() = profileManager.profileBooleanKey("show_budget_on_home")

private fun subtitleSizeKey() = profileManager.profileStringKey("subtitle_size")
private fun subtitleColorKey() = profileManager.profileStringKey("subtitle_color")
Expand Down Expand Up @@ -265,6 +267,7 @@ class SettingsViewModel @Inject constructor(
}
val autoPlayMinQuality = normalizeAutoPlayMinQuality(prefs[autoPlayMinQualityKey()])
val trailerAutoPlay = prefs[trailerAutoPlayKey()] ?: false
val showBudget = prefs[showBudgetKey()] ?: true

val subtitleSize = prefs[subtitleSizeKey()] ?: "Medium"
val subtitleColor = prefs[subtitleColorKey()] ?: "White"
Expand Down Expand Up @@ -304,6 +307,7 @@ class SettingsViewModel @Inject constructor(
autoPlaySingleSource = autoPlaySingleSource,
autoPlayMinQuality = autoPlayMinQuality,
trailerAutoPlay = trailerAutoPlay,
showBudget = showBudget,

subtitleSize = subtitleSize,
subtitleColor = subtitleColor,
Expand Down Expand Up @@ -784,6 +788,14 @@ class SettingsViewModel @Inject constructor(
viewModelScope.launch { context.settingsDataStore.edit { it[trailerAutoPlayKey()] = enabled }; _uiState.value = _uiState.value.copy(trailerAutoPlay = enabled); syncLocalStateToCloud(silent = true) }
}

fun setShowBudget(enabled: Boolean) {
viewModelScope.launch {
context.settingsDataStore.edit { it[showBudgetKey()] = enabled }
_uiState.value = _uiState.value.copy(showBudget = enabled)
syncLocalStateToCloud(silent = true)
}
}

fun cycleSubtitleSize() {
val next = when (_uiState.value.subtitleSize) { "Small" -> "Medium"; "Medium" -> "Large"; "Large" -> "Extra Large"; else -> "Small" }
viewModelScope.launch { context.settingsDataStore.edit { it[subtitleSizeKey()] = next }; _uiState.value = _uiState.value.copy(subtitleSize = next); syncLocalStateToCloud(silent = true) }
Expand Down
Loading