mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 23:51:21 +02:00
Better tab UI & remember tab setting
This commit is contained in:
parent
c642636da7
commit
88038bfcfd
7 changed files with 139 additions and 15 deletions
|
|
@ -254,6 +254,18 @@ sealed interface AppPreference<T> {
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val RememberSelectedTab =
|
||||||
|
AppSwitchPreference(
|
||||||
|
title = R.string.remember_selected_tab,
|
||||||
|
defaultValue = false,
|
||||||
|
getter = { it.interfacePreferences.rememberSelectedTab },
|
||||||
|
setter = { prefs, value ->
|
||||||
|
prefs.updateInterfacePreferences { rememberSelectedTab = value }
|
||||||
|
},
|
||||||
|
summaryOn = R.string.enabled,
|
||||||
|
summaryOff = R.string.disabled,
|
||||||
|
)
|
||||||
|
|
||||||
val InstalledVersion =
|
val InstalledVersion =
|
||||||
AppClickablePreference(
|
AppClickablePreference(
|
||||||
title = R.string.installed_version,
|
title = R.string.installed_version,
|
||||||
|
|
@ -301,6 +313,7 @@ val basicPreferences =
|
||||||
AppPreference.HomePageItems,
|
AppPreference.HomePageItems,
|
||||||
AppPreference.RewatchNextUp,
|
AppPreference.RewatchNextUp,
|
||||||
AppPreference.PlayThemeMusic,
|
AppPreference.PlayThemeMusic,
|
||||||
|
AppPreference.RememberSelectedTab,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
PreferenceGroup(
|
PreferenceGroup(
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,10 @@ package com.github.damontecres.dolphin.preferences
|
||||||
import androidx.datastore.core.CorruptionException
|
import androidx.datastore.core.CorruptionException
|
||||||
import androidx.datastore.core.Serializer
|
import androidx.datastore.core.Serializer
|
||||||
import com.google.protobuf.InvalidProtocolBufferException
|
import com.google.protobuf.InvalidProtocolBufferException
|
||||||
|
import timber.log.Timber
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
|
import java.util.UUID
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlin.time.Duration.Companion.seconds
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
|
|
@ -77,3 +79,13 @@ inline fun AppPreferences.updateInterfacePreferences(block: InterfacePreferences
|
||||||
update {
|
update {
|
||||||
interfacePreferences = interfacePreferences.toBuilder().apply(block).build()
|
interfacePreferences = interfacePreferences.toBuilder().apply(block).build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun AppPreferences.rememberTab(
|
||||||
|
itemId: UUID,
|
||||||
|
index: Int,
|
||||||
|
): AppPreferences {
|
||||||
|
Timber.v("Updating $itemId tab to $index")
|
||||||
|
return updateInterfacePreferences {
|
||||||
|
putRememberedTabs(itemId.toString(), index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,17 +14,23 @@ import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.focus.FocusRequester
|
import androidx.compose.ui.focus.FocusRequester
|
||||||
import androidx.compose.ui.focus.focusRequester
|
import androidx.compose.ui.focus.focusRequester
|
||||||
import androidx.compose.ui.focus.focusRestorer
|
import androidx.compose.ui.focus.focusRestorer
|
||||||
|
import androidx.compose.ui.focus.onFocusChanged
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import androidx.tv.material3.MaterialTheme
|
import androidx.tv.material3.MaterialTheme
|
||||||
import androidx.tv.material3.Tab
|
import androidx.tv.material3.Tab
|
||||||
|
import androidx.tv.material3.TabDefaults
|
||||||
import androidx.tv.material3.TabRow
|
import androidx.tv.material3.TabRow
|
||||||
|
import androidx.tv.material3.TabRowDefaults
|
||||||
import androidx.tv.material3.Text
|
import androidx.tv.material3.Text
|
||||||
import com.github.damontecres.dolphin.preferences.UserPreferences
|
import com.github.damontecres.dolphin.preferences.UserPreferences
|
||||||
|
import com.github.damontecres.dolphin.preferences.rememberTab
|
||||||
import com.github.damontecres.dolphin.ui.components.ErrorMessage
|
import com.github.damontecres.dolphin.ui.components.ErrorMessage
|
||||||
import com.github.damontecres.dolphin.ui.components.RecommendedMovie
|
import com.github.damontecres.dolphin.ui.components.RecommendedMovie
|
||||||
import com.github.damontecres.dolphin.ui.ifElse
|
import com.github.damontecres.dolphin.ui.ifElse
|
||||||
import com.github.damontecres.dolphin.ui.nav.Destination
|
import com.github.damontecres.dolphin.ui.nav.Destination
|
||||||
import com.github.damontecres.dolphin.ui.nav.NavigationManager
|
import com.github.damontecres.dolphin.ui.nav.NavigationManager
|
||||||
|
import com.github.damontecres.dolphin.ui.preferences.PreferencesViewModel
|
||||||
import com.github.damontecres.dolphin.ui.tryRequestFocus
|
import com.github.damontecres.dolphin.ui.tryRequestFocus
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
@ -33,29 +39,71 @@ fun CollectionFolderMovie(
|
||||||
navigationManager: NavigationManager,
|
navigationManager: NavigationManager,
|
||||||
destination: Destination.MediaItem,
|
destination: Destination.MediaItem,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
preferencesViewModel: PreferencesViewModel = hiltViewModel(),
|
||||||
) {
|
) {
|
||||||
|
val uiPrefs = preferences.appPreferences.interfacePreferences
|
||||||
|
val rememberedTabIndex =
|
||||||
|
if (uiPrefs.rememberSelectedTab) {
|
||||||
|
uiPrefs.getRememberedTabsOrDefault(destination.itemId.toString(), 0)
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
val tabs = listOf("Recommended", "Library")
|
val tabs = listOf("Recommended", "Library")
|
||||||
var selectedTabIndex by rememberSaveable { mutableIntStateOf(0) }
|
var focusTabIndex by rememberSaveable { mutableIntStateOf(rememberedTabIndex) }
|
||||||
|
var selectedTabIndex by rememberSaveable { mutableIntStateOf(rememberedTabIndex) }
|
||||||
val focusRequester = remember { FocusRequester() }
|
val focusRequester = remember { FocusRequester() }
|
||||||
|
|
||||||
val firstTabFocusRequester = remember { FocusRequester() }
|
val firstTabFocusRequester = remember { FocusRequester() }
|
||||||
LaunchedEffect(Unit) { firstTabFocusRequester.tryRequestFocus() }
|
LaunchedEffect(Unit) { firstTabFocusRequester.tryRequestFocus() }
|
||||||
|
|
||||||
|
if (uiPrefs.rememberSelectedTab) {
|
||||||
|
LaunchedEffect(selectedTabIndex) {
|
||||||
|
preferencesViewModel.preferenceDataStore.updateData {
|
||||||
|
preferences.appPreferences.rememberTab(destination.itemId, selectedTabIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
LaunchedEffect(Unit) { focusRequester.tryRequestFocus() }
|
LaunchedEffect(Unit) { focusRequester.tryRequestFocus() }
|
||||||
Column(
|
Column(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
) {
|
) {
|
||||||
TabRow(
|
TabRow(
|
||||||
selectedTabIndex = selectedTabIndex,
|
selectedTabIndex = focusTabIndex,
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
.padding(start = 32.dp, top = 16.dp)
|
.padding(start = 32.dp, top = 16.dp, bottom = 16.dp)
|
||||||
.focusRestorer(firstTabFocusRequester),
|
.focusRestorer(firstTabFocusRequester)
|
||||||
|
.onFocusChanged {
|
||||||
|
if (!it.isFocused) {
|
||||||
|
focusTabIndex = selectedTabIndex
|
||||||
|
}
|
||||||
|
},
|
||||||
|
indicator =
|
||||||
|
@Composable { tabPositions, doesTabRowHaveFocus ->
|
||||||
|
tabPositions.getOrNull(focusTabIndex)?.let { currentTabPosition ->
|
||||||
|
// TabRowDefaults.PillIndicator(
|
||||||
|
// currentTabPosition = currentTabPosition,
|
||||||
|
// doesTabRowHaveFocus = doesTabRowHaveFocus,
|
||||||
|
// )
|
||||||
|
TabRowDefaults.UnderlinedIndicator(
|
||||||
|
currentTabPosition = currentTabPosition,
|
||||||
|
doesTabRowHaveFocus = doesTabRowHaveFocus,
|
||||||
|
activeColor = MaterialTheme.colorScheme.border,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
tabs = {
|
tabs = {
|
||||||
tabs.forEachIndexed { index, title ->
|
tabs.forEachIndexed { index, title ->
|
||||||
Tab(
|
Tab(
|
||||||
selected = selectedTabIndex == index,
|
selected = focusTabIndex == index,
|
||||||
onClick = { selectedTabIndex = index },
|
onClick = { selectedTabIndex = index },
|
||||||
onFocus = {},
|
onFocus = { focusTabIndex = index },
|
||||||
|
colors =
|
||||||
|
TabDefaults.pillIndicatorTabColors(
|
||||||
|
focusedContentColor = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
),
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = title,
|
text = title,
|
||||||
|
|
@ -64,7 +112,7 @@ fun CollectionFolderMovie(
|
||||||
Modifier
|
Modifier
|
||||||
.padding(8.dp)
|
.padding(8.dp)
|
||||||
.ifElse(
|
.ifElse(
|
||||||
index == 0,
|
index == selectedTabIndex,
|
||||||
Modifier.focusRequester(firstTabFocusRequester),
|
Modifier.focusRequester(firstTabFocusRequester),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -14,17 +14,23 @@ import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.focus.FocusRequester
|
import androidx.compose.ui.focus.FocusRequester
|
||||||
import androidx.compose.ui.focus.focusRequester
|
import androidx.compose.ui.focus.focusRequester
|
||||||
import androidx.compose.ui.focus.focusRestorer
|
import androidx.compose.ui.focus.focusRestorer
|
||||||
|
import androidx.compose.ui.focus.onFocusChanged
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import androidx.tv.material3.MaterialTheme
|
import androidx.tv.material3.MaterialTheme
|
||||||
import androidx.tv.material3.Tab
|
import androidx.tv.material3.Tab
|
||||||
|
import androidx.tv.material3.TabDefaults
|
||||||
import androidx.tv.material3.TabRow
|
import androidx.tv.material3.TabRow
|
||||||
|
import androidx.tv.material3.TabRowDefaults
|
||||||
import androidx.tv.material3.Text
|
import androidx.tv.material3.Text
|
||||||
import com.github.damontecres.dolphin.preferences.UserPreferences
|
import com.github.damontecres.dolphin.preferences.UserPreferences
|
||||||
|
import com.github.damontecres.dolphin.preferences.rememberTab
|
||||||
import com.github.damontecres.dolphin.ui.components.ErrorMessage
|
import com.github.damontecres.dolphin.ui.components.ErrorMessage
|
||||||
import com.github.damontecres.dolphin.ui.components.RecommendedTvShow
|
import com.github.damontecres.dolphin.ui.components.RecommendedTvShow
|
||||||
import com.github.damontecres.dolphin.ui.ifElse
|
import com.github.damontecres.dolphin.ui.ifElse
|
||||||
import com.github.damontecres.dolphin.ui.nav.Destination
|
import com.github.damontecres.dolphin.ui.nav.Destination
|
||||||
import com.github.damontecres.dolphin.ui.nav.NavigationManager
|
import com.github.damontecres.dolphin.ui.nav.NavigationManager
|
||||||
|
import com.github.damontecres.dolphin.ui.preferences.PreferencesViewModel
|
||||||
import com.github.damontecres.dolphin.ui.tryRequestFocus
|
import com.github.damontecres.dolphin.ui.tryRequestFocus
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
@ -33,29 +39,71 @@ fun CollectionFolderTv(
|
||||||
navigationManager: NavigationManager,
|
navigationManager: NavigationManager,
|
||||||
destination: Destination.MediaItem,
|
destination: Destination.MediaItem,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
preferencesViewModel: PreferencesViewModel = hiltViewModel(),
|
||||||
) {
|
) {
|
||||||
|
val uiPrefs = preferences.appPreferences.interfacePreferences
|
||||||
|
val rememberedTabIndex =
|
||||||
|
if (uiPrefs.rememberSelectedTab) {
|
||||||
|
uiPrefs.getRememberedTabsOrDefault(destination.itemId.toString(), 0)
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
val tabs = listOf("Recommended", "Library")
|
val tabs = listOf("Recommended", "Library")
|
||||||
var selectedTabIndex by rememberSaveable { mutableIntStateOf(0) }
|
var focusTabIndex by rememberSaveable { mutableIntStateOf(rememberedTabIndex) }
|
||||||
|
var selectedTabIndex by rememberSaveable { mutableIntStateOf(rememberedTabIndex) }
|
||||||
val focusRequester = remember { FocusRequester() }
|
val focusRequester = remember { FocusRequester() }
|
||||||
|
|
||||||
val firstTabFocusRequester = remember { FocusRequester() }
|
val firstTabFocusRequester = remember { FocusRequester() }
|
||||||
LaunchedEffect(Unit) { firstTabFocusRequester.tryRequestFocus() }
|
LaunchedEffect(Unit) { firstTabFocusRequester.tryRequestFocus() }
|
||||||
|
|
||||||
|
if (uiPrefs.rememberSelectedTab) {
|
||||||
|
LaunchedEffect(selectedTabIndex) {
|
||||||
|
preferencesViewModel.preferenceDataStore.updateData {
|
||||||
|
preferences.appPreferences.rememberTab(destination.itemId, selectedTabIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
LaunchedEffect(Unit) { focusRequester.tryRequestFocus() }
|
LaunchedEffect(Unit) { focusRequester.tryRequestFocus() }
|
||||||
Column(
|
Column(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
) {
|
) {
|
||||||
TabRow(
|
TabRow(
|
||||||
selectedTabIndex = selectedTabIndex,
|
selectedTabIndex = focusTabIndex,
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
.padding(start = 32.dp, top = 16.dp)
|
.padding(start = 32.dp, top = 16.dp, bottom = 16.dp)
|
||||||
.focusRestorer(firstTabFocusRequester),
|
.focusRestorer(firstTabFocusRequester)
|
||||||
|
.onFocusChanged {
|
||||||
|
if (!it.isFocused) {
|
||||||
|
focusTabIndex = selectedTabIndex
|
||||||
|
}
|
||||||
|
},
|
||||||
|
indicator =
|
||||||
|
@Composable { tabPositions, doesTabRowHaveFocus ->
|
||||||
|
tabPositions.getOrNull(focusTabIndex)?.let { currentTabPosition ->
|
||||||
|
// TabRowDefaults.PillIndicator(
|
||||||
|
// currentTabPosition = currentTabPosition,
|
||||||
|
// doesTabRowHaveFocus = doesTabRowHaveFocus,
|
||||||
|
// )
|
||||||
|
TabRowDefaults.UnderlinedIndicator(
|
||||||
|
currentTabPosition = currentTabPosition,
|
||||||
|
doesTabRowHaveFocus = doesTabRowHaveFocus,
|
||||||
|
activeColor = MaterialTheme.colorScheme.border,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
tabs = {
|
tabs = {
|
||||||
tabs.forEachIndexed { index, title ->
|
tabs.forEachIndexed { index, title ->
|
||||||
Tab(
|
Tab(
|
||||||
selected = selectedTabIndex == index,
|
selected = focusTabIndex == index,
|
||||||
onClick = { selectedTabIndex = index },
|
onClick = { selectedTabIndex = index },
|
||||||
onFocus = {},
|
onFocus = { focusTabIndex = index },
|
||||||
|
colors =
|
||||||
|
TabDefaults.pillIndicatorTabColors(
|
||||||
|
focusedContentColor = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
),
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = title,
|
text = title,
|
||||||
|
|
@ -64,7 +112,7 @@ fun CollectionFolderTv(
|
||||||
Modifier
|
Modifier
|
||||||
.padding(8.dp)
|
.padding(8.dp)
|
||||||
.ifElse(
|
.ifElse(
|
||||||
index == 0,
|
index == selectedTabIndex,
|
||||||
Modifier.focusRequester(firstTabFocusRequester),
|
Modifier.focusRequester(firstTabFocusRequester),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ data class HomeRow(
|
||||||
fun HomePage(
|
fun HomePage(
|
||||||
preferences: UserPreferences,
|
preferences: UserPreferences,
|
||||||
navigationManager: NavigationManager,
|
navigationManager: NavigationManager,
|
||||||
modifier: Modifier,
|
modifier: Modifier = Modifier,
|
||||||
viewModel: HomeViewModel = hiltViewModel(),
|
viewModel: HomeViewModel = hiltViewModel(),
|
||||||
) {
|
) {
|
||||||
OneTimeLaunchedEffect {
|
OneTimeLaunchedEffect {
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,8 @@ enum ThemeSongVolume {
|
||||||
|
|
||||||
message InterfacePreferences {
|
message InterfacePreferences {
|
||||||
ThemeSongVolume play_theme_songs = 1;
|
ThemeSongVolume play_theme_songs = 1;
|
||||||
|
bool remember_selected_tab = 2;
|
||||||
|
map<string, int32> remembered_tabs = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message AppPreferences {
|
message AppPreferences {
|
||||||
|
|
|
||||||
|
|
@ -59,5 +59,6 @@
|
||||||
<string name="pref_key_update_last_check_threshold">preference.update.threshold</string>
|
<string name="pref_key_update_last_check_threshold">preference.update.threshold</string>
|
||||||
<string name="pref_key_update_last_check">preference.update.lastTimestamp</string>
|
<string name="pref_key_update_last_check">preference.update.lastTimestamp</string>
|
||||||
<string name="confirm">Confirm</string>
|
<string name="confirm">Confirm</string>
|
||||||
|
<string name="remember_selected_tab">Remember selected tabs</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue