Unify settings toggles/chips across playback/profile and fix white-theme selected styling
This commit is contained in:
parent
b04b609f1f
commit
5b24848820
13 changed files with 335 additions and 215 deletions
|
|
@ -2,17 +2,5 @@
|
|||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/Player" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/android-tv-samples" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/libass-android" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/libass-android/lib_ass/src/main/cpp/libass-cmake" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/libass-android/lib_ass/src/main/cpp/libass-cmake/src/ass" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/libass-android/lib_ass/src/main/cpp/libass-cmake/src/expat" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/libass-android/lib_ass/src/main/cpp/libass-cmake/src/fontconfig" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/libass-android/lib_ass/src/main/cpp/libass-cmake/src/freetype" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/libass-android/lib_ass/src/main/cpp/libass-cmake/src/freetype/subprojects/dlg" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/libass-android/lib_ass/src/main/cpp/libass-cmake/src/fribidi" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/libass-android/lib_ass/src/main/cpp/libass-cmake/src/harfbuzz" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/libass-android/lib_ass/src/main/cpp/libass-cmake/src/unibreak" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
|
|
@ -63,8 +63,7 @@ android {
|
|||
|
||||
buildTypes {
|
||||
debug {
|
||||
signingConfig = signingConfigs.getByName("release")
|
||||
isDebuggable = false
|
||||
isDebuggable = true
|
||||
isMinifyEnabled = false
|
||||
|
||||
buildConfigField("boolean", "IS_DEBUG_BUILD", "true")
|
||||
|
|
|
|||
|
|
@ -351,50 +351,50 @@ class MainActivity : ComponentActivity() {
|
|||
Screen.Home.route,
|
||||
Screen.Search.route,
|
||||
Screen.Library.route,
|
||||
Screen.Settings.route,
|
||||
Screen.AddonManager.route
|
||||
Screen.Settings.route
|
||||
)
|
||||
}
|
||||
|
||||
val strNavHome = stringResource(R.string.nav_home)
|
||||
val strNavSearch = stringResource(R.string.nav_search)
|
||||
val strNavLibrary = stringResource(R.string.nav_library)
|
||||
val strNavAddons = stringResource(R.string.nav_addons)
|
||||
val strNavSettings = stringResource(R.string.nav_settings)
|
||||
val drawerItems = remember(
|
||||
strNavHome,
|
||||
strNavSearch,
|
||||
strNavLibrary,
|
||||
strNavAddons,
|
||||
strNavSettings
|
||||
) {
|
||||
listOf(
|
||||
DrawerItem(
|
||||
route = Screen.Home.route,
|
||||
label = strNavHome,
|
||||
icon = Icons.Default.Home
|
||||
),
|
||||
DrawerItem(
|
||||
route = Screen.Search.route,
|
||||
label = strNavSearch,
|
||||
iconRes = R.raw.sidebar_search
|
||||
),
|
||||
DrawerItem(
|
||||
route = Screen.Library.route,
|
||||
label = strNavLibrary,
|
||||
iconRes = R.raw.sidebar_library
|
||||
),
|
||||
DrawerItem(
|
||||
route = Screen.AddonManager.route,
|
||||
label = strNavAddons,
|
||||
iconRes = R.raw.sidebar_plugin
|
||||
),
|
||||
DrawerItem(
|
||||
route = Screen.Settings.route,
|
||||
label = strNavSettings,
|
||||
iconRes = R.raw.sidebar_settings
|
||||
buildList {
|
||||
add(
|
||||
DrawerItem(
|
||||
route = Screen.Home.route,
|
||||
label = strNavHome,
|
||||
icon = Icons.Default.Home
|
||||
)
|
||||
)
|
||||
)
|
||||
add(
|
||||
DrawerItem(
|
||||
route = Screen.Search.route,
|
||||
label = strNavSearch,
|
||||
iconRes = R.raw.sidebar_search
|
||||
)
|
||||
)
|
||||
add(
|
||||
DrawerItem(
|
||||
route = Screen.Library.route,
|
||||
label = strNavLibrary,
|
||||
iconRes = R.raw.sidebar_library
|
||||
)
|
||||
)
|
||||
add(
|
||||
DrawerItem(
|
||||
route = Screen.Settings.route,
|
||||
label = strNavSettings,
|
||||
iconRes = R.raw.sidebar_settings
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
val selectedDrawerRoute = drawerItems.firstOrNull { item ->
|
||||
currentRoute == item.route || currentRoute?.startsWith("${item.route}/") == true
|
||||
|
|
@ -413,6 +413,8 @@ class MainActivity : ComponentActivity() {
|
|||
sidebarCollapsed = sidebarCollapsed,
|
||||
modernSidebarBlurEnabled = modernSidebarBlurEnabled,
|
||||
hideBuiltInHeaders = hideBuiltInHeadersForFloatingPill,
|
||||
activeProfileUsesPrimaryAddons = activeProfile?.usesPrimaryAddons == true,
|
||||
activeProfileUsesPrimaryPlugins = activeProfile?.usesPrimaryPlugins == true,
|
||||
activeProfileName = activeProfile?.name ?: "",
|
||||
activeProfileColorHex = activeProfile?.avatarColorHex ?: "#1E88E5",
|
||||
showProfileSelector = profiles.size > 1,
|
||||
|
|
@ -432,6 +434,8 @@ class MainActivity : ComponentActivity() {
|
|||
selectedDrawerRoute = selectedDrawerRoute,
|
||||
sidebarCollapsed = sidebarCollapsed,
|
||||
hideBuiltInHeaders = false,
|
||||
activeProfileUsesPrimaryAddons = activeProfile?.usesPrimaryAddons == true,
|
||||
activeProfileUsesPrimaryPlugins = activeProfile?.usesPrimaryPlugins == true,
|
||||
activeProfileName = activeProfile?.name ?: "",
|
||||
activeProfileColorHex = activeProfile?.avatarColorHex ?: "#1E88E5",
|
||||
showProfileSelector = profiles.size > 1,
|
||||
|
|
@ -496,6 +500,8 @@ private fun LegacySidebarScaffold(
|
|||
selectedDrawerRoute: String?,
|
||||
sidebarCollapsed: Boolean,
|
||||
hideBuiltInHeaders: Boolean,
|
||||
activeProfileUsesPrimaryAddons: Boolean,
|
||||
activeProfileUsesPrimaryPlugins: Boolean,
|
||||
activeProfileName: String,
|
||||
activeProfileColorHex: String,
|
||||
showProfileSelector: Boolean,
|
||||
|
|
@ -693,7 +699,9 @@ private fun LegacySidebarScaffold(
|
|||
NuvioNavHost(
|
||||
navController = navController,
|
||||
startDestination = startDestination,
|
||||
hideBuiltInHeaders = hideBuiltInHeaders
|
||||
hideBuiltInHeaders = hideBuiltInHeaders,
|
||||
activeProfileUsesPrimaryAddons = activeProfileUsesPrimaryAddons,
|
||||
activeProfileUsesPrimaryPlugins = activeProfileUsesPrimaryPlugins
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -780,6 +788,8 @@ private fun ModernSidebarScaffold(
|
|||
sidebarCollapsed: Boolean,
|
||||
modernSidebarBlurEnabled: Boolean,
|
||||
hideBuiltInHeaders: Boolean,
|
||||
activeProfileUsesPrimaryAddons: Boolean,
|
||||
activeProfileUsesPrimaryPlugins: Boolean,
|
||||
activeProfileName: String,
|
||||
activeProfileColorHex: String,
|
||||
showProfileSelector: Boolean,
|
||||
|
|
@ -1032,7 +1042,9 @@ private fun ModernSidebarScaffold(
|
|||
NuvioNavHost(
|
||||
navController = navController,
|
||||
startDestination = startDestination,
|
||||
hideBuiltInHeaders = hideBuiltInHeaders
|
||||
hideBuiltInHeaders = hideBuiltInHeaders,
|
||||
activeProfileUsesPrimaryAddons = activeProfileUsesPrimaryAddons,
|
||||
activeProfileUsesPrimaryPlugins = activeProfileUsesPrimaryPlugins
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,9 @@ import androidx.tv.material3.FilterChip
|
|||
import androidx.tv.material3.FilterChipDefaults
|
||||
import androidx.tv.material3.MaterialTheme
|
||||
import androidx.tv.material3.Text
|
||||
import com.nuvio.tv.domain.model.AppTheme
|
||||
import com.nuvio.tv.ui.theme.NuvioColors
|
||||
import com.nuvio.tv.ui.theme.NuvioTheme
|
||||
|
||||
enum class SourceChipStatus {
|
||||
LOADING,
|
||||
|
|
@ -54,6 +56,7 @@ fun SourceStatusFilterChip(
|
|||
val isError = status == SourceChipStatus.ERROR
|
||||
val isLoading = status == SourceChipStatus.LOADING
|
||||
val shouldUseNormalColors = !isError
|
||||
val selectedAccent = if (NuvioTheme.currentTheme == AppTheme.WHITE) androidx.compose.ui.graphics.Color.White else NuvioColors.Secondary
|
||||
val shakeOffsetPx = remember { Animatable(0f) }
|
||||
val alphaPulse = remember { Animatable(1f) }
|
||||
|
||||
|
|
@ -102,9 +105,9 @@ fun SourceStatusFilterChip(
|
|||
},
|
||||
colors = FilterChipDefaults.colors(
|
||||
containerColor = if (shouldUseNormalColors) NuvioColors.BackgroundCard else NuvioColors.Error.copy(alpha = 0.05f),
|
||||
focusedContainerColor = if (shouldUseNormalColors) NuvioColors.Secondary else NuvioColors.Error.copy(alpha = 0.08f),
|
||||
selectedContainerColor = if (shouldUseNormalColors) NuvioColors.Secondary else NuvioColors.Error.copy(alpha = 0.07f),
|
||||
focusedSelectedContainerColor = if (shouldUseNormalColors) NuvioColors.Secondary else NuvioColors.Error.copy(alpha = 0.09f),
|
||||
focusedContainerColor = if (shouldUseNormalColors) selectedAccent else NuvioColors.Error.copy(alpha = 0.08f),
|
||||
selectedContainerColor = if (shouldUseNormalColors) selectedAccent else NuvioColors.Error.copy(alpha = 0.07f),
|
||||
focusedSelectedContainerColor = if (shouldUseNormalColors) selectedAccent else NuvioColors.Error.copy(alpha = 0.09f),
|
||||
contentColor = textColor,
|
||||
focusedContentColor = textColor,
|
||||
selectedContentColor = textColor,
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import androidx.compose.animation.ExitTransition
|
|||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.NavType
|
||||
import androidx.navigation.compose.NavHost
|
||||
|
|
@ -39,7 +40,9 @@ import com.nuvio.tv.ui.screens.cast.CastDetailScreen
|
|||
fun NuvioNavHost(
|
||||
navController: NavHostController,
|
||||
startDestination: String = Screen.Home.route,
|
||||
hideBuiltInHeaders: Boolean = false
|
||||
hideBuiltInHeaders: Boolean = false,
|
||||
activeProfileUsesPrimaryAddons: Boolean = false,
|
||||
activeProfileUsesPrimaryPlugins: Boolean = false
|
||||
) {
|
||||
fun isStreamToPlayer(from: String, to: String): Boolean {
|
||||
return from.startsWith("stream/") && to.startsWith("player/")
|
||||
|
|
@ -659,7 +662,8 @@ fun NuvioNavHost(
|
|||
SettingsScreen(
|
||||
showBuiltInHeader = !hideBuiltInHeaders,
|
||||
onNavigateToTrakt = { navController.navigate(Screen.Trakt.route) },
|
||||
onNavigateToAuthQrSignIn = { navController.navigate(Screen.AuthQrSignIn.route) }
|
||||
onNavigateToAuthQrSignIn = { navController.navigate(Screen.AuthQrSignIn.route) },
|
||||
onNavigateToCatalogOrder = { navController.navigate(Screen.CatalogOrder.route) }
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -694,10 +698,19 @@ fun NuvioNavHost(
|
|||
}
|
||||
|
||||
composable(Screen.AddonManager.route) {
|
||||
AddonManagerScreen(
|
||||
showBuiltInHeader = !hideBuiltInHeaders,
|
||||
onNavigateToCatalogOrder = { navController.navigate(Screen.CatalogOrder.route) }
|
||||
)
|
||||
if (activeProfileUsesPrimaryAddons) {
|
||||
LaunchedEffect(Unit) {
|
||||
navController.navigate(Screen.Settings.route) {
|
||||
popUpTo(Screen.AddonManager.route) { inclusive = true }
|
||||
launchSingleTop = true
|
||||
}
|
||||
}
|
||||
} else {
|
||||
AddonManagerScreen(
|
||||
showBuiltInHeader = !hideBuiltInHeaders,
|
||||
onNavigateToCatalogOrder = { navController.navigate(Screen.CatalogOrder.route) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
composable(Screen.CatalogOrder.route) {
|
||||
|
|
@ -707,9 +720,18 @@ fun NuvioNavHost(
|
|||
}
|
||||
|
||||
composable(Screen.Plugins.route) {
|
||||
PluginScreen(
|
||||
onBackPress = { navController.popBackStack() }
|
||||
)
|
||||
if (activeProfileUsesPrimaryPlugins) {
|
||||
LaunchedEffect(Unit) {
|
||||
navController.navigate(Screen.Settings.route) {
|
||||
popUpTo(Screen.Plugins.route) { inclusive = true }
|
||||
launchSingleTop = true
|
||||
}
|
||||
}
|
||||
} else {
|
||||
PluginScreen(
|
||||
onBackPress = { navController.popBackStack() }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
composable(Screen.Account.route) {
|
||||
|
|
|
|||
|
|
@ -95,6 +95,7 @@ import com.nuvio.tv.R
|
|||
fun AddonManagerScreen(
|
||||
viewModel: AddonManagerViewModel = hiltViewModel(),
|
||||
showBuiltInHeader: Boolean = true,
|
||||
embeddedInSettings: Boolean = false,
|
||||
onNavigateToCatalogOrder: () -> Unit = {}
|
||||
) {
|
||||
val uiState by viewModel.uiState.collectAsState()
|
||||
|
|
@ -156,156 +157,182 @@ fun AddonManagerScreen(
|
|||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(NuvioColors.Background)
|
||||
.let {
|
||||
if (embeddedInSettings) it else it.background(NuvioColors.Background)
|
||||
}
|
||||
) {
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentPadding = PaddingValues(horizontal = 36.dp, vertical = 28.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(20.dp)
|
||||
contentPadding = PaddingValues(
|
||||
start = if (embeddedInSettings) 0.dp else 36.dp,
|
||||
end = if (embeddedInSettings) 0.dp else 36.dp,
|
||||
top = if (showBuiltInHeader) 28.dp else 0.dp,
|
||||
bottom = if (embeddedInSettings) 32.dp else 28.dp
|
||||
),
|
||||
verticalArrangement = Arrangement.spacedBy(if (embeddedInSettings) 16.dp else 20.dp)
|
||||
) {
|
||||
item {
|
||||
Text(
|
||||
text = stringResource(R.string.addon_title),
|
||||
style = MaterialTheme.typography.headlineMedium,
|
||||
color = if (showBuiltInHeader) NuvioColors.TextPrimary else Color.Transparent
|
||||
)
|
||||
if (showBuiltInHeader) {
|
||||
item {
|
||||
Text(
|
||||
text = stringResource(R.string.addon_title),
|
||||
style = MaterialTheme.typography.headlineMedium,
|
||||
color = NuvioColors.TextPrimary
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (viewModel.isReadOnly) {
|
||||
item {
|
||||
Card(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
colors = CardDefaults.cardColors(containerColor = Color(0xFF1A3A5C)),
|
||||
shape = RoundedCornerShape(12.dp)
|
||||
) {
|
||||
if (embeddedInSettings) {
|
||||
Text(
|
||||
text = stringResource(R.string.addon_readonly_notice),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = NuvioColors.TextSecondary,
|
||||
modifier = androidx.compose.ui.Modifier.padding(16.dp)
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
} else {
|
||||
Card(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
colors = CardDefaults.cardColors(containerColor = Color(0xFF1A3A5C)),
|
||||
shape = RoundedCornerShape(12.dp)
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.addon_readonly_notice),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = NuvioColors.TextSecondary,
|
||||
modifier = androidx.compose.ui.Modifier.padding(16.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!viewModel.isReadOnly) {
|
||||
item {
|
||||
Card(
|
||||
modifier = Modifier
|
||||
val installContainerModifier = if (embeddedInSettings) {
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.animateContentSize(),
|
||||
colors = CardDefaults.cardColors(containerColor = NuvioColors.BackgroundCard),
|
||||
shape = RoundedCornerShape(12.dp)
|
||||
) {
|
||||
Column(modifier = Modifier.padding(20.dp)) {
|
||||
Text(
|
||||
text = stringResource(R.string.addon_install_title),
|
||||
style = MaterialTheme.typography.titleMedium.copy(fontWeight = FontWeight.Bold),
|
||||
color = NuvioColors.TextPrimary
|
||||
.animateContentSize()
|
||||
} else {
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.animateContentSize()
|
||||
.background(
|
||||
color = NuvioColors.BackgroundCard,
|
||||
shape = RoundedCornerShape(12.dp)
|
||||
)
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
}
|
||||
Column(
|
||||
modifier = installContainerModifier.padding(
|
||||
if (embeddedInSettings) 0.dp else 20.dp
|
||||
)
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.addon_install_title),
|
||||
style = MaterialTheme.typography.titleMedium.copy(fontWeight = FontWeight.Bold),
|
||||
color = NuvioColors.TextPrimary
|
||||
)
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Surface(
|
||||
onClick = { isEditing = true },
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.focusRequester(surfaceFocusRequester),
|
||||
colors = ClickableSurfaceDefaults.colors(
|
||||
containerColor = NuvioColors.BackgroundElevated,
|
||||
focusedContainerColor = NuvioColors.BackgroundElevated
|
||||
),
|
||||
border = ClickableSurfaceDefaults.border(
|
||||
border = Border(
|
||||
border = BorderStroke(1.dp, NuvioColors.Border),
|
||||
shape = RoundedCornerShape(12.dp)
|
||||
),
|
||||
focusedBorder = Border(
|
||||
border = BorderStroke(2.dp, NuvioColors.FocusRing),
|
||||
shape = RoundedCornerShape(12.dp)
|
||||
)
|
||||
),
|
||||
shape = ClickableSurfaceDefaults.shape(RoundedCornerShape(12.dp)),
|
||||
scale = ClickableSurfaceDefaults.scale(focusedScale = 1f)
|
||||
) {
|
||||
// Surface always stays in the tree for stable D-pad focus
|
||||
Surface(
|
||||
onClick = { isEditing = true },
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.focusRequester(surfaceFocusRequester),
|
||||
colors = ClickableSurfaceDefaults.colors(
|
||||
containerColor = NuvioColors.BackgroundElevated,
|
||||
focusedContainerColor = NuvioColors.BackgroundElevated
|
||||
),
|
||||
border = ClickableSurfaceDefaults.border(
|
||||
border = Border(
|
||||
border = BorderStroke(1.dp, NuvioColors.Border),
|
||||
shape = RoundedCornerShape(12.dp)
|
||||
),
|
||||
focusedBorder = Border(
|
||||
border = BorderStroke(2.dp, NuvioColors.FocusRing),
|
||||
shape = RoundedCornerShape(12.dp)
|
||||
)
|
||||
),
|
||||
shape = ClickableSurfaceDefaults.shape(RoundedCornerShape(12.dp)),
|
||||
scale = ClickableSurfaceDefaults.scale(focusedScale = 1f)
|
||||
) {
|
||||
Box(modifier = Modifier.padding(12.dp)) {
|
||||
BasicTextField(
|
||||
value = uiState.installUrl,
|
||||
onValueChange = viewModel::onInstallUrlChange,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.focusRequester(textFieldFocusRequester)
|
||||
.onFocusChanged {
|
||||
if (!it.isFocused && isEditing) {
|
||||
isEditing = false
|
||||
keyboardController?.hide()
|
||||
}
|
||||
},
|
||||
singleLine = true,
|
||||
keyboardOptions = KeyboardOptions(
|
||||
keyboardType = KeyboardType.Uri,
|
||||
imeAction = ImeAction.Done
|
||||
),
|
||||
keyboardActions = KeyboardActions(
|
||||
onDone = {
|
||||
viewModel.installAddon()
|
||||
Box(modifier = Modifier.padding(12.dp)) {
|
||||
BasicTextField(
|
||||
value = uiState.installUrl,
|
||||
onValueChange = viewModel::onInstallUrlChange,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.focusRequester(textFieldFocusRequester)
|
||||
.onFocusChanged {
|
||||
if (!it.isFocused && isEditing) {
|
||||
isEditing = false
|
||||
keyboardController?.hide()
|
||||
installButtonFocusRequester.requestFocus()
|
||||
}
|
||||
),
|
||||
textStyle = MaterialTheme.typography.bodyMedium.copy(
|
||||
color = NuvioColors.TextPrimary
|
||||
),
|
||||
cursorBrush = SolidColor(if (isEditing) NuvioColors.Primary else Color.Transparent),
|
||||
decorationBox = { innerTextField ->
|
||||
if (uiState.installUrl.isEmpty()) {
|
||||
Text(
|
||||
text = stringResource(R.string.addon_install_placeholder),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = NuvioColors.TextTertiary
|
||||
)
|
||||
}
|
||||
innerTextField()
|
||||
},
|
||||
singleLine = true,
|
||||
keyboardOptions = KeyboardOptions(
|
||||
keyboardType = KeyboardType.Uri,
|
||||
imeAction = ImeAction.Done
|
||||
),
|
||||
keyboardActions = KeyboardActions(
|
||||
onDone = {
|
||||
viewModel.installAddon()
|
||||
isEditing = false
|
||||
keyboardController?.hide()
|
||||
installButtonFocusRequester.requestFocus()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Button(
|
||||
onClick = {
|
||||
viewModel.installAddon()
|
||||
isEditing = false
|
||||
keyboardController?.hide()
|
||||
installButtonFocusRequester.requestFocus()
|
||||
},
|
||||
enabled = !uiState.isInstalling,
|
||||
modifier = Modifier.focusRequester(installButtonFocusRequester),
|
||||
colors = ButtonDefaults.colors(
|
||||
containerColor = NuvioColors.BackgroundCard,
|
||||
contentColor = NuvioColors.TextPrimary,
|
||||
focusedContainerColor = NuvioColors.FocusBackground,
|
||||
focusedContentColor = NuvioColors.Primary
|
||||
),
|
||||
shape = ButtonDefaults.shape(RoundedCornerShape(12.dp))
|
||||
) {
|
||||
Text(text = if (uiState.isInstalling) stringResource(R.string.addon_installing) else stringResource(R.string.addon_install_btn))
|
||||
),
|
||||
textStyle = MaterialTheme.typography.bodyMedium.copy(
|
||||
color = NuvioColors.TextPrimary
|
||||
),
|
||||
cursorBrush = SolidColor(if (isEditing) NuvioColors.Primary else Color.Transparent),
|
||||
decorationBox = { innerTextField ->
|
||||
if (uiState.installUrl.isEmpty()) {
|
||||
Text(
|
||||
text = stringResource(R.string.addon_install_placeholder),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = NuvioColors.TextTertiary
|
||||
)
|
||||
}
|
||||
innerTextField()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
AnimatedVisibility(visible = uiState.error != null) {
|
||||
Text(
|
||||
text = uiState.error.orEmpty(),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = NuvioColors.Error,
|
||||
modifier = Modifier.padding(top = 10.dp)
|
||||
)
|
||||
Button(
|
||||
onClick = {
|
||||
viewModel.installAddon()
|
||||
isEditing = false
|
||||
keyboardController?.hide()
|
||||
installButtonFocusRequester.requestFocus()
|
||||
},
|
||||
enabled = !uiState.isInstalling,
|
||||
modifier = Modifier.focusRequester(installButtonFocusRequester),
|
||||
colors = ButtonDefaults.colors(
|
||||
containerColor = NuvioColors.BackgroundCard,
|
||||
contentColor = NuvioColors.TextPrimary,
|
||||
focusedContainerColor = NuvioColors.FocusBackground,
|
||||
focusedContentColor = NuvioColors.Primary
|
||||
),
|
||||
shape = ButtonDefaults.shape(RoundedCornerShape(12.dp))
|
||||
) {
|
||||
Text(text = if (uiState.isInstalling) stringResource(R.string.addon_installing) else stringResource(R.string.addon_install_btn))
|
||||
}
|
||||
}
|
||||
|
||||
AnimatedVisibility(visible = uiState.error != null) {
|
||||
Text(
|
||||
text = uiState.error.orEmpty(),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = NuvioColors.Error,
|
||||
modifier = Modifier.padding(top = 10.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -49,12 +49,14 @@ import androidx.tv.material3.ExperimentalTvMaterial3Api
|
|||
import androidx.tv.material3.Icon
|
||||
import androidx.tv.material3.MaterialTheme
|
||||
import androidx.tv.material3.Text
|
||||
import com.nuvio.tv.domain.model.AppTheme
|
||||
import com.nuvio.tv.data.local.AVAILABLE_SUBTITLE_LANGUAGES
|
||||
import com.nuvio.tv.data.local.AudioLanguageOption
|
||||
import com.nuvio.tv.data.local.PlayerSettings
|
||||
import com.nuvio.tv.data.local.TrailerSettings
|
||||
import com.nuvio.tv.ui.components.NuvioDialog
|
||||
import com.nuvio.tv.ui.theme.NuvioColors
|
||||
import com.nuvio.tv.ui.theme.NuvioTheme
|
||||
|
||||
internal fun LazyListScope.trailerAndAudioSettingsItems(
|
||||
playerSettings: PlayerSettings,
|
||||
|
|
@ -260,6 +262,7 @@ private fun AudioLanguageSelectionDialog(
|
|||
onDismiss: () -> Unit
|
||||
) {
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
val selectedAccent = if (NuvioTheme.currentTheme == AppTheme.WHITE) Color.White else NuvioColors.Secondary
|
||||
val specialOptions = listOf(
|
||||
AudioLanguageOption.DEFAULT to stringResource(R.string.audio_lang_default),
|
||||
AudioLanguageOption.DEVICE to stringResource(R.string.audio_lang_device)
|
||||
|
|
@ -301,8 +304,8 @@ private fun AudioLanguageSelectionDialog(
|
|||
.then(if (index == 0) Modifier.focusRequester(focusRequester) else Modifier)
|
||||
.onFocusChanged { isFocused = it.isFocused },
|
||||
colors = CardDefaults.colors(
|
||||
containerColor = if (isSelected) NuvioColors.FocusBackground else NuvioColors.BackgroundCard,
|
||||
focusedContainerColor = NuvioColors.FocusBackground
|
||||
containerColor = if (isSelected) selectedAccent.copy(alpha = 0.22f) else NuvioColors.BackgroundCard,
|
||||
focusedContainerColor = if (isSelected) selectedAccent.copy(alpha = 0.28f) else NuvioColors.FocusBackground
|
||||
),
|
||||
shape = CardDefaults.shape(shape = RoundedCornerShape(10.dp)),
|
||||
scale = CardDefaults.scale(focusedScale = 1f)
|
||||
|
|
@ -316,7 +319,7 @@ private fun AudioLanguageSelectionDialog(
|
|||
Text(
|
||||
text = name,
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = if (isSelected) NuvioColors.Primary else NuvioColors.TextPrimary,
|
||||
color = NuvioColors.TextPrimary,
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
if (isSelected) {
|
||||
|
|
@ -324,7 +327,7 @@ private fun AudioLanguageSelectionDialog(
|
|||
Icon(
|
||||
imageVector = Icons.Default.Check,
|
||||
contentDescription = stringResource(R.string.cd_selected),
|
||||
tint = NuvioColors.Primary,
|
||||
tint = selectedAccent,
|
||||
modifier = Modifier.size(20.dp)
|
||||
)
|
||||
}
|
||||
|
|
@ -343,6 +346,7 @@ private fun DecoderPriorityDialog(
|
|||
onDismiss: () -> Unit
|
||||
) {
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
val selectedAccent = if (NuvioTheme.currentTheme == AppTheme.WHITE) Color.White else NuvioColors.Secondary
|
||||
val options = listOf(
|
||||
Triple(0, stringResource(R.string.audio_decoder_device_only), stringResource(R.string.audio_decoder_device_only_desc)),
|
||||
Triple(1, stringResource(R.string.audio_decoder_prefer_device), stringResource(R.string.audio_decoder_prefer_device_desc)),
|
||||
|
|
@ -382,8 +386,8 @@ private fun DecoderPriorityDialog(
|
|||
.fillMaxWidth()
|
||||
.then(if (index == 0) Modifier.focusRequester(focusRequester) else Modifier),
|
||||
colors = CardDefaults.colors(
|
||||
containerColor = if (isSelected) NuvioColors.FocusBackground else NuvioColors.BackgroundCard,
|
||||
focusedContainerColor = NuvioColors.FocusBackground
|
||||
containerColor = if (isSelected) selectedAccent.copy(alpha = 0.22f) else NuvioColors.BackgroundCard,
|
||||
focusedContainerColor = if (isSelected) selectedAccent.copy(alpha = 0.28f) else NuvioColors.FocusBackground
|
||||
),
|
||||
shape = CardDefaults.shape(shape = RoundedCornerShape(10.dp)),
|
||||
scale = CardDefaults.scale(focusedScale = 1f)
|
||||
|
|
@ -397,7 +401,7 @@ private fun DecoderPriorityDialog(
|
|||
Column(modifier = Modifier.weight(1f)) {
|
||||
Text(
|
||||
text = title,
|
||||
color = if (isSelected) NuvioColors.Primary else NuvioColors.TextPrimary,
|
||||
color = NuvioColors.TextPrimary,
|
||||
style = MaterialTheme.typography.bodyLarge
|
||||
)
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
|
|
@ -412,7 +416,7 @@ private fun DecoderPriorityDialog(
|
|||
Icon(
|
||||
imageVector = Icons.Default.Check,
|
||||
contentDescription = stringResource(R.string.cd_selected),
|
||||
tint = NuvioColors.Primary,
|
||||
tint = selectedAccent,
|
||||
modifier = Modifier.size(20.dp)
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ import androidx.compose.ui.text.style.TextOverflow
|
|||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.nuvio.tv.R
|
||||
import com.nuvio.tv.domain.model.AppTheme
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import android.view.KeyEvent
|
||||
import androidx.compose.ui.input.key.onKeyEvent
|
||||
|
|
@ -85,8 +86,6 @@ import androidx.tv.material3.ExperimentalTvMaterial3Api
|
|||
import androidx.tv.material3.Icon
|
||||
import androidx.tv.material3.IconButton
|
||||
import androidx.tv.material3.MaterialTheme
|
||||
import androidx.tv.material3.Switch
|
||||
import androidx.tv.material3.SwitchDefaults
|
||||
import androidx.tv.material3.Text
|
||||
import com.nuvio.tv.data.local.AVAILABLE_SUBTITLE_LANGUAGES
|
||||
import com.nuvio.tv.data.local.AudioLanguageOption
|
||||
|
|
@ -98,6 +97,7 @@ import com.nuvio.tv.data.local.StreamAutoPlaySource
|
|||
import com.nuvio.tv.data.local.TrailerSettings
|
||||
import com.nuvio.tv.ui.components.NuvioDialog
|
||||
import com.nuvio.tv.ui.theme.NuvioColors
|
||||
import com.nuvio.tv.ui.theme.NuvioTheme
|
||||
import kotlinx.coroutines.launch
|
||||
import androidx.compose.material.icons.filled.PlayCircle
|
||||
import androidx.compose.material.icons.filled.PauseCircle
|
||||
|
|
@ -404,15 +404,9 @@ internal fun ToggleSettingsItem(
|
|||
|
||||
Spacer(modifier = Modifier.width(16.dp))
|
||||
|
||||
Switch(
|
||||
SettingsTogglePill(
|
||||
checked = isChecked,
|
||||
onCheckedChange = null, // Handled by Card onClick
|
||||
colors = SwitchDefaults.colors(
|
||||
checkedThumbColor = NuvioColors.Secondary.copy(alpha = contentAlpha),
|
||||
checkedTrackColor = NuvioColors.Secondary.copy(alpha = 0.35f * contentAlpha),
|
||||
uncheckedThumbColor = NuvioColors.TextSecondary.copy(alpha = contentAlpha),
|
||||
uncheckedTrackColor = NuvioColors.Border
|
||||
)
|
||||
enabled = enabled
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -428,6 +422,7 @@ internal fun RenderTypeSettingsItem(
|
|||
enabled: Boolean = true
|
||||
) {
|
||||
var isFocused by remember { mutableStateOf(false) }
|
||||
val selectedAccent = if (NuvioTheme.currentTheme == AppTheme.WHITE) Color.White else NuvioColors.Secondary
|
||||
val contentAlpha = if (enabled) 1f else 0.4f
|
||||
|
||||
Card(
|
||||
|
|
@ -443,12 +438,12 @@ internal fun RenderTypeSettingsItem(
|
|||
},
|
||||
colors = CardDefaults.colors(
|
||||
containerColor = if (isSelected) {
|
||||
NuvioColors.Primary.copy(alpha = 0.15f * contentAlpha)
|
||||
selectedAccent.copy(alpha = 0.22f * contentAlpha)
|
||||
} else {
|
||||
NuvioColors.BackgroundCard
|
||||
},
|
||||
focusedContainerColor = if (isSelected) {
|
||||
NuvioColors.Primary.copy(alpha = 0.15f * contentAlpha)
|
||||
selectedAccent.copy(alpha = 0.28f * contentAlpha)
|
||||
} else {
|
||||
NuvioColors.BackgroundCard
|
||||
}
|
||||
|
|
@ -459,7 +454,7 @@ internal fun RenderTypeSettingsItem(
|
|||
shape = RoundedCornerShape(SettingsSecondaryCardRadius)
|
||||
),
|
||||
border = if (isSelected) Border(
|
||||
border = BorderStroke(2.dp, NuvioColors.Primary.copy(alpha = contentAlpha)),
|
||||
border = BorderStroke(2.dp, selectedAccent.copy(alpha = contentAlpha)),
|
||||
shape = RoundedCornerShape(SettingsSecondaryCardRadius)
|
||||
) else Border.None
|
||||
),
|
||||
|
|
@ -476,7 +471,7 @@ internal fun RenderTypeSettingsItem(
|
|||
Text(
|
||||
text = title,
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = (if (isSelected) NuvioColors.Primary else NuvioColors.TextPrimary).copy(alpha = contentAlpha),
|
||||
color = NuvioColors.TextPrimary.copy(alpha = contentAlpha),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
|
|
@ -495,7 +490,7 @@ internal fun RenderTypeSettingsItem(
|
|||
Icon(
|
||||
imageVector = Icons.Default.Check,
|
||||
contentDescription = stringResource(R.string.cd_selected),
|
||||
tint = NuvioColors.Primary.copy(alpha = contentAlpha),
|
||||
tint = selectedAccent.copy(alpha = contentAlpha),
|
||||
modifier = Modifier.size(24.dp)
|
||||
)
|
||||
}
|
||||
|
|
@ -989,6 +984,7 @@ private fun LanguageOptionItem(
|
|||
modifier: Modifier = Modifier
|
||||
) {
|
||||
var isFocused by remember { mutableStateOf(false) }
|
||||
val selectedAccent = if (NuvioTheme.currentTheme == AppTheme.WHITE) Color.White else NuvioColors.Secondary
|
||||
|
||||
Card(
|
||||
onClick = onClick,
|
||||
|
|
@ -997,8 +993,8 @@ private fun LanguageOptionItem(
|
|||
.then(modifier)
|
||||
.onFocusChanged { isFocused = it.isFocused },
|
||||
colors = CardDefaults.colors(
|
||||
containerColor = if (isSelected) NuvioColors.FocusBackground else NuvioColors.BackgroundCard,
|
||||
focusedContainerColor = NuvioColors.FocusBackground
|
||||
containerColor = if (isSelected) selectedAccent.copy(alpha = 0.22f) else NuvioColors.BackgroundCard,
|
||||
focusedContainerColor = if (isSelected) selectedAccent.copy(alpha = 0.28f) else NuvioColors.FocusBackground
|
||||
),
|
||||
shape = CardDefaults.shape(shape = RoundedCornerShape(10.dp)),
|
||||
scale = CardDefaults.scale(focusedScale = 1f)
|
||||
|
|
@ -1012,7 +1008,7 @@ private fun LanguageOptionItem(
|
|||
Text(
|
||||
text = name,
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = if (isSelected) NuvioColors.Primary else NuvioColors.TextPrimary,
|
||||
color = NuvioColors.TextPrimary,
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
|
||||
|
|
@ -1029,7 +1025,7 @@ private fun LanguageOptionItem(
|
|||
Icon(
|
||||
imageVector = Icons.Default.Check,
|
||||
contentDescription = stringResource(R.string.cd_selected),
|
||||
tint = NuvioColors.Primary,
|
||||
tint = selectedAccent,
|
||||
modifier = Modifier.size(20.dp)
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ import com.nuvio.tv.R
|
|||
import androidx.tv.material3.ExperimentalTvMaterial3Api
|
||||
import androidx.tv.material3.MaterialTheme
|
||||
import androidx.tv.material3.Text
|
||||
import com.nuvio.tv.domain.model.AppTheme
|
||||
import com.nuvio.tv.data.local.AVAILABLE_SUBTITLE_LANGUAGES
|
||||
import com.nuvio.tv.data.local.LibassRenderType
|
||||
import com.nuvio.tv.data.local.PlayerSettings
|
||||
|
|
@ -37,6 +38,7 @@ import com.nuvio.tv.data.local.SubtitleOrganizationMode
|
|||
import com.nuvio.tv.data.local.SUBTITLE_LANGUAGE_FORCED
|
||||
import com.nuvio.tv.ui.components.NuvioDialog
|
||||
import com.nuvio.tv.ui.theme.NuvioColors
|
||||
import com.nuvio.tv.ui.theme.NuvioTheme
|
||||
|
||||
private val subtitleColors = listOf(
|
||||
Color.White,
|
||||
|
|
@ -431,6 +433,7 @@ private fun SubtitleOrganizationModeDialog(
|
|||
onModeSelected: (SubtitleOrganizationMode) -> Unit,
|
||||
onDismiss: () -> Unit
|
||||
) {
|
||||
val selectedAccent = if (NuvioTheme.currentTheme == AppTheme.WHITE) Color.White else NuvioColors.Secondary
|
||||
val options = listOf(
|
||||
Triple(SubtitleOrganizationMode.NONE, stringResource(R.string.sub_org_none), stringResource(R.string.sub_org_none_desc)),
|
||||
Triple(SubtitleOrganizationMode.BY_LANGUAGE, stringResource(R.string.sub_org_by_lang), stringResource(R.string.sub_org_by_lang_desc)),
|
||||
|
|
@ -462,8 +465,8 @@ private fun SubtitleOrganizationModeDialog(
|
|||
onClick = { onModeSelected(mode) },
|
||||
modifier = androidx.compose.ui.Modifier.fillMaxWidth(),
|
||||
colors = androidx.tv.material3.CardDefaults.colors(
|
||||
containerColor = if (isSelected) NuvioColors.FocusBackground else NuvioColors.BackgroundCard,
|
||||
focusedContainerColor = NuvioColors.FocusBackground
|
||||
containerColor = if (isSelected) selectedAccent.copy(alpha = 0.22f) else NuvioColors.BackgroundCard,
|
||||
focusedContainerColor = if (isSelected) selectedAccent.copy(alpha = 0.28f) else NuvioColors.FocusBackground
|
||||
),
|
||||
shape = androidx.tv.material3.CardDefaults.shape(
|
||||
shape = androidx.compose.foundation.shape.RoundedCornerShape(10.dp)
|
||||
|
|
@ -482,7 +485,7 @@ private fun SubtitleOrganizationModeDialog(
|
|||
Text(
|
||||
text = title,
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = if (isSelected) NuvioColors.Primary else NuvioColors.TextPrimary
|
||||
color = NuvioColors.TextPrimary
|
||||
)
|
||||
Spacer(modifier = androidx.compose.ui.Modifier.height(4.dp))
|
||||
Text(
|
||||
|
|
@ -495,7 +498,7 @@ private fun SubtitleOrganizationModeDialog(
|
|||
androidx.tv.material3.Icon(
|
||||
imageVector = Icons.Default.Check,
|
||||
contentDescription = stringResource(R.string.cd_selected),
|
||||
tint = NuvioColors.Primary
|
||||
tint = selectedAccent
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
|||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.launch
|
||||
|
|
@ -27,6 +28,13 @@ class ProfileSettingsViewModel @Inject constructor(
|
|||
.map { it == 1 }
|
||||
.stateIn(viewModelScope, SharingStarted.Eagerly, true)
|
||||
|
||||
val activeProfile: StateFlow<UserProfile?> = combine(
|
||||
profileManager.activeProfileId,
|
||||
profileManager.profiles
|
||||
) { activeId, profiles ->
|
||||
profiles.firstOrNull { it.id == activeId }
|
||||
}.stateIn(viewModelScope, SharingStarted.Eagerly, profileManager.activeProfile)
|
||||
|
||||
val canAddProfile: Boolean
|
||||
get() = profileManager.profiles.value.size < 4
|
||||
|
||||
|
|
|
|||
|
|
@ -58,8 +58,10 @@ import androidx.tv.material3.Text
|
|||
import coil.compose.rememberAsyncImagePainter
|
||||
import coil.decode.SvgDecoder
|
||||
import coil.request.ImageRequest
|
||||
import com.nuvio.tv.domain.model.AppTheme
|
||||
import com.nuvio.tv.R
|
||||
import com.nuvio.tv.ui.theme.NuvioColors
|
||||
import com.nuvio.tv.ui.theme.NuvioTheme
|
||||
|
||||
internal val SettingsContainerRadius = 28.dp
|
||||
internal val SettingsPillRadius = 999.dp
|
||||
|
|
@ -538,6 +540,7 @@ internal fun SettingsChoiceChip(
|
|||
onFocused: () -> Unit = {}
|
||||
) {
|
||||
var isFocused by remember { mutableStateOf(false) }
|
||||
val selectedAccent = if (NuvioTheme.currentTheme == AppTheme.WHITE) Color.White else NuvioColors.Secondary
|
||||
|
||||
Card(
|
||||
onClick = onClick,
|
||||
|
|
@ -549,12 +552,12 @@ internal fun SettingsChoiceChip(
|
|||
}
|
||||
},
|
||||
colors = CardDefaults.colors(
|
||||
containerColor = if (selected) NuvioColors.FocusRing.copy(alpha = 0.2f) else NuvioColors.Background,
|
||||
focusedContainerColor = if (selected) NuvioColors.FocusRing.copy(alpha = 0.2f) else NuvioColors.Background
|
||||
containerColor = if (selected) selectedAccent.copy(alpha = 0.22f) else NuvioColors.Background,
|
||||
focusedContainerColor = if (selected) selectedAccent.copy(alpha = 0.28f) else NuvioColors.Background
|
||||
),
|
||||
border = CardDefaults.border(
|
||||
border = if (selected) Border(
|
||||
border = BorderStroke(1.dp, NuvioColors.FocusRing),
|
||||
border = BorderStroke(1.dp, selectedAccent),
|
||||
shape = RoundedCornerShape(SettingsPillRadius)
|
||||
) else Border.None,
|
||||
focusedBorder = Border(
|
||||
|
|
@ -568,17 +571,18 @@ internal fun SettingsChoiceChip(
|
|||
Text(
|
||||
text = label,
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
color = if (selected || isFocused) NuvioColors.TextPrimary else NuvioColors.TextSecondary,
|
||||
color = if (selected) selectedAccent else if (isFocused) NuvioColors.TextPrimary else NuvioColors.TextSecondary,
|
||||
modifier = Modifier.padding(horizontal = 16.dp, vertical = 10.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SettingsTogglePill(
|
||||
internal fun SettingsTogglePill(
|
||||
checked: Boolean,
|
||||
enabled: Boolean
|
||||
) {
|
||||
val checkedAccent = if (NuvioTheme.currentTheme == AppTheme.WHITE) Color.White else NuvioColors.Secondary
|
||||
val alpha = if (enabled) 1f else 0.35f
|
||||
Box(
|
||||
modifier = Modifier
|
||||
|
|
@ -587,7 +591,7 @@ private fun SettingsTogglePill(
|
|||
.clip(RoundedCornerShape(SettingsPillRadius))
|
||||
.background(
|
||||
if (checked) {
|
||||
NuvioColors.Secondary.copy(alpha = 0.35f * alpha)
|
||||
checkedAccent.copy(alpha = 0.42f * alpha)
|
||||
} else {
|
||||
NuvioColors.Border.copy(alpha = alpha)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
|||
import androidx.tv.material3.ExperimentalTvMaterial3Api
|
||||
import com.nuvio.tv.BuildConfig
|
||||
import com.nuvio.tv.R
|
||||
import com.nuvio.tv.ui.screens.addon.AddonManagerScreen
|
||||
import com.nuvio.tv.ui.screens.plugin.PluginScreenContent
|
||||
import com.nuvio.tv.ui.theme.NuvioColors
|
||||
import kotlinx.coroutines.delay
|
||||
|
|
@ -63,6 +64,7 @@ internal enum class SettingsCategory {
|
|||
PROFILES,
|
||||
APPEARANCE,
|
||||
LAYOUT,
|
||||
ADDONS,
|
||||
PLUGINS,
|
||||
INTEGRATION,
|
||||
PLAYBACK,
|
||||
|
|
@ -125,6 +127,13 @@ private fun rememberSettingsSectionSpecs() = listOf(
|
|||
subtitle = stringResource(R.string.settings_layout_subtitle),
|
||||
destination = SettingsSectionDestination.Inline
|
||||
),
|
||||
SettingsSectionSpec(
|
||||
category = SettingsCategory.ADDONS,
|
||||
title = stringResource(R.string.addon_title),
|
||||
rawIconRes = R.raw.sidebar_plugin,
|
||||
subtitle = stringResource(R.string.settings_addons_subtitle),
|
||||
destination = SettingsSectionDestination.Inline
|
||||
),
|
||||
SettingsSectionSpec(
|
||||
category = SettingsCategory.PLUGINS,
|
||||
title = stringResource(R.string.settings_plugins),
|
||||
|
|
@ -174,18 +183,29 @@ fun SettingsScreen(
|
|||
showBuiltInHeader: Boolean = true,
|
||||
onNavigateToTrakt: () -> Unit = {},
|
||||
onNavigateToAuthQrSignIn: () -> Unit = {},
|
||||
onNavigateToCatalogOrder: () -> Unit = {},
|
||||
profileViewModel: ProfileSettingsViewModel = hiltViewModel()
|
||||
) {
|
||||
val isPrimaryProfileActive by profileViewModel.isPrimaryProfileActive.collectAsStateWithLifecycle()
|
||||
val activeProfile by profileViewModel.activeProfile.collectAsStateWithLifecycle()
|
||||
val usesPrimaryProfileAddons = activeProfile?.let { !it.isPrimary && it.usesPrimaryAddons } == true
|
||||
val usesPrimaryProfilePlugins = activeProfile?.let { !it.isPrimary && it.usesPrimaryPlugins } == true
|
||||
|
||||
val allSectionSpecs = rememberSettingsSectionSpecs()
|
||||
val visibleSections = remember(isPrimaryProfileActive, allSectionSpecs) {
|
||||
val visibleSections = remember(
|
||||
isPrimaryProfileActive,
|
||||
usesPrimaryProfileAddons,
|
||||
usesPrimaryProfilePlugins,
|
||||
allSectionSpecs
|
||||
) {
|
||||
allSectionSpecs.filter { section ->
|
||||
when (section.category) {
|
||||
SettingsCategory.DEBUG -> BuildConfig.IS_DEBUG_BUILD
|
||||
SettingsCategory.PROFILES -> isPrimaryProfileActive
|
||||
SettingsCategory.ACCOUNT -> isPrimaryProfileActive
|
||||
SettingsCategory.TRAKT -> isPrimaryProfileActive
|
||||
SettingsCategory.ADDONS -> !usesPrimaryProfileAddons
|
||||
SettingsCategory.PLUGINS -> !usesPrimaryProfilePlugins
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
|
|
@ -373,6 +393,9 @@ fun SettingsScreen(
|
|||
null
|
||||
}
|
||||
)
|
||||
SettingsCategory.ADDONS -> AddonsSettingsContent(
|
||||
onNavigateToCatalogOrder = onNavigateToCatalogOrder
|
||||
)
|
||||
SettingsCategory.PLAYBACK -> PlaybackSettingsContent(
|
||||
initialFocusRequester = if (allowDetailAutofocus) {
|
||||
contentFocusRequesters[SettingsCategory.PLAYBACK]
|
||||
|
|
@ -443,6 +466,35 @@ private fun PluginsSettingsContent() {
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AddonsSettingsContent(
|
||||
onNavigateToCatalogOrder: () -> Unit
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
SettingsDetailHeader(
|
||||
title = stringResource(R.string.addon_title),
|
||||
subtitle = stringResource(R.string.settings_addons_section_subtitle)
|
||||
)
|
||||
SettingsGroupCard(modifier = Modifier.fillMaxSize()) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.weight(1f),
|
||||
contentAlignment = Alignment.TopStart
|
||||
) {
|
||||
AddonManagerScreen(
|
||||
showBuiltInHeader = false,
|
||||
embeddedInSettings = true,
|
||||
onNavigateToCatalogOrder = onNavigateToCatalogOrder
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AccountSettingsInline(
|
||||
onNavigateToAuthQrSignIn: () -> Unit
|
||||
|
|
|
|||
|
|
@ -122,6 +122,7 @@
|
|||
<string name="settings_profiles_subtitle">Manage user profiles.</string>
|
||||
<string name="settings_layout">Layout</string>
|
||||
<string name="settings_layout_subtitle">Home structure and poster styles.</string>
|
||||
<string name="settings_addons_subtitle">Addon installation and catalog sources.</string>
|
||||
<string name="settings_plugins">Plugins</string>
|
||||
<string name="settings_plugins_subtitle">Repositories and providers.</string>
|
||||
<string name="settings_integration">Integration</string>
|
||||
|
|
@ -131,6 +132,7 @@
|
|||
<string name="settings_about_subtitle">Version and policies.</string>
|
||||
<string name="settings_debug">Debug</string>
|
||||
<string name="settings_debug_subtitle">Developer tools and feature flags.</string>
|
||||
<string name="settings_addons_section_subtitle">Manage addon installation, updates, and catalog order.</string>
|
||||
<string name="settings_plugins_section_subtitle">Manage repositories, providers, and plugin states.</string>
|
||||
<string name="settings_account_section_subtitle">Account and sync status.</string>
|
||||
<string name="account_stat_addons">addons</string>
|
||||
|
|
|
|||
Loading…
Reference in a new issue