flatten settigns navigation + move next mcu card behind preference

This commit is contained in:
Owen LeJeune
2023-07-09 15:37:26 -04:00
parent 080023c93e
commit f1471e280b
7 changed files with 605 additions and 501 deletions

View File

@@ -34,6 +34,7 @@ class AppPreferences(context: Context) {
private val ACCOUNT_TAB_POSITION = "account_tab_position" private val ACCOUNT_TAB_POSITION = "account_tab_position"
private val SHOW_BTAB_LABELS = "show_btab_labels" private val SHOW_BTAB_LABELS = "show_btab_labels"
private val SHOW_POSTER_TITLE = "show_poster_titles" private val SHOW_POSTER_TITLE = "show_poster_titles"
private val SHOW_NEXT_MCU = "show_next_mcu"
} }
private val preferences: SharedPreferences = context.getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE) private val preferences: SharedPreferences = context.getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE)
@@ -128,6 +129,12 @@ class AppPreferences(context: Context) {
get() = preferences.getBoolean(SHOW_BACKDROP_GALLERY, showBackdropGalleryDefault) get() = preferences.getBoolean(SHOW_BACKDROP_GALLERY, showBackdropGalleryDefault)
set(value) { preferences.put(SHOW_BACKDROP_GALLERY, value) } set(value) { preferences.put(SHOW_BACKDROP_GALLERY, value) }
/******** Special Features Preferences ********/
val showNextMcuProductionDefault: Boolean = false
var showNextMcuProduction: Boolean
get() = preferences.getBoolean(SHOW_NEXT_MCU, showNextMcuProductionDefault)
set(value) { preferences.put(SHOW_NEXT_MCU, value) }
/********* Helpers ********/ /********* Helpers ********/
private fun SharedPreferences.put(key: String, value: Any?) { private fun SharedPreferences.put(key: String, value: Any?) {
edit().apply { edit().apply {

View File

@@ -1,7 +1,5 @@
package com.owenlejeune.tvtime.ui.navigation package com.owenlejeune.tvtime.ui.navigation
import android.os.Build
import android.util.Log
import androidx.compose.animation.AnimatedContentTransitionScope import androidx.compose.animation.AnimatedContentTransitionScope
import androidx.compose.animation.core.tween import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeIn
@@ -93,18 +91,6 @@ fun AppNavigationHost(
} }
} }
} }
composable(
AppNavItem.SettingsView.route.plus("/{${NavConstants.SETTINGS_KEY}}"),
arguments = listOf(
navArgument(NavConstants.SETTINGS_KEY) { type = NavType.StringType }
)
) {
val route = it.arguments?.getString(NavConstants.SETTINGS_KEY)
SettingsScreen(
appNavController = appNavController,
route = route
)
}
composable(AppNavItem.SettingsView.route) { composable(AppNavItem.SettingsView.route) {
SettingsScreen(appNavController = appNavController) SettingsScreen(appNavController = appNavController)
} }
@@ -237,9 +223,7 @@ sealed class AppNavItem(val route: String) {
object DetailView: AppNavItem("detail_route") { object DetailView: AppNavItem("detail_route") {
fun withArgs(type: MediaViewType, id: Int) = route.plus("/${type}/${id}") fun withArgs(type: MediaViewType, id: Int) = route.plus("/${type}/${id}")
} }
object SettingsView: AppNavItem("settings_route") { object SettingsView: AppNavItem("settings_route")
fun withArgs(page: String) = route.plus("/$page")
}
object SearchView: AppNavItem("search_route") { object SearchView: AppNavItem("search_route") {
fun withArgs(searchType: MediaViewType, pageTitle: String) = route.plus("?searchType=$searchType&pageTitle=$pageTitle") fun withArgs(searchType: MediaViewType, pageTitle: String) = route.plus("?searchType=$searchType&pageTitle=$pageTitle")
} }

View File

@@ -0,0 +1,84 @@
package com.owenlejeune.tvtime.ui.navigation
import androidx.compose.animation.AnimatedContentTransitionScope
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.ui.res.stringResource
import androidx.navigation.NavController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import com.owenlejeune.tvtime.R
import com.owenlejeune.tvtime.ui.screens.DarkModePreferences
import com.owenlejeune.tvtime.ui.screens.DesignPreferences
import com.owenlejeune.tvtime.ui.screens.DevPreferences
import com.owenlejeune.tvtime.ui.screens.HomeScreenPreferences
import com.owenlejeune.tvtime.ui.screens.SearchPreferences
import com.owenlejeune.tvtime.ui.screens.SettingsHome
import com.owenlejeune.tvtime.ui.screens.SpecialFeaturePreferences
@Composable
fun SettingsNavigationHost(
appNavController: NavController,
appBarTitle: MutableState<String>,
popBackAction: MutableState<() -> Unit>
) {
val settingsNavController = rememberNavController()
NavHost(
navController = settingsNavController,
startDestination = SettingsNavItem.Home.route,
enterTransition = { slideIntoContainer(AnimatedContentTransitionScope.SlideDirection.Start, tween(500)) },
popEnterTransition = { fadeIn(tween(500)) },
exitTransition = { fadeOut(tween(500)) },
popExitTransition = { slideOutOfContainer(AnimatedContentTransitionScope.SlideDirection.End, tween(500)) }
) {
composable(SettingsNavItem.Home.route) {
appBarTitle.value = stringResource(id = R.string.nav_settings_title)
popBackAction.value = { appNavController.popBackStack() }
SettingsHome(settingsNavController = settingsNavController)
}
composable(SettingsNavItem.Search.route) {
appBarTitle.value = stringResource(id = R.string.preference_heading_search)
popBackAction.value = { settingsNavController.popBackStack() }
SearchPreferences()
}
composable(SettingsNavItem.Design.route) {
appBarTitle.value = stringResource(id = R.string.preference_heading_design)
popBackAction.value = { settingsNavController.popBackStack() }
DesignPreferences(settingsNavController = settingsNavController)
}
composable(SettingsNavItem.DarkMode.route) {
appBarTitle.value = stringResource(id = R.string.preference_heading_dark_mode)
popBackAction.value = { settingsNavController.popBackStack() }
DarkModePreferences()
}
composable(SettingsNavItem.HomeScreen.route) {
appBarTitle.value = stringResource(id = R.string.preference_heading_home_screen)
popBackAction.value = { settingsNavController.popBackStack() }
HomeScreenPreferences()
}
composable(SettingsNavItem.SpecialFeatures.route) {
appBarTitle.value = stringResource(id = R.string.preference_heading_special_features)
popBackAction.value = { settingsNavController.popBackStack() }
SpecialFeaturePreferences()
}
composable(SettingsNavItem.Developer.route) {
appBarTitle.value = stringResource(id = R.string.preferences_debug_title)
popBackAction.value = { settingsNavController.popBackStack() }
DevPreferences()
}
}
}
sealed class SettingsNavItem(val route: String) {
object Home: SettingsNavItem("settings_home")
object Search: SettingsNavItem("settings_search")
object Design: SettingsNavItem("settings_design")
object DarkMode: SettingsNavItem("settings_dark_mode")
object HomeScreen: SettingsNavItem("settings_home_screen")
object SpecialFeatures: SettingsNavItem("settings_special_feature")
object Developer: SettingsNavItem("settings_developer")
}

View File

@@ -46,6 +46,7 @@ import com.owenlejeune.tvtime.extensions.format
import com.owenlejeune.tvtime.extensions.getCalendarYear import com.owenlejeune.tvtime.extensions.getCalendarYear
import com.owenlejeune.tvtime.extensions.lazyPagingItems import com.owenlejeune.tvtime.extensions.lazyPagingItems
import com.owenlejeune.tvtime.extensions.listItems import com.owenlejeune.tvtime.extensions.listItems
import com.owenlejeune.tvtime.preferences.AppPreferences
import com.owenlejeune.tvtime.ui.components.* import com.owenlejeune.tvtime.ui.components.*
import com.owenlejeune.tvtime.ui.navigation.AppNavItem import com.owenlejeune.tvtime.ui.navigation.AppNavItem
import com.owenlejeune.tvtime.ui.viewmodel.MainViewModel import com.owenlejeune.tvtime.ui.viewmodel.MainViewModel
@@ -54,6 +55,7 @@ import com.owenlejeune.tvtime.utils.SessionManager
import com.owenlejeune.tvtime.utils.TmdbUtils import com.owenlejeune.tvtime.utils.TmdbUtils
import com.owenlejeune.tvtime.utils.types.MediaViewType import com.owenlejeune.tvtime.utils.types.MediaViewType
import kotlinx.coroutines.* import kotlinx.coroutines.*
import org.koin.java.KoinJavaComponent.get
@OptIn(ExperimentalMaterial3Api::class, ExperimentalPagerApi::class) @OptIn(ExperimentalMaterial3Api::class, ExperimentalPagerApi::class)
@Composable @Composable
@@ -152,7 +154,8 @@ private fun MediaViewContent(
type: MediaViewType, type: MediaViewType,
windowSize: WindowSizeClass, windowSize: WindowSizeClass,
showImageGallery: MutableState<Boolean>, showImageGallery: MutableState<Boolean>,
pagerState: PagerState pagerState: PagerState,
preferences: AppPreferences = get(AppPreferences::class.java)
) { ) {
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
mainViewModel.getExternalIds(itemId, type) mainViewModel.getExternalIds(itemId, type)
@@ -249,7 +252,10 @@ private fun MediaViewContent(
WatchProvidersCard(itemId = itemId, type = type, mainViewModel = mainViewModel) WatchProvidersCard(itemId = itemId, type = type, mainViewModel = mainViewModel)
if (mediaItem?.productionCompanies?.firstOrNull { it.name == "Marvel Studios" } != null) { if (
mediaItem?.productionCompanies?.firstOrNull { it.name == "Marvel Studios" } != null
&& preferences.showNextMcuProduction
) {
NextMcuProjectCard(appNavController = appNavController) NextMcuProjectCard(appNavController = appNavController)
} }
@@ -1078,7 +1084,7 @@ private fun NextMcuProjectCard(
Text( Text(
text = nextMcuProject.value?.title ?: "", text = nextMcuProject.value?.title ?: "",
style = MaterialTheme.typography.headlineSmall, style = MaterialTheme.typography.headlineSmall,
fontWeight = FontWeight.W700 fontWeight = FontWeight.W600
) )
val releaseDate = val releaseDate =

View File

@@ -1,20 +1,50 @@
package com.owenlejeune.tvtime.ui.screens package com.owenlejeune.tvtime.ui.screens
import android.app.Activity
import android.os.Build import android.os.Build
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.border import androidx.compose.foundation.border
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.IconButton import androidx.compose.material.IconButton
import androidx.compose.material.TextButton import androidx.compose.material.TextButton
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.* import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.Brightness6
import androidx.compose.material.icons.filled.Check
import androidx.compose.material.icons.filled.DeveloperBoard
import androidx.compose.material.icons.filled.Home
import androidx.compose.material.icons.filled.Palette
import androidx.compose.material.icons.filled.RestartAlt
import androidx.compose.material.icons.filled.Search
import androidx.compose.material.icons.outlined.DarkMode import androidx.compose.material.icons.outlined.DarkMode
import androidx.compose.material.icons.outlined.Flare
import androidx.compose.material.icons.outlined.LightMode import androidx.compose.material.icons.outlined.LightMode
import androidx.compose.material3.* import androidx.compose.material3.AlertDialog
import androidx.compose.runtime.* import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.LargeTopAppBar
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.rememberTopAppBarState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
@@ -36,25 +66,24 @@ import com.owenlejeune.tvtime.BuildConfig
import com.owenlejeune.tvtime.OnboardingActivity import com.owenlejeune.tvtime.OnboardingActivity
import com.owenlejeune.tvtime.R import com.owenlejeune.tvtime.R
import com.owenlejeune.tvtime.preferences.AppPreferences import com.owenlejeune.tvtime.preferences.AppPreferences
import com.owenlejeune.tvtime.ui.components.* import com.owenlejeune.tvtime.ui.components.CenteredIconCircle
import com.owenlejeune.tvtime.ui.navigation.AppNavItem import com.owenlejeune.tvtime.ui.components.PaletteView
import com.owenlejeune.tvtime.ui.components.PreferenceHeading
import com.owenlejeune.tvtime.ui.components.RadioButtonPreference
import com.owenlejeune.tvtime.ui.components.SliderPreference
import com.owenlejeune.tvtime.ui.components.SwitchPreference
import com.owenlejeune.tvtime.ui.navigation.SettingsNavItem
import com.owenlejeune.tvtime.ui.navigation.SettingsNavigationHost
import com.owenlejeune.tvtime.ui.viewmodel.SettingsViewModel import com.owenlejeune.tvtime.ui.viewmodel.SettingsViewModel
import com.owenlejeune.tvtime.ui.views.HomeTabRecyclerAdapter import com.owenlejeune.tvtime.ui.views.HomeTabRecyclerAdapter
import com.owenlejeune.tvtime.ui.views.ItemMoveCallback import com.owenlejeune.tvtime.ui.views.ItemMoveCallback
import com.owenlejeune.tvtime.utils.ResourceUtils
import com.owenlejeune.tvtime.utils.SessionManager import com.owenlejeune.tvtime.utils.SessionManager
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.koin.androidx.compose.koinViewModel
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
import org.koin.java.KoinJavaComponent.get
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun SettingsScreen( fun SettingsScreen(
appNavController: NavController, appNavController: NavController
route: String? = null,
preferences: AppPreferences = get(AppPreferences::class.java)
) { ) {
val systemUiController = rememberSystemUiController() val systemUiController = rememberSystemUiController()
systemUiController.setStatusBarColor(color = MaterialTheme.colorScheme.background) systemUiController.setStatusBarColor(color = MaterialTheme.colorScheme.background)
@@ -64,12 +93,7 @@ fun SettingsScreen(
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(topAppBarScrollState) val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(topAppBarScrollState)
val appBarTitle = remember { mutableStateOf("") } val appBarTitle = remember { mutableStateOf("") }
val defaultRestoreAction = ::resetAllPreferences val popBackAction = remember { mutableStateOf({}) }
val restoreAction = remember { mutableStateOf<SettingsResetHandler>(defaultRestoreAction) }
val settingsViewModel = viewModel<SettingsViewModel>()
val activity = LocalContext.current as AppCompatActivity
Scaffold( Scaffold(
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
@@ -83,7 +107,7 @@ fun SettingsScreen(
title = { Text(text = appBarTitle.value) }, title = { Text(text = appBarTitle.value) },
navigationIcon = { navigationIcon = {
IconButton( IconButton(
onClick = { appNavController.popBackStack() } onClick = popBackAction.value
) { ) {
Icon( Icon(
imageVector = Icons.Filled.ArrowBack, imageVector = Icons.Filled.ArrowBack,
@@ -91,53 +115,35 @@ fun SettingsScreen(
) )
} }
}, },
actions = { // actions = {
IconButton( // IconButton(
onClick = { // onClick = {
restoreAction.value(preferences, settingsViewModel, activity) // restoreAction.value(preferences, settingsViewModel, activity)
} // }
) { // ) {
Icon( // Icon(
imageVector = Icons.Filled.SettingsBackupRestore, // imageVector = Icons.Filled.SettingsBackupRestore,
contentDescription = stringResource(R.string.preferences_restore_content_description) // contentDescription = stringResource(R.string.preferences_restore_content_description)
) // )
} // }
} // }
) )
} }
) { innerPadding -> ) { innerPadding ->
Box(modifier = Modifier.padding(innerPadding)) { Box(modifier = Modifier.padding(innerPadding)) {
if (route != null) { SettingsNavigationHost(
Column(
modifier = Modifier
.fillMaxSize()
.padding(all = 24.dp),
verticalArrangement = Arrangement.spacedBy(18.dp)
) {
SettingsPage.getByRoute(route).apply {
appBarTitle.value = name
restoreAction.value = resetPreferencesHandler
settingsPageRenderer(appNavController, activity)
}
}
} else {
restoreAction.value = ::resetAllPreferences
SettingsTabView(
appNavController = appNavController, appNavController = appNavController,
appBarTitle = appBarTitle appBarTitle = appBarTitle,
popBackAction = popBackAction
) )
} }
} }
}
} }
@Composable @Composable
private fun SettingsTabView( fun SettingsHome(
appNavController: NavController, settingsNavController: NavController
appBarTitle: MutableState<String>
) { ) {
appBarTitle.value = stringResource(id = R.string.nav_settings_title)
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
@@ -148,81 +154,54 @@ private fun SettingsTabView(
title = stringResource(id = R.string.preference_heading_search), title = stringResource(id = R.string.preference_heading_search),
subtitle = stringResource(R.string.preference_subtitle_search), subtitle = stringResource(R.string.preference_subtitle_search),
icon = Icons.Filled.Search, icon = Icons.Filled.Search,
settingsView = SettingsPage.SearchSettings, route = SettingsNavItem.Search.route,
appNavController = appNavController settingsNavController = settingsNavController
) )
TopLevelSettingsCard( TopLevelSettingsCard(
title = stringResource(id = R.string.preference_heading_design), title = stringResource(id = R.string.preference_heading_design),
subtitle = stringResource(R.string.preference_subtitle_design), subtitle = stringResource(R.string.preference_subtitle_design),
icon = Icons.Filled.Palette, icon = Icons.Filled.Palette,
settingsView = SettingsPage.DesignSettings, route = SettingsNavItem.Design.route,
appNavController = appNavController settingsNavController = settingsNavController
) )
TopLevelSettingsCard( TopLevelSettingsCard(
title = stringResource(id = R.string.preference_heading_home_screen), title = stringResource(id = R.string.preference_heading_home_screen),
subtitle = stringResource(R.string.preference_subtitle_home_screen), subtitle = stringResource(R.string.preference_subtitle_home_screen),
icon = Icons.Filled.Home, icon = Icons.Filled.Home,
settingsView = SettingsPage.HomeScreenSettings, route = SettingsNavItem.HomeScreen.route,
appNavController = appNavController settingsNavController = settingsNavController
)
TopLevelSettingsCard(
title = stringResource(id = R.string.preference_heading_special_features),
subtitle = stringResource(id = R.string.preference_subtitle_special_features),
icon = Icons.Outlined.Flare,
route = SettingsNavItem.SpecialFeatures.route,
settingsNavController = settingsNavController
) )
TopLevelSettingsCard( TopLevelSettingsCard(
title = stringResource(id = R.string.preferences_debug_title), title = stringResource(id = R.string.preferences_debug_title),
subtitle = stringResource(R.string.preference_subtitle_debug), subtitle = stringResource(R.string.preference_subtitle_debug),
icon = Icons.Filled.DeveloperBoard, icon = Icons.Filled.DeveloperBoard,
settingsView = SettingsPage.DeveloperSettings, route = SettingsNavItem.Developer.route,
appNavController = appNavController settingsNavController = settingsNavController
) )
} }
} }
@Composable @Composable
private fun TopLevelSettingsCard( fun SearchPreferences() {
title: String, val settingsViewModel = viewModel<SettingsViewModel>()
subtitle: String? = null,
icon: ImageVector,
settingsView: SettingsPage,
appNavController: NavController
) {
Row(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.clickable(
onClick = {
appNavController.navigate(AppNavItem.SettingsView.withArgs(settingsView.route))
}
)
) {
Icon(
imageVector = icon,
contentDescription = subtitle,
modifier = Modifier
.align(Alignment.CenterVertically)
.padding(end = 16.dp),
tint = MaterialTheme.colorScheme.primary
)
Column( Column(
modifier = Modifier modifier = Modifier
.align(Alignment.CenterVertically) .fillMaxSize()
.weight(1f) .padding(all = 24.dp),
verticalArrangement = Arrangement.spacedBy(24.dp)
) { ) {
val titleColor = MaterialTheme.colorScheme.onBackground
val subtitleColor = MaterialTheme.colorScheme.onSurfaceVariant
Text(text = title, style = MaterialTheme.typography.titleLarge, color = titleColor, fontSize = 20.sp)
subtitle?.let {
Text(text = subtitle, style = MaterialTheme.typography.bodyMedium, color = subtitleColor)
}
}
}
}
@Composable
private fun SearchPreferences() {
val settingsViewModel = koinViewModel<SettingsViewModel>()
SwitchPreference( SwitchPreference(
titleText = stringResource(R.string.preferences_persistent_search_title), titleText = stringResource(R.string.preferences_persistent_search_title),
subtitleText = stringResource(R.string.preferences_persistent_search_subtitle), subtitleText = stringResource(R.string.preferences_persistent_search_subtitle),
@@ -240,21 +219,28 @@ private fun SearchPreferences() {
settingsViewModel.toggleMultiSearch() settingsViewModel.toggleMultiSearch()
} }
) )
}
} }
@Composable @Composable
private fun DesignPreferences( fun DesignPreferences(
appNavController: NavController, settingsNavController: NavController,
activity: AppCompatActivity
) { ) {
val settingsViewModel = viewModel<SettingsViewModel>() val settingsViewModel = viewModel<SettingsViewModel>()
val activity = LocalContext.current as Activity
Column(
modifier = Modifier
.fillMaxSize()
.padding(all = 24.dp),
verticalArrangement = Arrangement.spacedBy(24.dp)
) {
TopLevelSettingsCard( TopLevelSettingsCard(
title = stringResource(id = R.string.preference_heading_dark_mode), title = stringResource(id = R.string.preference_heading_dark_mode),
subtitle = stringResource(R.string.preference_subtitle_dark_mode), subtitle = stringResource(R.string.preference_subtitle_dark_mode),
icon = Icons.Outlined.DarkMode, icon = Icons.Outlined.DarkMode,
settingsView = SettingsPage.DarkModeSettings, route = SettingsNavItem.DarkMode.route,
appNavController = appNavController settingsNavController = settingsNavController
) )
SwitchPreference( SwitchPreference(
@@ -318,12 +304,13 @@ private fun DesignPreferences(
if (showWallpaperPicker.value) { if (showWallpaperPicker.value) {
WallpaperPicker(showPopup = showWallpaperPicker) WallpaperPicker(showPopup = showWallpaperPicker)
} }
}
} }
@Composable @Composable
private fun DarkModePreferences( fun DarkModePreferences() {
activity: AppCompatActivity val activity = LocalContext.current as Activity
) {
val settingsViewModel = viewModel<SettingsViewModel>() val settingsViewModel = viewModel<SettingsViewModel>()
val isSelected: (Int) -> Boolean = { settingsViewModel.darkTheme.value == it } val isSelected: (Int) -> Boolean = { settingsViewModel.darkTheme.value == it }
@@ -332,6 +319,12 @@ private fun DarkModePreferences(
activity.recreate() activity.recreate()
} }
Column(
modifier = Modifier
.fillMaxSize()
.padding(all = 24.dp),
verticalArrangement = Arrangement.spacedBy(24.dp)
) {
PreferenceHeading(text = stringResource(R.string.preference_dark_mode_automatic_heading)) PreferenceHeading(text = stringResource(R.string.preference_dark_mode_automatic_heading))
RadioButtonPreference( RadioButtonPreference(
selected = isSelected(AppPreferences.DarkMode.Automatic.ordinal), selected = isSelected(AppPreferences.DarkMode.Automatic.ordinal),
@@ -359,12 +352,19 @@ private fun DarkModePreferences(
onChangeState(AppPreferences.DarkMode.Dark.ordinal) onChangeState(AppPreferences.DarkMode.Dark.ordinal)
} }
) )
}
} }
@Composable @Composable
private fun HomeScreenPreferences() { fun HomeScreenPreferences() {
val settingsViewModel = viewModel<SettingsViewModel>() val settingsViewModel = viewModel<SettingsViewModel>()
Column(
modifier = Modifier
.fillMaxSize()
.padding(all = 24.dp),
verticalArrangement = Arrangement.spacedBy(24.dp)
) {
PreferenceHeading(text = stringResource(R.string.preference_look_and_feel_heading)) PreferenceHeading(text = stringResource(R.string.preference_look_and_feel_heading))
SwitchPreference( SwitchPreference(
@@ -387,7 +387,8 @@ private fun HomeScreenPreferences() {
PreferenceHeading(text = stringResource(R.string.preference_home_tab_order_heading)) PreferenceHeading(text = stringResource(R.string.preference_home_tab_order_heading))
Box(modifier = Modifier Box(
modifier = Modifier
.border( .border(
width = 1.dp, width = 1.dp,
color = MaterialTheme.colorScheme.primary, color = MaterialTheme.colorScheme.primary,
@@ -410,16 +411,44 @@ private fun HomeScreenPreferences() {
update = { } update = { }
) )
} }
}
} }
@Composable @Composable
private fun DevPreferences() { fun SpecialFeaturePreferences() {
val settingsViewModel = viewModel<SettingsViewModel>()
Column(
modifier = Modifier
.fillMaxSize()
.padding(all = 24.dp),
verticalArrangement = Arrangement.spacedBy(24.dp)
) {
SwitchPreference(
titleText = stringResource(R.string.preferences_show_next_mcu_title),
subtitleText = stringResource(R.string.preferences_show_next_mcu_subtitle),
checkState = settingsViewModel.showNextMcuProduction.collectAsState(),
onCheckedChange = {
settingsViewModel.toggleShowNextMcuProduction()
}
)
}
}
@Composable
fun DevPreferences() {
if (BuildConfig.DEBUG) { if (BuildConfig.DEBUG) {
val settingsViewModel = viewModel<SettingsViewModel>() val settingsViewModel = viewModel<SettingsViewModel>()
val context = LocalContext.current val context = LocalContext.current
val coroutineScope = rememberCoroutineScope() val coroutineScope = rememberCoroutineScope()
Column(
modifier = Modifier
.fillMaxSize()
.padding(all = 24.dp),
verticalArrangement = Arrangement.spacedBy(24.dp)
) {
SwitchPreference( SwitchPreference(
titleText = "Show backdrop gallery", titleText = "Show backdrop gallery",
subtitleText = "Show galleries for movies/tv backdrops", subtitleText = "Show galleries for movies/tv backdrops",
@@ -488,6 +517,7 @@ private fun DevPreferences() {
) )
) )
} }
}
} }
@Composable @Composable
@@ -570,151 +600,128 @@ private fun WallpaperPicker(
} }
} }
private fun resetAllPreferences( @Composable
preferences: AppPreferences, private fun TopLevelSettingsCard(
settingsViewModel: SettingsViewModel, title: String,
activity: AppCompatActivity subtitle: String? = null,
icon: ImageVector,
route: String,
settingsNavController: NavController
) { ) {
resetSearchPreferences(preferences = preferences, settingsViewModel = settingsViewModel, activity = activity, skipRecreate = true) Row(
resetDesignPreferences(preferences = preferences, settingsViewModel = settingsViewModel, activity = activity, skipRecreate = true) modifier = Modifier
resetHomeScreenPreferences(preferences = preferences, settingsViewModel = settingsViewModel, activity = activity, skipRecreate = true) .fillMaxWidth()
resetDevModePreference(preferences = preferences, settingsViewModel = settingsViewModel, activity = activity, skipRecreate = true) .wrapContentHeight()
activity.recreate() .clickable(
Toast.makeText(activity, "All settings reset successfully", Toast.LENGTH_SHORT).show() onClick = {
} settingsNavController.navigate(route)
private fun resetSearchPreferences(
preferences: AppPreferences,
settingsViewModel: SettingsViewModel,
activity: AppCompatActivity,
skipRecreate: Boolean = false
) {
settingsViewModel.setShowSearchBar(preferences.showSearchBarDefault)
settingsViewModel.setMultiSearch(preferences.multiSearchDefault)
if (!skipRecreate) {
activity.recreate()
} }
}
private fun resetDesignPreferences(
preferences: AppPreferences,
settingsViewModel: SettingsViewModel,
activity: AppCompatActivity,
skipRecreate: Boolean = false
) {
settingsViewModel.setUseSystemColors(preferences.useSystemColorsDefault)
settingsViewModel.setUseWallpaperColors(preferences.useWallpaperColorsDefault)
settingsViewModel.setChromaMultiplier(preferences.chromaMultiplierDefault)
settingsViewModel.setSelectedColor(preferences.selectedColorDefault)
resetDarkModePreferences(preferences = preferences, settingsViewModel = settingsViewModel, activity = activity, skipRecreate = true)
if (!skipRecreate) {
activity.recreate()
}
}
private fun resetDarkModePreferences(
preferences: AppPreferences,
settingsViewModel: SettingsViewModel,
activity: AppCompatActivity,
skipRecreate: Boolean = false
) {
settingsViewModel.setDarkMode(preferences.darkThemeDefault)
if (!skipRecreate) {
activity.recreate()
}
}
private fun resetDevModePreference(
preferences: AppPreferences,
settingsViewModel: SettingsViewModel,
activity: AppCompatActivity,
skipRecreate: Boolean = false
) {
settingsViewModel.setFirstLaunchTesting(preferences.firstLaunchTestingDefault)
settingsViewModel.setShowBackdropGallery(preferences.showBackdropGalleryDefault)
if (!skipRecreate) {
activity.recreate()
}
}
private fun resetHomeScreenPreferences(
preferences: AppPreferences,
settingsViewModel: SettingsViewModel,
activity: AppCompatActivity,
skipRecreate: Boolean = false
) {
settingsViewModel.setShowBottomTabLabels(preferences.showBottomTabLabelsDefault)
settingsViewModel.setShowPosterTitles(preferences.showPosterTitlesDefault)
preferences.moviesTabPosition = preferences.moviesTabPositionDefault
preferences.tvTabPosition = preferences.tvTabPositionDefault
preferences.peopleTabPosition = preferences.peopleTabPositionDefault
preferences.accountTabPosition = preferences.accountTabPositionDefault
preferences.showBottomTabLabels = preferences.showBottomTabLabelsDefault
if (!skipRecreate) {
activity.recreate()
}
}
private typealias SettingsResetHandler = (AppPreferences, SettingsViewModel, AppCompatActivity) -> Unit
private typealias SettingsPageRenderer = @Composable (NavController, AppCompatActivity) -> Unit
private sealed class SettingsPage(
stringRes: Int,
val route: String,
val settingsPageRenderer: SettingsPageRenderer,
val resetPreferencesHandler: SettingsResetHandler
): KoinComponent {
private val resources: ResourceUtils by inject()
val name = resources.getString(stringRes)
companion object {
val Pages by lazy {
listOf(
SearchSettings,
DesignSettings,
DeveloperSettings,
DarkModeSettings,
HomeScreenSettings
) )
} ) {
Icon(
fun getByRoute(route: String): SettingsPage { imageVector = icon,
return Pages.map { it.route to it } contentDescription = subtitle,
.find { it.first == route } modifier = Modifier
?.second .align(Alignment.CenterVertically)
?: throw IllegalArgumentException("Invalid settings page route: $route") .padding(end = 16.dp),
tint = MaterialTheme.colorScheme.primary
)
Column(
modifier = Modifier
.align(Alignment.CenterVertically)
.weight(1f)
) {
val titleColor = MaterialTheme.colorScheme.onBackground
val subtitleColor = MaterialTheme.colorScheme.onSurfaceVariant
Text(text = title, style = MaterialTheme.typography.titleLarge, color = titleColor, fontSize = 20.sp)
subtitle?.let {
Text(text = subtitle, style = MaterialTheme.typography.bodyMedium, color = subtitleColor)
}
} }
} }
object SearchSettings: SettingsPage(
R.string.preference_heading_search,
"search",
@Composable { _, _ -> SearchPreferences() },
::resetSearchPreferences
)
object DesignSettings: SettingsPage(
R.string.preference_heading_design,
"design",
@Composable { n, a -> DesignPreferences(n, a) },
::resetDesignPreferences
)
object HomeScreenSettings: SettingsPage(
R.string.preference_heading_home_screen,
"home",
@Composable { _, _ -> HomeScreenPreferences() },
::resetHomeScreenPreferences
)
object DeveloperSettings: SettingsPage(
R.string.preferences_debug_title,
"dev",
@Composable { _, _ -> DevPreferences() },
::resetDevModePreference
)
object DarkModeSettings: SettingsPage(
R.string.preference_heading_dark_mode,
"darkmode",
@Composable { _, a -> DarkModePreferences(a) },
::resetDarkModePreferences
)
} }
//private fun resetAllPreferences(
// preferences: AppPreferences,
// settingsViewModel: SettingsViewModel,
// activity: AppCompatActivity
//) {
// resetSearchPreferences(preferences = preferences, settingsViewModel = settingsViewModel, activity = activity, skipRecreate = true)
// resetDesignPreferences(preferences = preferences, settingsViewModel = settingsViewModel, activity = activity, skipRecreate = true)
// resetHomeScreenPreferences(preferences = preferences, settingsViewModel = settingsViewModel, activity = activity, skipRecreate = true)
// resetDevModePreference(preferences = preferences, settingsViewModel = settingsViewModel, activity = activity, skipRecreate = true)
// activity.recreate()
// Toast.makeText(activity, "All settings reset successfully", Toast.LENGTH_SHORT).show()
//}
//
//private fun resetSearchPreferences(
// preferences: AppPreferences,
// settingsViewModel: SettingsViewModel,
// activity: AppCompatActivity,
// skipRecreate: Boolean = false
//) {
// settingsViewModel.setShowSearchBar(preferences.showSearchBarDefault)
// settingsViewModel.setMultiSearch(preferences.multiSearchDefault)
// if (!skipRecreate) {
// activity.recreate()
// }
//}
//
//private fun resetDesignPreferences(
// preferences: AppPreferences,
// settingsViewModel: SettingsViewModel,
// activity: AppCompatActivity,
// skipRecreate: Boolean = false
//) {
// settingsViewModel.setUseSystemColors(preferences.useSystemColorsDefault)
// settingsViewModel.setUseWallpaperColors(preferences.useWallpaperColorsDefault)
// settingsViewModel.setChromaMultiplier(preferences.chromaMultiplierDefault)
// settingsViewModel.setSelectedColor(preferences.selectedColorDefault)
// resetDarkModePreferences(preferences = preferences, settingsViewModel = settingsViewModel, activity = activity, skipRecreate = true)
// if (!skipRecreate) {
// activity.recreate()
// }
//}
//
//private fun resetDarkModePreferences(
// preferences: AppPreferences,
// settingsViewModel: SettingsViewModel,
// activity: AppCompatActivity,
// skipRecreate: Boolean = false
//) {
// settingsViewModel.setDarkMode(preferences.darkThemeDefault)
// if (!skipRecreate) {
// activity.recreate()
// }
//}
//
//private fun resetDevModePreference(
// preferences: AppPreferences,
// settingsViewModel: SettingsViewModel,
// activity: AppCompatActivity,
// skipRecreate: Boolean = false
//) {
// settingsViewModel.setFirstLaunchTesting(preferences.firstLaunchTestingDefault)
// settingsViewModel.setShowBackdropGallery(preferences.showBackdropGalleryDefault)
// if (!skipRecreate) {
// activity.recreate()
// }
//}
//
//private fun resetHomeScreenPreferences(
// preferences: AppPreferences,
// settingsViewModel: SettingsViewModel,
// activity: AppCompatActivity,
// skipRecreate: Boolean = false
//) {
// settingsViewModel.setShowBottomTabLabels(preferences.showBottomTabLabelsDefault)
// settingsViewModel.setShowPosterTitles(preferences.showPosterTitlesDefault)
// preferences.moviesTabPosition = preferences.moviesTabPositionDefault
// preferences.tvTabPosition = preferences.tvTabPositionDefault
// preferences.peopleTabPosition = preferences.peopleTabPositionDefault
// preferences.accountTabPosition = preferences.accountTabPositionDefault
// preferences.showBottomTabLabels = preferences.showBottomTabLabelsDefault
// if (!skipRecreate) {
// activity.recreate()
// }
//}

View File

@@ -23,6 +23,7 @@ class SettingsViewModel: ViewModel() {
private val _showPosterTitles = MutableStateFlow(preferences.showPosterTitles) private val _showPosterTitles = MutableStateFlow(preferences.showPosterTitles)
private val _firstLaunchTesting = MutableStateFlow(preferences.firstLaunchTesting) private val _firstLaunchTesting = MutableStateFlow(preferences.firstLaunchTesting)
private val _showBackdropGallery = MutableStateFlow(preferences.showBackdropGallery) private val _showBackdropGallery = MutableStateFlow(preferences.showBackdropGallery)
private val _showNextMcuProduction = MutableStateFlow(preferences.showNextMcuProduction)
} }
val showSearchBar = _showSearchBar.asStateFlow() val showSearchBar = _showSearchBar.asStateFlow()
@@ -36,6 +37,7 @@ class SettingsViewModel: ViewModel() {
val showPosterTitles = _showPosterTitles.asStateFlow() val showPosterTitles = _showPosterTitles.asStateFlow()
val firstLaunchTesting = _firstLaunchTesting.asStateFlow() val firstLaunchTesting = _firstLaunchTesting.asStateFlow()
val showBackdropGallery = _showBackdropGallery.asStateFlow() val showBackdropGallery = _showBackdropGallery.asStateFlow()
val showNextMcuProduction = _showNextMcuProduction.asStateFlow()
fun toggleShowSearchBar() { fun toggleShowSearchBar() {
_showSearchBar.value = _showSearchBar.value.not() _showSearchBar.value = _showSearchBar.value.not()
@@ -132,4 +134,14 @@ class SettingsViewModel: ViewModel() {
preferences.showBackdropGallery = value preferences.showBackdropGallery = value
} }
fun toggleShowNextMcuProduction() {
_showNextMcuProduction.value = _showNextMcuProduction.value.not()
preferences.showNextMcuProduction = _showNextMcuProduction.value
}
fun setShowNextMcuProduction(value: Boolean) {
_showNextMcuProduction.value = value
preferences.showNextMcuProduction = value
}
} }

View File

@@ -84,6 +84,10 @@
<string name="preference_show_text_labels_title">Show tab text labels</string> <string name="preference_show_text_labels_title">Show tab text labels</string>
<string name="preference_show_text_labels_subtitle">Show text labels for tab items in the bottom tab bar or navigation rail</string> <string name="preference_show_text_labels_subtitle">Show text labels for tab items in the bottom tab bar or navigation rail</string>
<string name="preference_home_tab_order_heading">Home Tab Order</string> <string name="preference_home_tab_order_heading">Home Tab Order</string>
<string name="preference_heading_special_features">Special Features</string>
<string name="preference_subtitle_special_features">Extra features and hidden goodies</string>
<string name="preferences_show_next_mcu_title">Show Next MCU Production</string>
<string name="preferences_show_next_mcu_subtitle">Show a card with information on the next scheduled MCU production when viewing details for an MCU release</string>
<!-- video type --> <!-- video type -->
<string name="video_type_clip">Clips</string> <string name="video_type_clip">Clips</string>