mirror of
https://github.com/owenlejeune/TVTime.git
synced 2026-01-03 21:31:18 -05:00
refactor navigation
This commit is contained in:
@@ -1,59 +1,25 @@
|
|||||||
package com.owenlejeune.tvtime
|
package com.owenlejeune.tvtime
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.activity.compose.BackHandler
|
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.compose.animation.rememberSplineBasedDecay
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.runtime.DisposableEffect
|
||||||
import androidx.compose.material.Scaffold
|
|
||||||
import androidx.compose.material3.*
|
|
||||||
import androidx.compose.runtime.*
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.draw.clip
|
|
||||||
import androidx.compose.ui.graphics.Color
|
|
||||||
import androidx.compose.ui.graphics.compositeOver
|
|
||||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.platform.LocalFocusManager
|
import androidx.compose.ui.platform.LocalFocusManager
|
||||||
import androidx.compose.ui.res.painterResource
|
|
||||||
import androidx.compose.ui.res.stringResource
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.navigation.NavController
|
|
||||||
import androidx.navigation.NavHostController
|
|
||||||
import androidx.navigation.NavType
|
|
||||||
import androidx.navigation.compose.NavHost
|
|
||||||
import androidx.navigation.compose.composable
|
|
||||||
import androidx.navigation.compose.currentBackStackEntryAsState
|
|
||||||
import androidx.navigation.compose.rememberNavController
|
import androidx.navigation.compose.rememberNavController
|
||||||
import androidx.navigation.navArgument
|
|
||||||
import androidx.navigation.navDeepLink
|
|
||||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
|
||||||
import com.kieronquinn.monetcompat.app.MonetCompatActivity
|
import com.kieronquinn.monetcompat.app.MonetCompatActivity
|
||||||
import com.owenlejeune.tvtime.extensions.WindowSizeClass
|
|
||||||
import com.owenlejeune.tvtime.extensions.navigateInBottomBar
|
|
||||||
import com.owenlejeune.tvtime.extensions.rememberWindowSizeClass
|
import com.owenlejeune.tvtime.extensions.rememberWindowSizeClass
|
||||||
import com.owenlejeune.tvtime.preferences.AppPreferences
|
import com.owenlejeune.tvtime.ui.navigation.AppNavigationHost
|
||||||
import com.owenlejeune.tvtime.ui.components.AccountIcon
|
import com.owenlejeune.tvtime.ui.navigation.HomeScreenNavItem
|
||||||
import com.owenlejeune.tvtime.ui.components.ProfileMenuContainer
|
|
||||||
import com.owenlejeune.tvtime.ui.components.ProfileMenuDefaults
|
|
||||||
import com.owenlejeune.tvtime.ui.components.ProfileMenuOverlay
|
|
||||||
import com.owenlejeune.tvtime.ui.navigation.BottomNavItem
|
|
||||||
import com.owenlejeune.tvtime.ui.navigation.MainNavGraph
|
|
||||||
import com.owenlejeune.tvtime.ui.navigation.MainNavItem
|
|
||||||
import com.owenlejeune.tvtime.ui.screens.SearchScreen
|
|
||||||
import com.owenlejeune.tvtime.ui.screens.main.*
|
|
||||||
import com.owenlejeune.tvtime.ui.theme.TVTimeTheme
|
import com.owenlejeune.tvtime.ui.theme.TVTimeTheme
|
||||||
import com.owenlejeune.tvtime.utils.KeyboardManager
|
import com.owenlejeune.tvtime.utils.KeyboardManager
|
||||||
import com.owenlejeune.tvtime.utils.NavConstants
|
|
||||||
import com.owenlejeune.tvtime.utils.SessionManager
|
import com.owenlejeune.tvtime.utils.SessionManager
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.koin.java.KoinJavaComponent.get
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
|
||||||
class MainActivity : MonetCompatActivity() {
|
class MainActivity : MonetCompatActivity() {
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
@@ -63,7 +29,7 @@ class MainActivity : MonetCompatActivity() {
|
|||||||
SessionManager.initialize()
|
SessionManager.initialize()
|
||||||
}
|
}
|
||||||
|
|
||||||
val mainNavStartRoute = BottomNavItem.SortedItems[0].route
|
val mainNavStartRoute = HomeScreenNavItem.SortedItems[0].route
|
||||||
|
|
||||||
lifecycleScope.launchWhenCreated {
|
lifecycleScope.launchWhenCreated {
|
||||||
monet.awaitMonetReady()
|
monet.awaitMonetReady()
|
||||||
@@ -73,7 +39,7 @@ class MainActivity : MonetCompatActivity() {
|
|||||||
val windowSize = rememberWindowSizeClass()
|
val windowSize = rememberWindowSizeClass()
|
||||||
val appNavController = rememberNavController()
|
val appNavController = rememberNavController()
|
||||||
Box {
|
Box {
|
||||||
MainNavigationRoutes(
|
AppNavigationHost(
|
||||||
appNavController = appNavController,
|
appNavController = appNavController,
|
||||||
mainNavStartRoute = mainNavStartRoute,
|
mainNavStartRoute = mainNavStartRoute,
|
||||||
windowSize = windowSize
|
windowSize = windowSize
|
||||||
@@ -84,394 +50,6 @@ class MainActivity : MonetCompatActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun AppScaffold(
|
|
||||||
appNavController: NavHostController,
|
|
||||||
mainNavStartRoute: String = BottomNavItem.SortedItems[0].route,
|
|
||||||
windowSize: WindowSizeClass,
|
|
||||||
preferences: AppPreferences = get(AppPreferences::class.java)
|
|
||||||
) {
|
|
||||||
val navController = rememberNavController()
|
|
||||||
|
|
||||||
val decayAnimationSpec = rememberSplineBasedDecay<Float>()
|
|
||||||
val topAppBarScrollState = rememberTopAppBarScrollState()
|
|
||||||
val scrollBehavior = remember(decayAnimationSpec) {
|
|
||||||
TopAppBarDefaults.exitUntilCollapsedScrollBehavior(decayAnimationSpec, topAppBarScrollState)
|
|
||||||
}
|
|
||||||
|
|
||||||
val appBarTitle = remember { mutableStateOf<@Composable () -> Unit>({}) }
|
|
||||||
val appBarActions = remember { mutableStateOf<@Composable RowScope.() -> Unit>({}) }
|
|
||||||
val fab = remember { mutableStateOf<@Composable () -> Unit>({}) }
|
|
||||||
|
|
||||||
val showProfileMenuOverlay = remember { mutableStateOf(false) }
|
|
||||||
val navigationIcon = @Composable {
|
|
||||||
AccountIcon(
|
|
||||||
modifier = Modifier.padding(horizontal = 12.dp),
|
|
||||||
size = 32.dp,
|
|
||||||
onClick = { showProfileMenuOverlay.value = true }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
val defaultNavBarColor = MaterialTheme.colorScheme.primary.copy(alpha = 0.08f).compositeOver(background = MaterialTheme.colorScheme.surface)
|
|
||||||
|
|
||||||
ProfileMenuContainer(
|
|
||||||
appNavController = appNavController,
|
|
||||||
visible = showProfileMenuOverlay.value,
|
|
||||||
onDismissRequest = { showProfileMenuOverlay.value = false },
|
|
||||||
colors = ProfileMenuDefaults.systemBarColors(navBarColor = defaultNavBarColor)
|
|
||||||
) {
|
|
||||||
Scaffold(
|
|
||||||
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
|
||||||
topBar = {
|
|
||||||
if (windowSize != WindowSizeClass.Expanded) {
|
|
||||||
TopBar(
|
|
||||||
appNavController = appNavController,
|
|
||||||
title = appBarTitle.value,
|
|
||||||
scrollBehavior = scrollBehavior,
|
|
||||||
appBarActions = appBarActions,
|
|
||||||
navigationIcon = navigationIcon
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
floatingActionButton = {
|
|
||||||
fab.value()
|
|
||||||
},
|
|
||||||
bottomBar = {
|
|
||||||
if (windowSize != WindowSizeClass.Expanded) {
|
|
||||||
BottomNavBar(navController = navController)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
) { innerPadding ->
|
|
||||||
Box(modifier = Modifier.padding(innerPadding)) {
|
|
||||||
MainContent(
|
|
||||||
windowSize = windowSize,
|
|
||||||
appNavController = appNavController,
|
|
||||||
navController = navController,
|
|
||||||
fab = fab,
|
|
||||||
appBarTitle = appBarTitle,
|
|
||||||
appBarActions = appBarActions,
|
|
||||||
topBarScrollBehaviour = scrollBehavior,
|
|
||||||
mainNavStartRoute = mainNavStartRoute,
|
|
||||||
navigationIcon = navigationIcon
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun TopBar(
|
|
||||||
appNavController: NavHostController,
|
|
||||||
title: @Composable () -> Unit,
|
|
||||||
scrollBehavior: TopAppBarScrollBehavior,
|
|
||||||
appBarActions: MutableState<@Composable (RowScope.() -> Unit)> = mutableStateOf({}),
|
|
||||||
navigationIcon: @Composable () -> Unit = {}
|
|
||||||
) {
|
|
||||||
LargeTopAppBar(
|
|
||||||
title = title,
|
|
||||||
scrollBehavior = scrollBehavior,
|
|
||||||
colors = TopAppBarDefaults
|
|
||||||
.largeTopAppBarColors(
|
|
||||||
scrolledContainerColor = MaterialTheme.colorScheme.background
|
|
||||||
),
|
|
||||||
actions = {
|
|
||||||
appBarActions.value(this)
|
|
||||||
},
|
|
||||||
navigationIcon = navigationIcon
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun BottomNavBar(
|
|
||||||
navController: NavController,
|
|
||||||
preferences: AppPreferences = get(AppPreferences::class.java)
|
|
||||||
) {
|
|
||||||
val navBackStackEntry by navController.currentBackStackEntryAsState()
|
|
||||||
val currentRoute = navBackStackEntry?.destination?.route
|
|
||||||
|
|
||||||
NavigationBar {
|
|
||||||
BottomNavItem.SortedItems.forEach { item ->
|
|
||||||
val isSelected = currentRoute == item.route
|
|
||||||
NavigationBarItem(
|
|
||||||
modifier = Modifier
|
|
||||||
.padding(4.dp)
|
|
||||||
.clip(RoundedCornerShape(24.dp)),
|
|
||||||
icon = { Icon(painter = painterResource(id = item.icon), contentDescription = null) },
|
|
||||||
label = {
|
|
||||||
val name = if (preferences.showBottomTabLabels) item.name else " "
|
|
||||||
Text(text = name)
|
|
||||||
},
|
|
||||||
selected = isSelected,
|
|
||||||
onClick = {
|
|
||||||
if (!isSelected) {
|
|
||||||
navController.navigateInBottomBar(item.route)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun MainContent(
|
|
||||||
windowSize: WindowSizeClass,
|
|
||||||
appNavController: NavHostController,
|
|
||||||
navController: NavHostController,
|
|
||||||
fab: MutableState<@Composable () -> Unit>,
|
|
||||||
topBarScrollBehaviour: TopAppBarScrollBehavior,
|
|
||||||
appBarTitle: MutableState<@Composable () -> Unit>,
|
|
||||||
appBarActions: MutableState<@Composable (RowScope.() -> Unit)> = mutableStateOf({}),
|
|
||||||
navigationIcon: @Composable () -> Unit = {},
|
|
||||||
mainNavStartRoute: String = BottomNavItem.SortedItems[0].route
|
|
||||||
) {
|
|
||||||
if (windowSize == WindowSizeClass.Expanded) {
|
|
||||||
DualColumnMainContent(
|
|
||||||
appNavController = appNavController,
|
|
||||||
navController = navController,
|
|
||||||
fab = fab,
|
|
||||||
appBarTitle = appBarTitle,
|
|
||||||
appBarActions = appBarActions,
|
|
||||||
topBarScrollBehaviour = topBarScrollBehaviour,
|
|
||||||
mainNavStartRoute = mainNavStartRoute,
|
|
||||||
navigationIcon = navigationIcon
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
SingleColumnMainContent(
|
|
||||||
appNavController = appNavController,
|
|
||||||
navController = navController,
|
|
||||||
fab = fab,
|
|
||||||
appBarTitle = appBarTitle,
|
|
||||||
appBarActions = appBarActions,
|
|
||||||
mainNavStartRoute = mainNavStartRoute
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun SingleColumnMainContent(
|
|
||||||
appNavController: NavHostController,
|
|
||||||
navController: NavHostController,
|
|
||||||
fab: MutableState<@Composable () -> Unit>,
|
|
||||||
appBarTitle: MutableState<@Composable () -> Unit>,
|
|
||||||
appBarActions: MutableState<@Composable (RowScope.() -> Unit)> = mutableStateOf({}),
|
|
||||||
mainNavStartRoute: String = BottomNavItem.SortedItems[0].route
|
|
||||||
) {
|
|
||||||
MainMediaView(
|
|
||||||
appNavController = appNavController,
|
|
||||||
navController = navController,
|
|
||||||
fab = fab,
|
|
||||||
appBarTitle = appBarTitle,
|
|
||||||
appBarActions = appBarActions,
|
|
||||||
mainNavStartRoute = mainNavStartRoute
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun DualColumnMainContent(
|
|
||||||
appNavController: NavHostController,
|
|
||||||
navController: NavHostController,
|
|
||||||
fab: MutableState<@Composable () -> Unit>,
|
|
||||||
topBarScrollBehaviour: TopAppBarScrollBehavior,
|
|
||||||
appBarTitle: MutableState<@Composable () -> Unit>,
|
|
||||||
appBarActions: MutableState<@Composable (RowScope.() -> Unit)> = mutableStateOf({}),
|
|
||||||
navigationIcon: @Composable () -> Unit = {},
|
|
||||||
mainNavStartRoute: String = BottomNavItem.SortedItems[0].route,
|
|
||||||
preferences: AppPreferences = get(AppPreferences::class.java)
|
|
||||||
) {
|
|
||||||
val navBackStackEntry by navController.currentBackStackEntryAsState()
|
|
||||||
val currentRoute = navBackStackEntry?.destination?.route
|
|
||||||
|
|
||||||
Row(modifier = Modifier.fillMaxSize()) {
|
|
||||||
NavigationRail {
|
|
||||||
Spacer(modifier = Modifier.weight(1f))
|
|
||||||
BottomNavItem.SortedItems.forEachIndexed { index, item ->
|
|
||||||
val isSelected = currentRoute == item.route
|
|
||||||
NavigationRailItem(
|
|
||||||
icon = { Icon(painter = painterResource(id = item.icon), contentDescription = null) },
|
|
||||||
label = { if (preferences.showBottomTabLabels) Text(item.name) },
|
|
||||||
selected = isSelected,
|
|
||||||
onClick = {
|
|
||||||
if (!isSelected) {
|
|
||||||
navController.navigateInBottomBar(item.route)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
if (index < BottomNavItem.SortedItems.size - 1) {
|
|
||||||
Spacer(modifier = Modifier.height(20.dp))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Spacer(modifier = Modifier.weight(1f))
|
|
||||||
}
|
|
||||||
Column {
|
|
||||||
TopBar(
|
|
||||||
appNavController = appNavController,
|
|
||||||
title = appBarTitle.value,
|
|
||||||
scrollBehavior = topBarScrollBehaviour,
|
|
||||||
appBarActions = appBarActions,
|
|
||||||
navigationIcon = navigationIcon
|
|
||||||
)
|
|
||||||
MainMediaView(
|
|
||||||
appNavController = appNavController,
|
|
||||||
navController = navController,
|
|
||||||
fab = fab,
|
|
||||||
appBarTitle = appBarTitle,
|
|
||||||
appBarActions = appBarActions,
|
|
||||||
mainNavStartRoute = mainNavStartRoute
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun MainMediaView(
|
|
||||||
appNavController: NavHostController,
|
|
||||||
navController: NavHostController,
|
|
||||||
fab: MutableState<@Composable () -> Unit>,
|
|
||||||
appBarTitle: MutableState<@Composable () -> Unit>,
|
|
||||||
appBarActions: MutableState<RowScope.() -> Unit> = mutableStateOf({}),
|
|
||||||
mainNavStartRoute: String = BottomNavItem.SortedItems[0].route
|
|
||||||
) {
|
|
||||||
Column {
|
|
||||||
BackHandler(enabled = true) {
|
|
||||||
finish()
|
|
||||||
}
|
|
||||||
|
|
||||||
MainNavGraph(
|
|
||||||
activity = this@MainActivity,
|
|
||||||
appNavController = appNavController,
|
|
||||||
navController = navController,
|
|
||||||
fab = fab,
|
|
||||||
appBarTitle = appBarTitle,
|
|
||||||
appBarActions = appBarActions,
|
|
||||||
startDestination = mainNavStartRoute
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun MainNavigationRoutes(
|
|
||||||
startDestination: String = MainNavItem.MainView.route,
|
|
||||||
mainNavStartRoute: String = MainNavItem.Items[0].route,
|
|
||||||
appNavController: NavHostController,
|
|
||||||
windowSize: WindowSizeClass,
|
|
||||||
preferences: AppPreferences = get(AppPreferences::class.java)
|
|
||||||
) {
|
|
||||||
NavHost(navController = appNavController, startDestination = startDestination) {
|
|
||||||
composable(MainNavItem.MainView.route) {
|
|
||||||
AppScaffold(
|
|
||||||
appNavController = appNavController,
|
|
||||||
mainNavStartRoute = mainNavStartRoute,
|
|
||||||
windowSize = windowSize
|
|
||||||
)
|
|
||||||
}
|
|
||||||
composable(
|
|
||||||
MainNavItem.DetailView.route.plus("/{${NavConstants.TYPE_KEY}}/{${NavConstants.ID_KEY}}"),
|
|
||||||
arguments = listOf(
|
|
||||||
navArgument(NavConstants.ID_KEY) { type = NavType.IntType },
|
|
||||||
navArgument(NavConstants.TYPE_KEY) { type = NavType.EnumType(MediaViewType::class.java) }
|
|
||||||
)
|
|
||||||
) { navBackStackEntry ->
|
|
||||||
val args = navBackStackEntry.arguments
|
|
||||||
val mediaType = args?.getSerializable(NavConstants.TYPE_KEY) as MediaViewType
|
|
||||||
|
|
||||||
when (mediaType) {
|
|
||||||
MediaViewType.PERSON -> {
|
|
||||||
PersonDetailView(
|
|
||||||
appNavController = appNavController,
|
|
||||||
personId = args.getInt(NavConstants.ID_KEY)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
MediaViewType.LIST -> {
|
|
||||||
ListDetailView(
|
|
||||||
appNavController = appNavController,
|
|
||||||
itemId = args.getInt(NavConstants.ID_KEY),
|
|
||||||
windowSize = windowSize
|
|
||||||
)
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
MediaDetailView(
|
|
||||||
appNavController = appNavController,
|
|
||||||
itemId = args.getInt(NavConstants.ID_KEY),
|
|
||||||
type = mediaType,
|
|
||||||
windowSize = windowSize
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
composable(
|
|
||||||
MainNavItem.SettingsView.route.plus("/{${NavConstants.SETTINGS_KEY}}"),
|
|
||||||
arguments = listOf(
|
|
||||||
navArgument(NavConstants.SETTINGS_KEY) { type = NavType.StringType }
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
val route = it.arguments?.getString(NavConstants.SETTINGS_KEY)
|
|
||||||
SettingsTab(
|
|
||||||
appNavController = appNavController,
|
|
||||||
activity = this@MainActivity,
|
|
||||||
route = route
|
|
||||||
)
|
|
||||||
}
|
|
||||||
composable(MainNavItem.SettingsView.route) {
|
|
||||||
SettingsTab(appNavController = appNavController, activity = this@MainActivity)
|
|
||||||
}
|
|
||||||
composable(
|
|
||||||
route = MainNavItem.SearchView.route.plus("/{${NavConstants.SEARCH_ID_KEY}}/{${NavConstants.SEARCH_TITLE_KEY}}"),
|
|
||||||
arguments = listOf(
|
|
||||||
navArgument(NavConstants.SEARCH_ID_KEY) { type = NavType.IntType },
|
|
||||||
navArgument(NavConstants.SEARCH_TITLE_KEY) { type = NavType.StringType }
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
it.arguments?.let { arguments ->
|
|
||||||
val (type, title) = if (preferences.multiSearch) {
|
|
||||||
Pair(
|
|
||||||
MediaViewType.MIXED,
|
|
||||||
stringResource(id = R.string.search_all_title)
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
Pair(
|
|
||||||
MediaViewType[arguments.getInt(NavConstants.SEARCH_ID_KEY)],
|
|
||||||
arguments.getString(NavConstants.SEARCH_TITLE_KEY) ?: ""
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
SearchScreen(
|
|
||||||
appNavController = appNavController,
|
|
||||||
title = title,
|
|
||||||
mediaViewType = type
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
composable(
|
|
||||||
route = MainNavItem.WebLinkView.route.plus("/{${NavConstants.WEB_LINK_KEY}}"),
|
|
||||||
arguments = listOf(
|
|
||||||
navArgument(NavConstants.WEB_LINK_KEY) { type = NavType.StringType }
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
val url = it.arguments?.getString(NavConstants.WEB_LINK_KEY)
|
|
||||||
url?.let {
|
|
||||||
WebLinkView(url = url, appNavController = appNavController)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
composable(
|
|
||||||
route = MainNavItem.AccountView.route,
|
|
||||||
deepLinks = listOf(
|
|
||||||
navDeepLink { uriPattern = "app://tvtime.auth.{${NavConstants.ACCOUNT_KEY}}" }
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
val deepLink = it.arguments?.getString(NavConstants.ACCOUNT_KEY)
|
|
||||||
AccountView(
|
|
||||||
appNavController = appNavController,
|
|
||||||
doSignInPartTwo = deepLink == NavConstants.AUTH_REDIRECT_PAGE
|
|
||||||
)
|
|
||||||
}
|
|
||||||
composable(
|
|
||||||
route = MainNavItem.AboutView.route
|
|
||||||
) {
|
|
||||||
AboutView(appNavController = appNavController)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun AppKeyboardFocusManager() {
|
private fun AppKeyboardFocusManager() {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
package com.owenlejeune.tvtime.api.tmdb.api.v3.deserializer
|
package com.owenlejeune.tvtime.api.tmdb.api.v3.deserializer
|
||||||
|
|
||||||
import com.google.gson.Gson
|
|
||||||
import com.google.gson.JsonObject
|
import com.google.gson.JsonObject
|
||||||
import com.google.gson.JsonParseException
|
import com.google.gson.JsonParseException
|
||||||
import com.owenlejeune.tvtime.api.tmdb.api.BaseDeserializer
|
import com.owenlejeune.tvtime.api.tmdb.api.BaseDeserializer
|
||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.KnownFor
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.KnownFor
|
||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.KnownForMovie
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.KnownForMovie
|
||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.KnownForTv
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.KnownForTv
|
||||||
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
|
import com.owenlejeune.tvtime.utils.types.MediaViewType
|
||||||
|
|
||||||
class KnownForDeserializer: BaseDeserializer<KnownFor>() {
|
class KnownForDeserializer: BaseDeserializer<KnownFor>() {
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package com.owenlejeune.tvtime.api.tmdb.api.v3.deserializer
|
package com.owenlejeune.tvtime.api.tmdb.api.v3.deserializer
|
||||||
|
|
||||||
import com.google.gson.Gson
|
|
||||||
import com.google.gson.JsonObject
|
import com.google.gson.JsonObject
|
||||||
import com.google.gson.JsonParseException
|
import com.google.gson.JsonParseException
|
||||||
import com.owenlejeune.tvtime.api.tmdb.api.BaseDeserializer
|
import com.owenlejeune.tvtime.api.tmdb.api.BaseDeserializer
|
||||||
@@ -8,7 +7,7 @@ import com.owenlejeune.tvtime.api.tmdb.api.v3.model.SearchResultMovie
|
|||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.SearchResultPerson
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.SearchResultPerson
|
||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.SearchResultTv
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.SearchResultTv
|
||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.SortableSearchResult
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.SortableSearchResult
|
||||||
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
|
import com.owenlejeune.tvtime.utils.types.MediaViewType
|
||||||
|
|
||||||
class SortableSearchResultDeserializer: BaseDeserializer<SortableSearchResult>() {
|
class SortableSearchResultDeserializer: BaseDeserializer<SortableSearchResult>() {
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.owenlejeune.tvtime.api.tmdb.api.v3.model
|
package com.owenlejeune.tvtime.api.tmdb.api.v3.model
|
||||||
|
|
||||||
import com.google.gson.annotations.SerializedName
|
import com.google.gson.annotations.SerializedName
|
||||||
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
|
import com.owenlejeune.tvtime.utils.types.MediaViewType
|
||||||
|
|
||||||
class DetailCast(
|
class DetailCast(
|
||||||
@SerializedName("id") val id: Int,
|
@SerializedName("id") val id: Int,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.owenlejeune.tvtime.api.tmdb.api.v3.model
|
package com.owenlejeune.tvtime.api.tmdb.api.v3.model
|
||||||
|
|
||||||
import com.google.gson.annotations.SerializedName
|
import com.google.gson.annotations.SerializedName
|
||||||
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
|
import com.owenlejeune.tvtime.utils.types.MediaViewType
|
||||||
|
|
||||||
class DetailCrew(
|
class DetailCrew(
|
||||||
@SerializedName("id") val id: Int,
|
@SerializedName("id") val id: Int,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.owenlejeune.tvtime.api.tmdb.api.v3.model
|
package com.owenlejeune.tvtime.api.tmdb.api.v3.model
|
||||||
|
|
||||||
import com.google.gson.annotations.SerializedName
|
import com.google.gson.annotations.SerializedName
|
||||||
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
|
import com.owenlejeune.tvtime.utils.types.MediaViewType
|
||||||
|
|
||||||
abstract class KnownFor(
|
abstract class KnownFor(
|
||||||
@SerializedName("backdrop_path") val backdropPath: String?,
|
@SerializedName("backdrop_path") val backdropPath: String?,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.owenlejeune.tvtime.api.tmdb.api.v3.model
|
package com.owenlejeune.tvtime.api.tmdb.api.v3.model
|
||||||
|
|
||||||
import com.google.gson.annotations.SerializedName
|
import com.google.gson.annotations.SerializedName
|
||||||
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
|
import com.owenlejeune.tvtime.utils.types.MediaViewType
|
||||||
|
|
||||||
class KnownForMovie(
|
class KnownForMovie(
|
||||||
backdropPath: String?,
|
backdropPath: String?,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.owenlejeune.tvtime.api.tmdb.api.v3.model
|
package com.owenlejeune.tvtime.api.tmdb.api.v3.model
|
||||||
|
|
||||||
import com.google.gson.annotations.SerializedName
|
import com.google.gson.annotations.SerializedName
|
||||||
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
|
import com.owenlejeune.tvtime.utils.types.MediaViewType
|
||||||
|
|
||||||
class KnownForTv(
|
class KnownForTv(
|
||||||
backdropPath: String?,
|
backdropPath: String?,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.owenlejeune.tvtime.api.tmdb.api.v3.model
|
package com.owenlejeune.tvtime.api.tmdb.api.v3.model
|
||||||
|
|
||||||
import com.google.gson.annotations.SerializedName
|
import com.google.gson.annotations.SerializedName
|
||||||
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
|
import com.owenlejeune.tvtime.utils.types.MediaViewType
|
||||||
|
|
||||||
class MarkAsFavoriteBody(
|
class MarkAsFavoriteBody(
|
||||||
@SerializedName("media_type") val mediaType: MediaViewType,
|
@SerializedName("media_type") val mediaType: MediaViewType,
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import androidx.paging.PagingSource
|
|||||||
import androidx.paging.PagingState
|
import androidx.paging.PagingState
|
||||||
import com.owenlejeune.tvtime.api.tmdb.api.v4.AccountV4Service
|
import com.owenlejeune.tvtime.api.tmdb.api.v4.AccountV4Service
|
||||||
import com.owenlejeune.tvtime.preferences.AppPreferences
|
import com.owenlejeune.tvtime.preferences.AppPreferences
|
||||||
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
|
import com.owenlejeune.tvtime.utils.types.MediaViewType
|
||||||
import org.koin.core.component.KoinComponent
|
import org.koin.core.component.KoinComponent
|
||||||
import org.koin.core.component.inject
|
import org.koin.core.component.inject
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.owenlejeune.tvtime.api.tmdb.api.v3.model
|
package com.owenlejeune.tvtime.api.tmdb.api.v3.model
|
||||||
|
|
||||||
import com.google.gson.annotations.SerializedName
|
import com.google.gson.annotations.SerializedName
|
||||||
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
|
import com.owenlejeune.tvtime.utils.types.MediaViewType
|
||||||
|
|
||||||
abstract class SearchResultMedia(
|
abstract class SearchResultMedia(
|
||||||
@SerializedName("overview") val overview: String,
|
@SerializedName("overview") val overview: String,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.owenlejeune.tvtime.api.tmdb.api.v3.model
|
package com.owenlejeune.tvtime.api.tmdb.api.v3.model
|
||||||
|
|
||||||
import com.google.gson.annotations.SerializedName
|
import com.google.gson.annotations.SerializedName
|
||||||
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
|
import com.owenlejeune.tvtime.utils.types.MediaViewType
|
||||||
|
|
||||||
class SearchResultMovie(
|
class SearchResultMovie(
|
||||||
id: Int,
|
id: Int,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.owenlejeune.tvtime.api.tmdb.api.v3.model
|
package com.owenlejeune.tvtime.api.tmdb.api.v3.model
|
||||||
|
|
||||||
import com.google.gson.annotations.SerializedName
|
import com.google.gson.annotations.SerializedName
|
||||||
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
|
import com.owenlejeune.tvtime.utils.types.MediaViewType
|
||||||
|
|
||||||
class SearchResultPerson(
|
class SearchResultPerson(
|
||||||
@SerializedName("profile_path") val profilePath: String,
|
@SerializedName("profile_path") val profilePath: String,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.owenlejeune.tvtime.api.tmdb.api.v3.model
|
package com.owenlejeune.tvtime.api.tmdb.api.v3.model
|
||||||
|
|
||||||
import com.google.gson.annotations.SerializedName
|
import com.google.gson.annotations.SerializedName
|
||||||
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
|
import com.owenlejeune.tvtime.utils.types.MediaViewType
|
||||||
|
|
||||||
class SearchResultTv(
|
class SearchResultTv(
|
||||||
id: Int,
|
id: Int,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.owenlejeune.tvtime.api.tmdb.api.v3.model
|
package com.owenlejeune.tvtime.api.tmdb.api.v3.model
|
||||||
|
|
||||||
import com.google.gson.annotations.SerializedName
|
import com.google.gson.annotations.SerializedName
|
||||||
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
|
import com.owenlejeune.tvtime.utils.types.MediaViewType
|
||||||
|
|
||||||
abstract class SortableSearchResult(
|
abstract class SortableSearchResult(
|
||||||
@SerializedName("media_type") val mediaType: MediaViewType,
|
@SerializedName("media_type") val mediaType: MediaViewType,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.owenlejeune.tvtime.api.tmdb.api.v3.model
|
package com.owenlejeune.tvtime.api.tmdb.api.v3.model
|
||||||
|
|
||||||
import com.google.gson.annotations.SerializedName
|
import com.google.gson.annotations.SerializedName
|
||||||
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
|
import com.owenlejeune.tvtime.utils.types.MediaViewType
|
||||||
|
|
||||||
class WatchlistBody(
|
class WatchlistBody(
|
||||||
@SerializedName("media_type") val mediaType: MediaViewType,
|
@SerializedName("media_type") val mediaType: MediaViewType,
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import com.owenlejeune.tvtime.api.tmdb.api.BaseDeserializer
|
|||||||
import com.owenlejeune.tvtime.api.tmdb.api.v4.model.ListItem
|
import com.owenlejeune.tvtime.api.tmdb.api.v4.model.ListItem
|
||||||
import com.owenlejeune.tvtime.api.tmdb.api.v4.model.ListMovie
|
import com.owenlejeune.tvtime.api.tmdb.api.v4.model.ListMovie
|
||||||
import com.owenlejeune.tvtime.api.tmdb.api.v4.model.ListTv
|
import com.owenlejeune.tvtime.api.tmdb.api.v4.model.ListTv
|
||||||
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
|
import com.owenlejeune.tvtime.utils.types.MediaViewType
|
||||||
|
|
||||||
class ListItemDeserializer: BaseDeserializer<ListItem>() {
|
class ListItemDeserializer: BaseDeserializer<ListItem>() {
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.owenlejeune.tvtime.api.tmdb.api.v4.model
|
package com.owenlejeune.tvtime.api.tmdb.api.v4.model
|
||||||
|
|
||||||
import com.google.gson.annotations.SerializedName
|
import com.google.gson.annotations.SerializedName
|
||||||
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
|
import com.owenlejeune.tvtime.utils.types.MediaViewType
|
||||||
|
|
||||||
class AddToListBody(
|
class AddToListBody(
|
||||||
@SerializedName("items") val items: List<AddToListBodyItem>
|
@SerializedName("items") val items: List<AddToListBodyItem>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.owenlejeune.tvtime.api.tmdb.api.v4.model
|
package com.owenlejeune.tvtime.api.tmdb.api.v4.model
|
||||||
|
|
||||||
import com.google.gson.annotations.SerializedName
|
import com.google.gson.annotations.SerializedName
|
||||||
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
|
import com.owenlejeune.tvtime.utils.types.MediaViewType
|
||||||
|
|
||||||
class DeleteListItemsBody(
|
class DeleteListItemsBody(
|
||||||
@SerializedName("items") val items: List<DeleteListItemsItem>
|
@SerializedName("items") val items: List<DeleteListItemsItem>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.owenlejeune.tvtime.api.tmdb.api.v4.model
|
package com.owenlejeune.tvtime.api.tmdb.api.v4.model
|
||||||
|
|
||||||
import com.google.gson.annotations.SerializedName
|
import com.google.gson.annotations.SerializedName
|
||||||
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
|
import com.owenlejeune.tvtime.utils.types.MediaViewType
|
||||||
|
|
||||||
abstract class ListItem(
|
abstract class ListItem(
|
||||||
@SerializedName("backdrop_path") val backdropPath: String?,
|
@SerializedName("backdrop_path") val backdropPath: String?,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.owenlejeune.tvtime.api.tmdb.api.v4.model
|
package com.owenlejeune.tvtime.api.tmdb.api.v4.model
|
||||||
|
|
||||||
import com.google.gson.annotations.SerializedName
|
import com.google.gson.annotations.SerializedName
|
||||||
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
|
import com.owenlejeune.tvtime.utils.types.MediaViewType
|
||||||
|
|
||||||
class ListItemStatusResponse(
|
class ListItemStatusResponse(
|
||||||
statusMessage: String,
|
statusMessage: String,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.owenlejeune.tvtime.api.tmdb.api.v4.model
|
package com.owenlejeune.tvtime.api.tmdb.api.v4.model
|
||||||
|
|
||||||
import com.google.gson.annotations.SerializedName
|
import com.google.gson.annotations.SerializedName
|
||||||
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
|
import com.owenlejeune.tvtime.utils.types.MediaViewType
|
||||||
|
|
||||||
class ListMovie(
|
class ListMovie(
|
||||||
backdropPath: String?,
|
backdropPath: String?,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.owenlejeune.tvtime.api.tmdb.api.v4.model
|
package com.owenlejeune.tvtime.api.tmdb.api.v4.model
|
||||||
|
|
||||||
import com.google.gson.annotations.SerializedName
|
import com.google.gson.annotations.SerializedName
|
||||||
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
|
import com.owenlejeune.tvtime.utils.types.MediaViewType
|
||||||
|
|
||||||
class ListTv(
|
class ListTv(
|
||||||
backdropPath: String?,
|
backdropPath: String?,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.owenlejeune.tvtime.api.tmdb.api.v4.model
|
package com.owenlejeune.tvtime.api.tmdb.api.v4.model
|
||||||
|
|
||||||
import com.google.gson.annotations.SerializedName
|
import com.google.gson.annotations.SerializedName
|
||||||
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
|
import com.owenlejeune.tvtime.utils.types.MediaViewType
|
||||||
|
|
||||||
class UpdateListItemBody(
|
class UpdateListItemBody(
|
||||||
@SerializedName("items") val items: List<UpdateListItemBodyItem>
|
@SerializedName("items") val items: List<UpdateListItemBodyItem>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import androidx.paging.PagingData
|
|||||||
import androidx.paging.cachedIn
|
import androidx.paging.cachedIn
|
||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.RecommendedMediaPagingSource
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.RecommendedMediaPagingSource
|
||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.TmdbItem
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.TmdbItem
|
||||||
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
|
import com.owenlejeune.tvtime.utils.types.MediaViewType
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import org.koin.core.component.KoinComponent
|
import org.koin.core.component.KoinComponent
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.owenlejeune.tvtime.ui.screens.main
|
package com.owenlejeune.tvtime.ui.components
|
||||||
|
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
@@ -25,13 +25,9 @@ import com.google.accompanist.pager.PagerState
|
|||||||
import com.google.accompanist.pager.rememberPagerState
|
import com.google.accompanist.pager.rememberPagerState
|
||||||
import com.owenlejeune.tvtime.R
|
import com.owenlejeune.tvtime.R
|
||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.ImageCollection
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.ImageCollection
|
||||||
import com.owenlejeune.tvtime.ui.components.PosterItem
|
|
||||||
import com.owenlejeune.tvtime.ui.components.RatingRing
|
|
||||||
import com.owenlejeune.tvtime.utils.TmdbUtils
|
import com.owenlejeune.tvtime.utils.TmdbUtils
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
|
|
||||||
@OptIn(ExperimentalPagerApi::class)
|
@OptIn(ExperimentalPagerApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.owenlejeune.tvtime.ui.screens.main
|
package com.owenlejeune.tvtime.ui.components
|
||||||
|
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
@@ -12,7 +12,6 @@ import androidx.compose.ui.draw.blur
|
|||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.res.painterResource
|
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
@@ -22,7 +21,8 @@ import androidx.constraintlayout.compose.Dimension
|
|||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import coil.compose.AsyncImage
|
import coil.compose.AsyncImage
|
||||||
import com.owenlejeune.tvtime.R
|
import com.owenlejeune.tvtime.R
|
||||||
import com.owenlejeune.tvtime.ui.navigation.MainNavItem
|
import com.owenlejeune.tvtime.ui.navigation.AppNavItem
|
||||||
|
import com.owenlejeune.tvtime.utils.types.MediaViewType
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
@@ -45,7 +45,7 @@ fun MediaResultCard(
|
|||||||
.clickable(
|
.clickable(
|
||||||
onClick = {
|
onClick = {
|
||||||
appNavController.navigate(
|
appNavController.navigate(
|
||||||
"${MainNavItem.DetailView.route}/${mediaViewType}/${id}"
|
"${AppNavItem.DetailView.route}/${mediaViewType}/${id}"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -39,14 +39,13 @@ import androidx.compose.ui.platform.LocalContext
|
|||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||||
import com.owenlejeune.tvtime.BuildConfig
|
import com.owenlejeune.tvtime.BuildConfig
|
||||||
import com.owenlejeune.tvtime.R
|
import com.owenlejeune.tvtime.R
|
||||||
import com.owenlejeune.tvtime.ui.navigation.MainNavItem
|
import com.owenlejeune.tvtime.ui.navigation.AppNavItem
|
||||||
import com.owenlejeune.tvtime.utils.SessionManager
|
import com.owenlejeune.tvtime.utils.SessionManager
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@@ -130,7 +129,7 @@ fun ProfileMenuOverlay(
|
|||||||
ProfileMenuItem(
|
ProfileMenuItem(
|
||||||
onClick = {
|
onClick = {
|
||||||
onDismissRequest()
|
onDismissRequest()
|
||||||
appNavController.navigate(MainNavItem.AccountView.route)
|
appNavController.navigate(AppNavItem.AccountView.route)
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
@@ -147,7 +146,7 @@ fun ProfileMenuOverlay(
|
|||||||
ProfileMenuItem(
|
ProfileMenuItem(
|
||||||
onClick = {
|
onClick = {
|
||||||
onDismissRequest()
|
onDismissRequest()
|
||||||
appNavController.navigate(MainNavItem.SettingsView.route)
|
appNavController.navigate(AppNavItem.SettingsView.route)
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
@@ -163,7 +162,7 @@ fun ProfileMenuOverlay(
|
|||||||
ProfileMenuItem(
|
ProfileMenuItem(
|
||||||
onClick = {
|
onClick = {
|
||||||
onDismissRequest()
|
onDismissRequest()
|
||||||
appNavController.navigate(MainNavItem.AboutView.route)
|
appNavController.navigate(AppNavItem.AboutView.route)
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
@@ -187,7 +186,7 @@ fun ProfileMenuOverlay(
|
|||||||
} else {
|
} else {
|
||||||
SessionManager.signInPart1(context) {
|
SessionManager.signInPart1(context) {
|
||||||
appNavController.navigate(
|
appNavController.navigate(
|
||||||
MainNavItem.WebLinkView.route.plus("/$it")
|
AppNavItem.WebLinkView.route.plus("/$it")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.owenlejeune.tvtime.ui.screens.main.tabs.top
|
package com.owenlejeune.tvtime.ui.components
|
||||||
|
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
@@ -17,14 +17,11 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.text.TextStyle
|
import androidx.compose.ui.text.TextStyle
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.google.accompanist.pager.ExperimentalPagerApi
|
import com.google.accompanist.pager.ExperimentalPagerApi
|
||||||
import com.google.accompanist.pager.PagerState
|
import com.google.accompanist.pager.PagerState
|
||||||
import com.google.accompanist.pager.pagerTabIndicatorOffset
|
import com.google.accompanist.pager.pagerTabIndicatorOffset
|
||||||
import com.google.accompanist.pager.rememberPagerState
|
import com.owenlejeune.tvtime.utils.types.TabNavItem
|
||||||
import com.owenlejeune.tvtime.ui.navigation.MediaTabNavItem
|
|
||||||
import com.owenlejeune.tvtime.ui.navigation.TabNavItem
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@OptIn(ExperimentalPagerApi::class)
|
@OptIn(ExperimentalPagerApi::class)
|
||||||
@@ -17,7 +17,6 @@ import androidx.compose.foundation.text.BasicTextField
|
|||||||
import androidx.compose.foundation.text.KeyboardActions
|
import androidx.compose.foundation.text.KeyboardActions
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.AccountCircle
|
|
||||||
import androidx.compose.material.icons.filled.ArrowDropDown
|
import androidx.compose.material.icons.filled.ArrowDropDown
|
||||||
import androidx.compose.material.icons.filled.ArrowDropUp
|
import androidx.compose.material.icons.filled.ArrowDropUp
|
||||||
import androidx.compose.material.icons.filled.Error
|
import androidx.compose.material.icons.filled.Error
|
||||||
@@ -69,8 +68,8 @@ import com.owenlejeune.tvtime.R
|
|||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.AuthorDetails
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.AuthorDetails
|
||||||
import com.owenlejeune.tvtime.extensions.unlessEmpty
|
import com.owenlejeune.tvtime.extensions.unlessEmpty
|
||||||
import com.owenlejeune.tvtime.preferences.AppPreferences
|
import com.owenlejeune.tvtime.preferences.AppPreferences
|
||||||
import com.owenlejeune.tvtime.ui.navigation.MainNavItem
|
import com.owenlejeune.tvtime.ui.navigation.AppNavItem
|
||||||
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
|
import com.owenlejeune.tvtime.utils.types.MediaViewType
|
||||||
import com.owenlejeune.tvtime.utils.SessionManager
|
import com.owenlejeune.tvtime.utils.SessionManager
|
||||||
import com.owenlejeune.tvtime.utils.TmdbUtils
|
import com.owenlejeune.tvtime.utils.TmdbUtils
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
@@ -211,7 +210,7 @@ fun SearchView(
|
|||||||
fab: MutableState<@Composable () -> Unit>,
|
fab: MutableState<@Composable () -> Unit>,
|
||||||
preferences: AppPreferences = KoinJavaComponent.get(AppPreferences::class.java)
|
preferences: AppPreferences = KoinJavaComponent.get(AppPreferences::class.java)
|
||||||
) {
|
) {
|
||||||
val route = "${MainNavItem.SearchView.route}/${mediaType.ordinal}/$title"
|
val route = "${AppNavItem.SearchView.route}/${mediaType.ordinal}/$title"
|
||||||
if (preferences.showSearchBar) {
|
if (preferences.showSearchBar) {
|
||||||
SearchBar(
|
SearchBar(
|
||||||
placeholder = title
|
placeholder = title
|
||||||
|
|||||||
@@ -11,11 +11,12 @@ import com.owenlejeune.tvtime.api.tmdb.api.v3.model.RatedTv
|
|||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.WatchlistMovie
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.WatchlistMovie
|
||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.WatchlistTvSeries
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.WatchlistTvSeries
|
||||||
import com.owenlejeune.tvtime.api.tmdb.api.v4.model.AccountList
|
import com.owenlejeune.tvtime.api.tmdb.api.v4.model.AccountList
|
||||||
import com.owenlejeune.tvtime.ui.screens.main.AccountTabContent
|
import com.owenlejeune.tvtime.ui.screens.AccountTabContent
|
||||||
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
|
import com.owenlejeune.tvtime.utils.types.MediaViewType
|
||||||
import com.owenlejeune.tvtime.ui.screens.main.RecommendedAccountTabContent
|
import com.owenlejeune.tvtime.ui.screens.RecommendedAccountTabContent
|
||||||
import com.owenlejeune.tvtime.utils.ResourceUtils
|
import com.owenlejeune.tvtime.utils.ResourceUtils
|
||||||
import com.owenlejeune.tvtime.utils.SessionManager
|
import com.owenlejeune.tvtime.utils.SessionManager
|
||||||
|
import com.owenlejeune.tvtime.utils.types.TabNavItem
|
||||||
import org.koin.core.component.inject
|
import org.koin.core.component.inject
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,162 @@
|
|||||||
|
package com.owenlejeune.tvtime.ui.navigation
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.navigation.NavHostController
|
||||||
|
import androidx.navigation.NavType
|
||||||
|
import androidx.navigation.compose.NavHost
|
||||||
|
import androidx.navigation.compose.composable
|
||||||
|
import androidx.navigation.navArgument
|
||||||
|
import androidx.navigation.navDeepLink
|
||||||
|
import com.owenlejeune.tvtime.R
|
||||||
|
import com.owenlejeune.tvtime.extensions.WindowSizeClass
|
||||||
|
import com.owenlejeune.tvtime.preferences.AppPreferences
|
||||||
|
import com.owenlejeune.tvtime.ui.screens.SearchScreen
|
||||||
|
import com.owenlejeune.tvtime.ui.screens.AboutScreen
|
||||||
|
import com.owenlejeune.tvtime.ui.screens.AccountScreen
|
||||||
|
import com.owenlejeune.tvtime.ui.screens.ListDetailScreen
|
||||||
|
import com.owenlejeune.tvtime.ui.screens.MediaDetailScreen
|
||||||
|
import com.owenlejeune.tvtime.utils.types.MediaViewType
|
||||||
|
import com.owenlejeune.tvtime.ui.screens.PersonDetailScreen
|
||||||
|
import com.owenlejeune.tvtime.ui.screens.SettingsScreen
|
||||||
|
import com.owenlejeune.tvtime.ui.screens.WebLinkScreen
|
||||||
|
import com.owenlejeune.tvtime.ui.screens.HomeScreen
|
||||||
|
import com.owenlejeune.tvtime.utils.NavConstants
|
||||||
|
import org.koin.java.KoinJavaComponent
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun AppNavigationHost(
|
||||||
|
startDestination: String = AppNavItem.MainView.route,
|
||||||
|
mainNavStartRoute: String = AppNavItem.Items[0].route,
|
||||||
|
appNavController: NavHostController,
|
||||||
|
windowSize: WindowSizeClass,
|
||||||
|
preferences: AppPreferences = KoinJavaComponent.get(AppPreferences::class.java)
|
||||||
|
) {
|
||||||
|
NavHost(navController = appNavController, startDestination = startDestination) {
|
||||||
|
composable(AppNavItem.MainView.route) {
|
||||||
|
HomeScreen(
|
||||||
|
appNavController = appNavController,
|
||||||
|
mainNavStartRoute = mainNavStartRoute,
|
||||||
|
windowSize = windowSize
|
||||||
|
)
|
||||||
|
}
|
||||||
|
composable(
|
||||||
|
AppNavItem.DetailView.route.plus("/{${NavConstants.TYPE_KEY}}/{${NavConstants.ID_KEY}}"),
|
||||||
|
arguments = listOf(
|
||||||
|
navArgument(NavConstants.ID_KEY) { type = NavType.IntType },
|
||||||
|
navArgument(NavConstants.TYPE_KEY) { type = NavType.EnumType(MediaViewType::class.java) }
|
||||||
|
)
|
||||||
|
) { navBackStackEntry ->
|
||||||
|
val args = navBackStackEntry.arguments
|
||||||
|
val mediaType = args?.getSerializable(NavConstants.TYPE_KEY) as MediaViewType
|
||||||
|
|
||||||
|
when (mediaType) {
|
||||||
|
MediaViewType.PERSON -> {
|
||||||
|
PersonDetailScreen(
|
||||||
|
appNavController = appNavController,
|
||||||
|
personId = args.getInt(NavConstants.ID_KEY)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
MediaViewType.LIST -> {
|
||||||
|
ListDetailScreen(
|
||||||
|
appNavController = appNavController,
|
||||||
|
itemId = args.getInt(NavConstants.ID_KEY),
|
||||||
|
windowSize = windowSize
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
MediaDetailScreen(
|
||||||
|
appNavController = appNavController,
|
||||||
|
itemId = args.getInt(NavConstants.ID_KEY),
|
||||||
|
type = mediaType,
|
||||||
|
windowSize = windowSize
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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) {
|
||||||
|
SettingsScreen(appNavController = appNavController)
|
||||||
|
}
|
||||||
|
composable(
|
||||||
|
route = AppNavItem.SearchView.route.plus("/{${NavConstants.SEARCH_ID_KEY}}/{${NavConstants.SEARCH_TITLE_KEY}}"),
|
||||||
|
arguments = listOf(
|
||||||
|
navArgument(NavConstants.SEARCH_ID_KEY) { type = NavType.IntType },
|
||||||
|
navArgument(NavConstants.SEARCH_TITLE_KEY) { type = NavType.StringType }
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
it.arguments?.let { arguments ->
|
||||||
|
val (type, title) = if (preferences.multiSearch) {
|
||||||
|
Pair(
|
||||||
|
MediaViewType.MIXED,
|
||||||
|
stringResource(id = R.string.search_all_title)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Pair(
|
||||||
|
MediaViewType[arguments.getInt(NavConstants.SEARCH_ID_KEY)],
|
||||||
|
arguments.getString(NavConstants.SEARCH_TITLE_KEY) ?: ""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
SearchScreen(
|
||||||
|
appNavController = appNavController,
|
||||||
|
title = title,
|
||||||
|
mediaViewType = type
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
composable(
|
||||||
|
route = AppNavItem.WebLinkView.route.plus("/{${NavConstants.WEB_LINK_KEY}}"),
|
||||||
|
arguments = listOf(
|
||||||
|
navArgument(NavConstants.WEB_LINK_KEY) { type = NavType.StringType }
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
val url = it.arguments?.getString(NavConstants.WEB_LINK_KEY)
|
||||||
|
url?.let {
|
||||||
|
WebLinkScreen(url = url, appNavController = appNavController)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
composable(
|
||||||
|
route = AppNavItem.AccountView.route,
|
||||||
|
deepLinks = listOf(
|
||||||
|
navDeepLink { uriPattern = "app://tvtime.auth.{${NavConstants.ACCOUNT_KEY}}" }
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
val deepLink = it.arguments?.getString(NavConstants.ACCOUNT_KEY)
|
||||||
|
AccountScreen(
|
||||||
|
appNavController = appNavController,
|
||||||
|
doSignInPartTwo = deepLink == NavConstants.AUTH_REDIRECT_PAGE
|
||||||
|
)
|
||||||
|
}
|
||||||
|
composable(route = AppNavItem.AboutView.route) {
|
||||||
|
AboutScreen(appNavController = appNavController)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class AppNavItem(val route: String) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val Items = listOf(MainView, DetailView, SettingsView)
|
||||||
|
}
|
||||||
|
|
||||||
|
object MainView: AppNavItem("main_route")
|
||||||
|
object DetailView: AppNavItem("detail_route")
|
||||||
|
object SettingsView: AppNavItem("settings_route")
|
||||||
|
object SearchView: AppNavItem("search_route")
|
||||||
|
object WebLinkView: AppNavItem("web_link_route")
|
||||||
|
object AccountView: AppNavItem("account_route")
|
||||||
|
object AboutView: AppNavItem("about_route")
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
package com.owenlejeune.tvtime.ui.navigation
|
|
||||||
|
|
||||||
import com.owenlejeune.tvtime.R
|
|
||||||
import com.owenlejeune.tvtime.preferences.AppPreferences
|
|
||||||
import com.owenlejeune.tvtime.utils.ResourceUtils
|
|
||||||
import com.owenlejeune.tvtime.utils.SessionManager
|
|
||||||
import org.koin.core.component.KoinComponent
|
|
||||||
import org.koin.core.component.inject
|
|
||||||
|
|
||||||
sealed class BottomNavItem(
|
|
||||||
stringRes: Int,
|
|
||||||
val icon: Int,
|
|
||||||
val route: String,
|
|
||||||
private val orderGetter: (AppPreferences) -> Int,
|
|
||||||
private val orderSetter: (AppPreferences, Int) -> Unit
|
|
||||||
): KoinComponent {
|
|
||||||
|
|
||||||
private val appPreferences: AppPreferences by inject()
|
|
||||||
private val resourceUtils: ResourceUtils by inject()
|
|
||||||
|
|
||||||
val name = resourceUtils.getString(stringRes)
|
|
||||||
var order: Int
|
|
||||||
get() = orderGetter.invoke(appPreferences)
|
|
||||||
set(value) { orderSetter.invoke(appPreferences, value) }
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
|
|
||||||
val SortedItems
|
|
||||||
get() = Items.filter { it.order > -1 }.sortedBy { it.order }.ifEmpty { Items }
|
|
||||||
|
|
||||||
val Items by lazy {
|
|
||||||
listOf(Movies, TV, People, Account)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getByRoute(route: String?): BottomNavItem? {
|
|
||||||
return when (route) {
|
|
||||||
Movies.route -> Movies
|
|
||||||
TV.route -> TV
|
|
||||||
Account.route -> Account
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object Movies: BottomNavItem(
|
|
||||||
R.string.nav_movies_title,
|
|
||||||
R.drawable.ic_movie,
|
|
||||||
"movies_route",
|
|
||||||
{ it.moviesTabPosition },
|
|
||||||
{ p, i -> p.moviesTabPosition = i }
|
|
||||||
)
|
|
||||||
object TV: BottomNavItem(
|
|
||||||
R.string.nav_tv_title,
|
|
||||||
R.drawable.ic_tv,
|
|
||||||
"tv_route",
|
|
||||||
{ it.tvTabPosition },
|
|
||||||
{ p, i -> p.tvTabPosition = i }
|
|
||||||
)
|
|
||||||
object Account: BottomNavItem(
|
|
||||||
R.string.nav_account_title,
|
|
||||||
R.drawable.ic_person,
|
|
||||||
"account_route",
|
|
||||||
{
|
|
||||||
// if (SessionManager.currentSession.value?.isAuthorized == true) {
|
|
||||||
// it.accountTabPosition
|
|
||||||
// } else {
|
|
||||||
-2
|
|
||||||
// }
|
|
||||||
},
|
|
||||||
{ p, i -> p.accountTabPosition = i }
|
|
||||||
)
|
|
||||||
object People: BottomNavItem(
|
|
||||||
R.string.nav_people_title,
|
|
||||||
R.drawable.ic_face,
|
|
||||||
"people_route",
|
|
||||||
{ it.peopleTabPosition },
|
|
||||||
{ p, i -> p.peopleTabPosition = i }
|
|
||||||
)
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
package com.owenlejeune.tvtime.ui.navigation
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.RowScope
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.MutableState
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.navigation.NavHostController
|
||||||
|
import androidx.navigation.compose.NavHost
|
||||||
|
import androidx.navigation.compose.composable
|
||||||
|
import com.owenlejeune.tvtime.R
|
||||||
|
import com.owenlejeune.tvtime.preferences.AppPreferences
|
||||||
|
import com.owenlejeune.tvtime.ui.screens.AccountViewContent
|
||||||
|
import com.owenlejeune.tvtime.ui.screens.new.tabs.MediaTab
|
||||||
|
import com.owenlejeune.tvtime.utils.types.MediaViewType
|
||||||
|
import com.owenlejeune.tvtime.ui.screens.tabs.PeopleTab
|
||||||
|
import com.owenlejeune.tvtime.utils.ResourceUtils
|
||||||
|
import org.koin.core.component.KoinComponent
|
||||||
|
import org.koin.core.component.inject
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun HomeScreenNavHost(
|
||||||
|
appNavController: NavHostController,
|
||||||
|
navController: NavHostController,
|
||||||
|
fab: MutableState<@Composable () -> Unit>,
|
||||||
|
appBarTitle: MutableState<@Composable () -> Unit>,
|
||||||
|
appBarActions: MutableState<@Composable (RowScope.() -> Unit)> = mutableStateOf({}),
|
||||||
|
startDestination: String = HomeScreenNavItem.SortedItems[0].route
|
||||||
|
) {
|
||||||
|
NavHost(navController = navController, startDestination = startDestination) {
|
||||||
|
composable(HomeScreenNavItem.Movies.route) {
|
||||||
|
appBarActions.value = {}
|
||||||
|
MediaTab(
|
||||||
|
appBarTitle = appBarTitle,
|
||||||
|
appNavController = appNavController,
|
||||||
|
mediaType = MediaViewType.MOVIE,
|
||||||
|
fab = fab
|
||||||
|
)
|
||||||
|
}
|
||||||
|
composable(HomeScreenNavItem.TV.route) {
|
||||||
|
appBarActions.value = {}
|
||||||
|
MediaTab(
|
||||||
|
appBarTitle = appBarTitle,
|
||||||
|
appNavController = appNavController,
|
||||||
|
mediaType = MediaViewType.TV,
|
||||||
|
fab = fab
|
||||||
|
)
|
||||||
|
}
|
||||||
|
composable(route = HomeScreenNavItem.Account.route) {
|
||||||
|
AccountViewContent(appNavController = appNavController)
|
||||||
|
fab.value = {}
|
||||||
|
}
|
||||||
|
composable(HomeScreenNavItem.People.route) {
|
||||||
|
appBarActions.value = {}
|
||||||
|
PeopleTab(
|
||||||
|
appBarTitle = appBarTitle,
|
||||||
|
appNavController = appNavController,
|
||||||
|
fab = fab
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class HomeScreenNavItem(
|
||||||
|
stringRes: Int,
|
||||||
|
val icon: Int,
|
||||||
|
val route: String,
|
||||||
|
private val orderGetter: (AppPreferences) -> Int,
|
||||||
|
private val orderSetter: (AppPreferences, Int) -> Unit
|
||||||
|
): KoinComponent {
|
||||||
|
|
||||||
|
private val appPreferences: AppPreferences by inject()
|
||||||
|
private val resourceUtils: ResourceUtils by inject()
|
||||||
|
|
||||||
|
val name = resourceUtils.getString(stringRes)
|
||||||
|
var order: Int
|
||||||
|
get() = orderGetter.invoke(appPreferences)
|
||||||
|
set(value) { orderSetter.invoke(appPreferences, value) }
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
val SortedItems
|
||||||
|
get() = Items.filter { it.order > -1 }.sortedBy { it.order }.ifEmpty { Items }
|
||||||
|
|
||||||
|
val Items by lazy {
|
||||||
|
listOf(Movies, TV, People, Account)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getByRoute(route: String?): HomeScreenNavItem? {
|
||||||
|
return when (route) {
|
||||||
|
Movies.route -> Movies
|
||||||
|
TV.route -> TV
|
||||||
|
Account.route -> Account
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object Movies: HomeScreenNavItem(
|
||||||
|
R.string.nav_movies_title,
|
||||||
|
R.drawable.ic_movie,
|
||||||
|
"movies_route",
|
||||||
|
{ it.moviesTabPosition },
|
||||||
|
{ p, i -> p.moviesTabPosition = i }
|
||||||
|
)
|
||||||
|
object TV: HomeScreenNavItem(
|
||||||
|
R.string.nav_tv_title,
|
||||||
|
R.drawable.ic_tv,
|
||||||
|
"tv_route",
|
||||||
|
{ it.tvTabPosition },
|
||||||
|
{ p, i -> p.tvTabPosition = i }
|
||||||
|
)
|
||||||
|
object Account: HomeScreenNavItem(
|
||||||
|
R.string.nav_account_title,
|
||||||
|
R.drawable.ic_person,
|
||||||
|
"account_route",
|
||||||
|
{
|
||||||
|
// if (SessionManager.currentSession.value?.isAuthorized == true) {
|
||||||
|
// it.accountTabPosition
|
||||||
|
// } else {
|
||||||
|
-2
|
||||||
|
// }
|
||||||
|
},
|
||||||
|
{ p, i -> p.accountTabPosition = i }
|
||||||
|
)
|
||||||
|
object People: HomeScreenNavItem(
|
||||||
|
R.string.nav_people_title,
|
||||||
|
R.drawable.ic_face,
|
||||||
|
"people_route",
|
||||||
|
{ it.peopleTabPosition },
|
||||||
|
{ p, i -> p.peopleTabPosition = i }
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
package com.owenlejeune.tvtime.ui.navigation
|
|
||||||
|
|
||||||
sealed class MainNavItem(val route: String) {
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val Items = listOf(MainView, DetailView, SettingsView)
|
|
||||||
}
|
|
||||||
|
|
||||||
object MainView: MainNavItem("main_route")
|
|
||||||
object DetailView: MainNavItem("detail_route")
|
|
||||||
object SettingsView: MainNavItem("settings_route")
|
|
||||||
object SearchView: MainNavItem("search_route")
|
|
||||||
object WebLinkView: MainNavItem("web_link_route")
|
|
||||||
object AccountView: MainNavItem("account_route")
|
|
||||||
object AboutView: MainNavItem("about_route")
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -5,10 +5,11 @@ import androidx.navigation.NavHostController
|
|||||||
import com.owenlejeune.tvtime.R
|
import com.owenlejeune.tvtime.R
|
||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.HomePageService
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.HomePageService
|
||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.HomePageResponse
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.HomePageResponse
|
||||||
import com.owenlejeune.tvtime.ui.screens.main.MediaTabContent
|
import com.owenlejeune.tvtime.utils.types.MediaViewType
|
||||||
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
|
|
||||||
import com.owenlejeune.tvtime.api.tmdb.viewmodel.MediaTabViewModel
|
import com.owenlejeune.tvtime.api.tmdb.viewmodel.MediaTabViewModel
|
||||||
|
import com.owenlejeune.tvtime.ui.screens.tabs.MediaTabContent
|
||||||
import com.owenlejeune.tvtime.utils.ResourceUtils
|
import com.owenlejeune.tvtime.utils.ResourceUtils
|
||||||
|
import com.owenlejeune.tvtime.utils.types.TabNavItem
|
||||||
import org.koin.core.component.inject
|
import org.koin.core.component.inject
|
||||||
import retrofit2.Response
|
import retrofit2.Response
|
||||||
|
|
||||||
|
|||||||
@@ -1,58 +0,0 @@
|
|||||||
package com.owenlejeune.tvtime.ui.navigation
|
|
||||||
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
|
||||||
import androidx.compose.foundation.layout.RowScope
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.MutableState
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.navigation.NavHostController
|
|
||||||
import androidx.navigation.compose.NavHost
|
|
||||||
import androidx.navigation.compose.composable
|
|
||||||
import androidx.navigation.navDeepLink
|
|
||||||
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
|
|
||||||
import com.owenlejeune.tvtime.ui.screens.main.*
|
|
||||||
import com.owenlejeune.tvtime.utils.NavConstants
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun MainNavGraph(
|
|
||||||
activity: AppCompatActivity,
|
|
||||||
appNavController: NavHostController,
|
|
||||||
navController: NavHostController,
|
|
||||||
fab: MutableState<@Composable () -> Unit>,
|
|
||||||
appBarTitle: MutableState<@Composable () -> Unit>,
|
|
||||||
appBarActions: MutableState<@Composable (RowScope.() -> Unit)> = mutableStateOf({}),
|
|
||||||
startDestination: String = BottomNavItem.SortedItems[0].route
|
|
||||||
) {
|
|
||||||
NavHost(navController = navController, startDestination = startDestination) {
|
|
||||||
composable(BottomNavItem.Movies.route) {
|
|
||||||
appBarActions.value = {}
|
|
||||||
MediaTab(
|
|
||||||
appBarTitle = appBarTitle,
|
|
||||||
appNavController = appNavController,
|
|
||||||
mediaType = MediaViewType.MOVIE,
|
|
||||||
fab = fab
|
|
||||||
)
|
|
||||||
}
|
|
||||||
composable(BottomNavItem.TV.route) {
|
|
||||||
appBarActions.value = {}
|
|
||||||
MediaTab(
|
|
||||||
appBarTitle = appBarTitle,
|
|
||||||
appNavController = appNavController,
|
|
||||||
mediaType = MediaViewType.TV,
|
|
||||||
fab = fab
|
|
||||||
)
|
|
||||||
}
|
|
||||||
composable(route = BottomNavItem.Account.route) {
|
|
||||||
AccountViewContent(appNavController = appNavController)
|
|
||||||
fab.value = {}
|
|
||||||
}
|
|
||||||
composable(BottomNavItem.People.route) {
|
|
||||||
appBarActions.value = {}
|
|
||||||
PeopleTab(
|
|
||||||
appBarTitle = appBarTitle,
|
|
||||||
appNavController = appNavController,
|
|
||||||
fab = fab
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.owenlejeune.tvtime.ui.screens.main
|
package com.owenlejeune.tvtime.ui.screens
|
||||||
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
@@ -58,7 +58,7 @@ import dev.jeziellago.compose.markdowntext.MarkdownText
|
|||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun AboutView(
|
fun AboutScreen(
|
||||||
appNavController: NavController
|
appNavController: NavController
|
||||||
) {
|
) {
|
||||||
val systemUiController = rememberSystemUiController()
|
val systemUiController = rememberSystemUiController()
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.owenlejeune.tvtime.ui.screens.main
|
package com.owenlejeune.tvtime.ui.screens
|
||||||
|
|
||||||
import androidx.compose.animation.rememberSplineBasedDecay
|
import androidx.compose.animation.rememberSplineBasedDecay
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
@@ -36,8 +36,10 @@ import com.owenlejeune.tvtime.ui.components.AccountIcon
|
|||||||
import com.owenlejeune.tvtime.ui.components.PagingPosterGrid
|
import com.owenlejeune.tvtime.ui.components.PagingPosterGrid
|
||||||
import com.owenlejeune.tvtime.ui.navigation.AccountTabNavItem
|
import com.owenlejeune.tvtime.ui.navigation.AccountTabNavItem
|
||||||
import com.owenlejeune.tvtime.ui.navigation.ListFetchFun
|
import com.owenlejeune.tvtime.ui.navigation.ListFetchFun
|
||||||
import com.owenlejeune.tvtime.ui.navigation.MainNavItem
|
import com.owenlejeune.tvtime.ui.navigation.AppNavItem
|
||||||
import com.owenlejeune.tvtime.ui.screens.main.tabs.top.ScrollableTabs
|
import com.owenlejeune.tvtime.ui.components.MediaResultCard
|
||||||
|
import com.owenlejeune.tvtime.utils.types.MediaViewType
|
||||||
|
import com.owenlejeune.tvtime.ui.components.ScrollableTabs
|
||||||
import com.owenlejeune.tvtime.utils.SessionManager
|
import com.owenlejeune.tvtime.utils.SessionManager
|
||||||
import com.owenlejeune.tvtime.utils.TmdbUtils
|
import com.owenlejeune.tvtime.utils.TmdbUtils
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@@ -45,7 +47,7 @@ import kotlin.reflect.KClass
|
|||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun AccountView(
|
fun AccountScreen(
|
||||||
appNavController: NavHostController,
|
appNavController: NavHostController,
|
||||||
doSignInPartTwo: Boolean = false
|
doSignInPartTwo: Boolean = false
|
||||||
) {
|
) {
|
||||||
@@ -303,7 +305,7 @@ fun RecommendedAccountTabContent(
|
|||||||
lazyPagingItems = mediaListItems,
|
lazyPagingItems = mediaListItems,
|
||||||
onClick = { id ->
|
onClick = { id ->
|
||||||
appNavController.navigate(
|
appNavController.navigate(
|
||||||
"${MainNavItem.DetailView.route}/${mediaViewType}/${id}"
|
"${AppNavItem.DetailView.route}/${mediaViewType}/${id}"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -0,0 +1,313 @@
|
|||||||
|
package com.owenlejeune.tvtime.ui.screens
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import androidx.activity.compose.BackHandler
|
||||||
|
import androidx.compose.animation.rememberSplineBasedDecay
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.RowScope
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material.Scaffold
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.LargeTopAppBar
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.NavigationBar
|
||||||
|
import androidx.compose.material3.NavigationBarItem
|
||||||
|
import androidx.compose.material3.NavigationRail
|
||||||
|
import androidx.compose.material3.NavigationRailItem
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
|
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||||
|
import androidx.compose.material3.rememberTopAppBarScrollState
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.MutableState
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.graphics.compositeOver
|
||||||
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.navigation.NavController
|
||||||
|
import androidx.navigation.NavHostController
|
||||||
|
import androidx.navigation.compose.currentBackStackEntryAsState
|
||||||
|
import androidx.navigation.compose.rememberNavController
|
||||||
|
import com.owenlejeune.tvtime.extensions.WindowSizeClass
|
||||||
|
import com.owenlejeune.tvtime.extensions.navigateInBottomBar
|
||||||
|
import com.owenlejeune.tvtime.preferences.AppPreferences
|
||||||
|
import com.owenlejeune.tvtime.ui.components.AccountIcon
|
||||||
|
import com.owenlejeune.tvtime.ui.components.ProfileMenuContainer
|
||||||
|
import com.owenlejeune.tvtime.ui.components.ProfileMenuDefaults
|
||||||
|
import com.owenlejeune.tvtime.ui.navigation.HomeScreenNavItem
|
||||||
|
import com.owenlejeune.tvtime.ui.navigation.HomeScreenNavHost
|
||||||
|
import org.koin.java.KoinJavaComponent
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun HomeScreen(
|
||||||
|
appNavController: NavHostController,
|
||||||
|
mainNavStartRoute: String = HomeScreenNavItem.SortedItems[0].route,
|
||||||
|
windowSize: WindowSizeClass
|
||||||
|
) {
|
||||||
|
val navController = rememberNavController()
|
||||||
|
|
||||||
|
val decayAnimationSpec = rememberSplineBasedDecay<Float>()
|
||||||
|
val topAppBarScrollState = rememberTopAppBarScrollState()
|
||||||
|
val scrollBehavior = remember(decayAnimationSpec) {
|
||||||
|
TopAppBarDefaults.exitUntilCollapsedScrollBehavior(decayAnimationSpec, topAppBarScrollState)
|
||||||
|
}
|
||||||
|
|
||||||
|
val appBarTitle = remember { mutableStateOf<@Composable () -> Unit>({}) }
|
||||||
|
val appBarActions = remember { mutableStateOf<@Composable RowScope.() -> Unit>({}) }
|
||||||
|
val fab = remember { mutableStateOf<@Composable () -> Unit>({}) }
|
||||||
|
|
||||||
|
val showProfileMenuOverlay = remember { mutableStateOf(false) }
|
||||||
|
val navigationIcon = @Composable {
|
||||||
|
AccountIcon(
|
||||||
|
modifier = Modifier.padding(horizontal = 12.dp),
|
||||||
|
size = 32.dp,
|
||||||
|
onClick = { showProfileMenuOverlay.value = true }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val defaultNavBarColor = MaterialTheme.colorScheme.primary.copy(alpha = 0.08f).compositeOver(background = MaterialTheme.colorScheme.surface)
|
||||||
|
|
||||||
|
ProfileMenuContainer(
|
||||||
|
appNavController = appNavController,
|
||||||
|
visible = showProfileMenuOverlay.value,
|
||||||
|
onDismissRequest = { showProfileMenuOverlay.value = false },
|
||||||
|
colors = ProfileMenuDefaults.systemBarColors(navBarColor = defaultNavBarColor)
|
||||||
|
) {
|
||||||
|
Scaffold(
|
||||||
|
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||||
|
topBar = {
|
||||||
|
if (windowSize != WindowSizeClass.Expanded) {
|
||||||
|
TopBar(
|
||||||
|
title = appBarTitle.value,
|
||||||
|
scrollBehavior = scrollBehavior,
|
||||||
|
appBarActions = appBarActions,
|
||||||
|
navigationIcon = navigationIcon
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
floatingActionButton = {
|
||||||
|
fab.value()
|
||||||
|
},
|
||||||
|
bottomBar = {
|
||||||
|
if (windowSize != WindowSizeClass.Expanded) {
|
||||||
|
BottomNavBar(navController = navController)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) { innerPadding ->
|
||||||
|
Box(modifier = Modifier.padding(innerPadding)) {
|
||||||
|
MainContent(
|
||||||
|
windowSize = windowSize,
|
||||||
|
appNavController = appNavController,
|
||||||
|
navController = navController,
|
||||||
|
fab = fab,
|
||||||
|
appBarTitle = appBarTitle,
|
||||||
|
appBarActions = appBarActions,
|
||||||
|
topBarScrollBehaviour = scrollBehavior,
|
||||||
|
mainNavStartRoute = mainNavStartRoute,
|
||||||
|
navigationIcon = navigationIcon
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun TopBar(
|
||||||
|
title: @Composable () -> Unit,
|
||||||
|
scrollBehavior: TopAppBarScrollBehavior,
|
||||||
|
appBarActions: MutableState<@Composable (RowScope.() -> Unit)> = mutableStateOf({}),
|
||||||
|
navigationIcon: @Composable () -> Unit = {}
|
||||||
|
) {
|
||||||
|
LargeTopAppBar(
|
||||||
|
title = title,
|
||||||
|
scrollBehavior = scrollBehavior,
|
||||||
|
colors = TopAppBarDefaults
|
||||||
|
.largeTopAppBarColors(
|
||||||
|
scrolledContainerColor = MaterialTheme.colorScheme.background
|
||||||
|
),
|
||||||
|
actions = {
|
||||||
|
appBarActions.value(this)
|
||||||
|
},
|
||||||
|
navigationIcon = navigationIcon
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun BottomNavBar(
|
||||||
|
navController: NavController,
|
||||||
|
preferences: AppPreferences = KoinJavaComponent.get(AppPreferences::class.java)
|
||||||
|
) {
|
||||||
|
val navBackStackEntry by navController.currentBackStackEntryAsState()
|
||||||
|
val currentRoute = navBackStackEntry?.destination?.route
|
||||||
|
|
||||||
|
NavigationBar {
|
||||||
|
HomeScreenNavItem.SortedItems.forEach { item ->
|
||||||
|
val isSelected = currentRoute == item.route
|
||||||
|
NavigationBarItem(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(4.dp)
|
||||||
|
.clip(RoundedCornerShape(24.dp)),
|
||||||
|
icon = { Icon(painter = painterResource(id = item.icon), contentDescription = null) },
|
||||||
|
label = {
|
||||||
|
val name = if (preferences.showBottomTabLabels) item.name else " "
|
||||||
|
Text(text = name)
|
||||||
|
},
|
||||||
|
selected = isSelected,
|
||||||
|
onClick = {
|
||||||
|
if (!isSelected) {
|
||||||
|
navController.navigateInBottomBar(item.route)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun MainContent(
|
||||||
|
windowSize: WindowSizeClass,
|
||||||
|
appNavController: NavHostController,
|
||||||
|
navController: NavHostController,
|
||||||
|
fab: MutableState<@Composable () -> Unit>,
|
||||||
|
topBarScrollBehaviour: TopAppBarScrollBehavior,
|
||||||
|
appBarTitle: MutableState<@Composable () -> Unit>,
|
||||||
|
appBarActions: MutableState<@Composable (RowScope.() -> Unit)> = mutableStateOf({}),
|
||||||
|
navigationIcon: @Composable () -> Unit = {},
|
||||||
|
mainNavStartRoute: String = HomeScreenNavItem.SortedItems[0].route
|
||||||
|
) {
|
||||||
|
if (windowSize == WindowSizeClass.Expanded) {
|
||||||
|
DualColumnMainContent(
|
||||||
|
appNavController = appNavController,
|
||||||
|
navController = navController,
|
||||||
|
fab = fab,
|
||||||
|
appBarTitle = appBarTitle,
|
||||||
|
appBarActions = appBarActions,
|
||||||
|
topBarScrollBehaviour = topBarScrollBehaviour,
|
||||||
|
mainNavStartRoute = mainNavStartRoute,
|
||||||
|
navigationIcon = navigationIcon
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
SingleColumnMainContent(
|
||||||
|
appNavController = appNavController,
|
||||||
|
navController = navController,
|
||||||
|
fab = fab,
|
||||||
|
appBarTitle = appBarTitle,
|
||||||
|
appBarActions = appBarActions,
|
||||||
|
mainNavStartRoute = mainNavStartRoute
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun SingleColumnMainContent(
|
||||||
|
appNavController: NavHostController,
|
||||||
|
navController: NavHostController,
|
||||||
|
fab: MutableState<@Composable () -> Unit>,
|
||||||
|
appBarTitle: MutableState<@Composable () -> Unit>,
|
||||||
|
appBarActions: MutableState<@Composable (RowScope.() -> Unit)> = mutableStateOf({}),
|
||||||
|
mainNavStartRoute: String = HomeScreenNavItem.SortedItems[0].route
|
||||||
|
) {
|
||||||
|
MainMediaView(
|
||||||
|
appNavController = appNavController,
|
||||||
|
navController = navController,
|
||||||
|
fab = fab,
|
||||||
|
appBarTitle = appBarTitle,
|
||||||
|
appBarActions = appBarActions,
|
||||||
|
mainNavStartRoute = mainNavStartRoute
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun DualColumnMainContent(
|
||||||
|
appNavController: NavHostController,
|
||||||
|
navController: NavHostController,
|
||||||
|
fab: MutableState<@Composable () -> Unit>,
|
||||||
|
topBarScrollBehaviour: TopAppBarScrollBehavior,
|
||||||
|
appBarTitle: MutableState<@Composable () -> Unit>,
|
||||||
|
appBarActions: MutableState<@Composable (RowScope.() -> Unit)> = mutableStateOf({}),
|
||||||
|
navigationIcon: @Composable () -> Unit = {},
|
||||||
|
mainNavStartRoute: String = HomeScreenNavItem.SortedItems[0].route,
|
||||||
|
preferences: AppPreferences = KoinJavaComponent.get(AppPreferences::class.java)
|
||||||
|
) {
|
||||||
|
val navBackStackEntry by navController.currentBackStackEntryAsState()
|
||||||
|
val currentRoute = navBackStackEntry?.destination?.route
|
||||||
|
|
||||||
|
Row(modifier = Modifier.fillMaxSize()) {
|
||||||
|
NavigationRail {
|
||||||
|
Spacer(modifier = Modifier.weight(1f))
|
||||||
|
HomeScreenNavItem.SortedItems.forEachIndexed { index, item ->
|
||||||
|
val isSelected = currentRoute == item.route
|
||||||
|
NavigationRailItem(
|
||||||
|
icon = { Icon(painter = painterResource(id = item.icon), contentDescription = null) },
|
||||||
|
label = { if (preferences.showBottomTabLabels) Text(item.name) },
|
||||||
|
selected = isSelected,
|
||||||
|
onClick = {
|
||||||
|
if (!isSelected) {
|
||||||
|
navController.navigateInBottomBar(item.route)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
if (index < HomeScreenNavItem.SortedItems.size - 1) {
|
||||||
|
Spacer(modifier = Modifier.height(20.dp))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Spacer(modifier = Modifier.weight(1f))
|
||||||
|
}
|
||||||
|
Column {
|
||||||
|
TopBar(
|
||||||
|
title = appBarTitle.value,
|
||||||
|
scrollBehavior = topBarScrollBehaviour,
|
||||||
|
appBarActions = appBarActions,
|
||||||
|
navigationIcon = navigationIcon
|
||||||
|
)
|
||||||
|
MainMediaView(
|
||||||
|
appNavController = appNavController,
|
||||||
|
navController = navController,
|
||||||
|
fab = fab,
|
||||||
|
appBarTitle = appBarTitle,
|
||||||
|
appBarActions = appBarActions,
|
||||||
|
mainNavStartRoute = mainNavStartRoute
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun MainMediaView(
|
||||||
|
appNavController: NavHostController,
|
||||||
|
navController: NavHostController,
|
||||||
|
fab: MutableState<@Composable () -> Unit>,
|
||||||
|
appBarTitle: MutableState<@Composable () -> Unit>,
|
||||||
|
appBarActions: MutableState<RowScope.() -> Unit> = mutableStateOf({}),
|
||||||
|
mainNavStartRoute: String = HomeScreenNavItem.SortedItems[0].route
|
||||||
|
) {
|
||||||
|
val activity = LocalContext.current as Activity
|
||||||
|
Column {
|
||||||
|
BackHandler(enabled = true) {
|
||||||
|
activity.finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
HomeScreenNavHost(
|
||||||
|
appNavController = appNavController,
|
||||||
|
navController = navController,
|
||||||
|
fab = fab,
|
||||||
|
appBarTitle = appBarTitle,
|
||||||
|
appBarActions = appBarActions,
|
||||||
|
startDestination = mainNavStartRoute
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.owenlejeune.tvtime.ui.screens.main
|
package com.owenlejeune.tvtime.ui.screens
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
@@ -45,7 +45,9 @@ import com.owenlejeune.tvtime.extensions.unlessEmpty
|
|||||||
import com.owenlejeune.tvtime.preferences.AppPreferences
|
import com.owenlejeune.tvtime.preferences.AppPreferences
|
||||||
import com.owenlejeune.tvtime.ui.components.Spinner
|
import com.owenlejeune.tvtime.ui.components.Spinner
|
||||||
import com.owenlejeune.tvtime.ui.components.SwitchPreference
|
import com.owenlejeune.tvtime.ui.components.SwitchPreference
|
||||||
import com.owenlejeune.tvtime.ui.navigation.MainNavItem
|
import com.owenlejeune.tvtime.ui.navigation.AppNavItem
|
||||||
|
import com.owenlejeune.tvtime.utils.types.MediaViewType
|
||||||
|
import com.owenlejeune.tvtime.ui.components.RatingView
|
||||||
import com.owenlejeune.tvtime.ui.theme.*
|
import com.owenlejeune.tvtime.ui.theme.*
|
||||||
import com.owenlejeune.tvtime.utils.SessionManager
|
import com.owenlejeune.tvtime.utils.SessionManager
|
||||||
import com.owenlejeune.tvtime.utils.TmdbUtils
|
import com.owenlejeune.tvtime.utils.TmdbUtils
|
||||||
@@ -60,7 +62,7 @@ import kotlin.math.roundToInt
|
|||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun ListDetailView(
|
fun ListDetailScreen(
|
||||||
appNavController: NavController,
|
appNavController: NavController,
|
||||||
itemId: Int?,
|
itemId: Int?,
|
||||||
windowSize: WindowSizeClass,
|
windowSize: WindowSizeClass,
|
||||||
@@ -457,7 +459,7 @@ private fun ListItemView(
|
|||||||
.clickable(
|
.clickable(
|
||||||
onClick = {
|
onClick = {
|
||||||
appNavController.navigate(
|
appNavController.navigate(
|
||||||
"${MainNavItem.DetailView.route}/${listItem.mediaType}/${listItem.id}"
|
"${AppNavItem.DetailView.route}/${listItem.mediaType}/${listItem.id}"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -575,7 +577,7 @@ private fun ActionButtonRow(listItem: ListItem) {
|
|||||||
contentDescription = stringResource(id = R.string.favourite_label),
|
contentDescription = stringResource(id = R.string.favourite_label),
|
||||||
isSelected = isFavourited,
|
isSelected = isFavourited,
|
||||||
filledIconColor = FavoriteSelected,
|
filledIconColor = FavoriteSelected,
|
||||||
onClick = ::addToFavorite
|
onClick = ::listAddToFavorite
|
||||||
)
|
)
|
||||||
|
|
||||||
ActionButton(
|
ActionButton(
|
||||||
@@ -585,7 +587,7 @@ private fun ActionButtonRow(listItem: ListItem) {
|
|||||||
contentDescription = "",
|
contentDescription = "",
|
||||||
isSelected = isWatchlisted,
|
isSelected = isWatchlisted,
|
||||||
filledIconColor = WatchlistSelected,
|
filledIconColor = WatchlistSelected,
|
||||||
onClick = ::addToWatchlist
|
onClick = ::listAddToWatchlist
|
||||||
)
|
)
|
||||||
|
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
@@ -604,7 +606,7 @@ private fun ActionButtonRow(listItem: ListItem) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addToWatchlist(
|
private fun listAddToWatchlist(
|
||||||
context: Context,
|
context: Context,
|
||||||
itemId: Int,
|
itemId: Int,
|
||||||
type: MediaViewType,
|
type: MediaViewType,
|
||||||
@@ -629,7 +631,7 @@ private fun addToWatchlist(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addToFavorite(
|
private fun listAddToFavorite(
|
||||||
context: Context,
|
context: Context,
|
||||||
itemId: Int,
|
itemId: Int,
|
||||||
type: MediaViewType,
|
type: MediaViewType,
|
||||||
@@ -1,11 +1,9 @@
|
|||||||
package com.owenlejeune.tvtime.ui.screens.main
|
package com.owenlejeune.tvtime.ui.screens
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.compose.animation.*
|
import androidx.compose.animation.*
|
||||||
import androidx.compose.animation.core.LinearEasing
|
import androidx.compose.animation.core.LinearEasing
|
||||||
import androidx.compose.animation.core.RepeatMode
|
|
||||||
import androidx.compose.animation.core.infiniteRepeatable
|
|
||||||
import androidx.compose.animation.core.tween
|
import androidx.compose.animation.core.tween
|
||||||
import androidx.compose.foundation.*
|
import androidx.compose.foundation.*
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
@@ -16,7 +14,6 @@ import androidx.compose.material.icons.Icons
|
|||||||
import androidx.compose.material.icons.filled.ArrowBack
|
import androidx.compose.material.icons.filled.ArrowBack
|
||||||
import androidx.compose.material.icons.filled.Movie
|
import androidx.compose.material.icons.filled.Movie
|
||||||
import androidx.compose.material.icons.filled.Send
|
import androidx.compose.material.icons.filled.Send
|
||||||
import androidx.compose.material.icons.outlined.ExpandLess
|
|
||||||
import androidx.compose.material.icons.outlined.ExpandMore
|
import androidx.compose.material.icons.outlined.ExpandMore
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
@@ -31,7 +28,6 @@ import androidx.compose.ui.layout.ContentScale
|
|||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.TextStyle
|
|
||||||
import androidx.compose.ui.text.font.FontStyle
|
import androidx.compose.ui.text.font.FontStyle
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
@@ -55,9 +51,11 @@ import com.owenlejeune.tvtime.extensions.WindowSizeClass
|
|||||||
import com.owenlejeune.tvtime.extensions.listItems
|
import com.owenlejeune.tvtime.extensions.listItems
|
||||||
import com.owenlejeune.tvtime.preferences.AppPreferences
|
import com.owenlejeune.tvtime.preferences.AppPreferences
|
||||||
import com.owenlejeune.tvtime.ui.components.*
|
import com.owenlejeune.tvtime.ui.components.*
|
||||||
import com.owenlejeune.tvtime.ui.navigation.MainNavItem
|
import com.owenlejeune.tvtime.ui.navigation.AppNavItem
|
||||||
import com.owenlejeune.tvtime.ui.navigation.TabNavItem
|
import com.owenlejeune.tvtime.utils.types.TabNavItem
|
||||||
import com.owenlejeune.tvtime.ui.screens.main.tabs.top.Tabs
|
import com.owenlejeune.tvtime.ui.components.DetailHeader
|
||||||
|
import com.owenlejeune.tvtime.utils.types.MediaViewType
|
||||||
|
import com.owenlejeune.tvtime.ui.components.Tabs
|
||||||
import com.owenlejeune.tvtime.ui.theme.FavoriteSelected
|
import com.owenlejeune.tvtime.ui.theme.FavoriteSelected
|
||||||
import com.owenlejeune.tvtime.ui.theme.RatingSelected
|
import com.owenlejeune.tvtime.ui.theme.RatingSelected
|
||||||
import com.owenlejeune.tvtime.ui.theme.WatchlistSelected
|
import com.owenlejeune.tvtime.ui.theme.WatchlistSelected
|
||||||
@@ -71,7 +69,7 @@ import java.text.DecimalFormat
|
|||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalPagerApi::class)
|
@OptIn(ExperimentalMaterial3Api::class, ExperimentalPagerApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun MediaDetailView(
|
fun MediaDetailScreen(
|
||||||
appNavController: NavController,
|
appNavController: NavController,
|
||||||
itemId: Int?,
|
itemId: Int?,
|
||||||
type: MediaViewType,
|
type: MediaViewType,
|
||||||
@@ -780,7 +778,7 @@ fun FavoriteButton(
|
|||||||
contentDescription = "",
|
contentDescription = "",
|
||||||
isSelected = isFavourited,
|
isSelected = isFavourited,
|
||||||
filledIconColor = FavoriteSelected,
|
filledIconColor = FavoriteSelected,
|
||||||
onClick = ::addToFavorite
|
onClick = ::mediaAddToFavorite
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1140,7 +1138,7 @@ private fun CastCrewCard(appNavController: NavController, person: Person) {
|
|||||||
subtitleTextColor = MaterialTheme.colorScheme.onSecondary,
|
subtitleTextColor = MaterialTheme.colorScheme.onSecondary,
|
||||||
onItemClicked = {
|
onItemClicked = {
|
||||||
appNavController.navigate(
|
appNavController.navigate(
|
||||||
"${MainNavItem.DetailView.route}/${MediaViewType.PERSON}/${person.id}"
|
"${AppNavItem.DetailView.route}/${MediaViewType.PERSON}/${person.id}"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -1186,7 +1184,7 @@ fun SimilarContentCard(
|
|||||||
imageUrl = TmdbUtils.getFullPosterPath(content),
|
imageUrl = TmdbUtils.getFullPosterPath(content),
|
||||||
onItemClicked = {
|
onItemClicked = {
|
||||||
appNavController.navigate(
|
appNavController.navigate(
|
||||||
"${MainNavItem.DetailView.route}/${mediaType}/${content.id}"
|
"${AppNavItem.DetailView.route}/${mediaType}/${content.id}"
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
placeholder = Icons.Filled.Movie
|
placeholder = Icons.Filled.Movie
|
||||||
@@ -1573,7 +1571,7 @@ private fun addToWatchlist(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addToFavorite(
|
private fun mediaAddToFavorite(
|
||||||
context: Context,
|
context: Context,
|
||||||
itemId: Int,
|
itemId: Int,
|
||||||
type: MediaViewType,
|
type: MediaViewType,
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.owenlejeune.tvtime.ui.screens.main
|
package com.owenlejeune.tvtime.ui.screens
|
||||||
|
|
||||||
import androidx.compose.animation.rememberSplineBasedDecay
|
import androidx.compose.animation.rememberSplineBasedDecay
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
@@ -28,7 +28,9 @@ import com.owenlejeune.tvtime.api.tmdb.api.v3.model.PersonCreditsResponse
|
|||||||
import com.owenlejeune.tvtime.ui.components.ContentCard
|
import com.owenlejeune.tvtime.ui.components.ContentCard
|
||||||
import com.owenlejeune.tvtime.ui.components.ExpandableContentCard
|
import com.owenlejeune.tvtime.ui.components.ExpandableContentCard
|
||||||
import com.owenlejeune.tvtime.ui.components.TwoLineImageTextCard
|
import com.owenlejeune.tvtime.ui.components.TwoLineImageTextCard
|
||||||
import com.owenlejeune.tvtime.ui.navigation.MainNavItem
|
import com.owenlejeune.tvtime.ui.navigation.AppNavItem
|
||||||
|
import com.owenlejeune.tvtime.ui.components.DetailHeader
|
||||||
|
import com.owenlejeune.tvtime.utils.types.MediaViewType
|
||||||
import com.owenlejeune.tvtime.utils.TmdbUtils
|
import com.owenlejeune.tvtime.utils.TmdbUtils
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@@ -37,7 +39,7 @@ import kotlinx.coroutines.withContext
|
|||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalPagerApi::class)
|
@OptIn(ExperimentalMaterial3Api::class, ExperimentalPagerApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun PersonDetailView(
|
fun PersonDetailScreen(
|
||||||
appNavController: NavController,
|
appNavController: NavController,
|
||||||
personId: Int?
|
personId: Int?
|
||||||
) {
|
) {
|
||||||
@@ -125,7 +127,7 @@ fun PersonDetailView(
|
|||||||
onItemClicked = {
|
onItemClicked = {
|
||||||
personId?.let {
|
personId?.let {
|
||||||
appNavController.navigate(
|
appNavController.navigate(
|
||||||
"${MainNavItem.DetailView.route}/${content.mediaType}/${content.id}"
|
"${AppNavItem.DetailView.route}/${content.mediaType}/${content.id}"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -171,7 +173,7 @@ fun PersonDetailView(
|
|||||||
onItemClicked = {
|
onItemClicked = {
|
||||||
personId?.let {
|
personId?.let {
|
||||||
appNavController.navigate(
|
appNavController.navigate(
|
||||||
"${MainNavItem.DetailView.route}/${content.mediaType}/${content.id}"
|
"${AppNavItem.DetailView.route}/${content.mediaType}/${content.id}"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,8 @@
|
|||||||
package com.owenlejeune.tvtime.ui.screens
|
package com.owenlejeune.tvtime.ui.screens
|
||||||
|
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
||||||
import androidx.compose.material.Card
|
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.ArrowBack
|
import androidx.compose.material.icons.filled.ArrowBack
|
||||||
import androidx.compose.material.icons.filled.Clear
|
import androidx.compose.material.icons.filled.Clear
|
||||||
@@ -14,17 +11,13 @@ import androidx.compose.runtime.*
|
|||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.blur
|
|
||||||
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.graphics.Color
|
|
||||||
import androidx.compose.ui.layout.ContentScale
|
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import coil.compose.AsyncImage
|
|
||||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||||
import com.owenlejeune.tvtime.R
|
import com.owenlejeune.tvtime.R
|
||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.DetailService
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.DetailService
|
||||||
@@ -33,9 +26,8 @@ import com.owenlejeune.tvtime.api.tmdb.api.v3.SearchService
|
|||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.TvService
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.TvService
|
||||||
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.*
|
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.*
|
||||||
import com.owenlejeune.tvtime.extensions.listItems
|
import com.owenlejeune.tvtime.extensions.listItems
|
||||||
import com.owenlejeune.tvtime.ui.navigation.MainNavItem
|
import com.owenlejeune.tvtime.ui.components.MediaResultCard
|
||||||
import com.owenlejeune.tvtime.ui.screens.main.MediaResultCard
|
import com.owenlejeune.tvtime.utils.types.MediaViewType
|
||||||
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
|
|
||||||
import com.owenlejeune.tvtime.utils.TmdbUtils
|
import com.owenlejeune.tvtime.utils.TmdbUtils
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.owenlejeune.tvtime.ui.screens.main
|
package com.owenlejeune.tvtime.ui.screens
|
||||||
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
@@ -37,7 +37,7 @@ 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.*
|
||||||
import com.owenlejeune.tvtime.ui.navigation.MainNavItem
|
import com.owenlejeune.tvtime.ui.navigation.AppNavItem
|
||||||
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.ResourceUtils
|
||||||
@@ -49,9 +49,8 @@ import org.koin.java.KoinJavaComponent.get
|
|||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun SettingsTab(
|
fun SettingsScreen(
|
||||||
appNavController: NavController,
|
appNavController: NavController,
|
||||||
activity: AppCompatActivity,
|
|
||||||
route: String? = null,
|
route: String? = null,
|
||||||
preferences: AppPreferences = get(AppPreferences::class.java)
|
preferences: AppPreferences = get(AppPreferences::class.java)
|
||||||
) {
|
) {
|
||||||
@@ -110,6 +109,7 @@ fun SettingsTab(
|
|||||||
.padding(all = 24.dp),
|
.padding(all = 24.dp),
|
||||||
verticalArrangement = Arrangement.spacedBy(18.dp)
|
verticalArrangement = Arrangement.spacedBy(18.dp)
|
||||||
) {
|
) {
|
||||||
|
val activity = LocalContext.current as AppCompatActivity
|
||||||
SettingsPage.getByRoute(route).apply {
|
SettingsPage.getByRoute(route).apply {
|
||||||
appBarTitle.value = name
|
appBarTitle.value = name
|
||||||
restoreAction.value = resetPreferencesHandler
|
restoreAction.value = resetPreferencesHandler
|
||||||
@@ -188,7 +188,7 @@ private fun TopLevelSettingsCard(
|
|||||||
.wrapContentHeight()
|
.wrapContentHeight()
|
||||||
.clickable(
|
.clickable(
|
||||||
onClick = {
|
onClick = {
|
||||||
appNavController.navigate("${MainNavItem.SettingsView.route}/${settingsView.route}")
|
appNavController.navigate("${AppNavItem.SettingsView.route}/${settingsView.route}")
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.owenlejeune.tvtime.ui.screens.main
|
package com.owenlejeune.tvtime.ui.screens
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
@@ -30,7 +30,7 @@ import org.koin.core.component.inject
|
|||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun WebLinkView(
|
fun WebLinkScreen(
|
||||||
url: String,
|
url: String,
|
||||||
appNavController: NavController
|
appNavController: NavController
|
||||||
) {
|
) {
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.owenlejeune.tvtime.ui.screens.main
|
package com.owenlejeune.tvtime.ui.screens.tabs
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
@@ -15,10 +15,11 @@ import com.google.accompanist.pager.rememberPagerState
|
|||||||
import com.owenlejeune.tvtime.R
|
import com.owenlejeune.tvtime.R
|
||||||
import com.owenlejeune.tvtime.ui.components.PagingPosterGrid
|
import com.owenlejeune.tvtime.ui.components.PagingPosterGrid
|
||||||
import com.owenlejeune.tvtime.ui.components.SearchView
|
import com.owenlejeune.tvtime.ui.components.SearchView
|
||||||
import com.owenlejeune.tvtime.ui.navigation.MainNavItem
|
import com.owenlejeune.tvtime.ui.navigation.AppNavItem
|
||||||
import com.owenlejeune.tvtime.ui.navigation.MediaTabNavItem
|
import com.owenlejeune.tvtime.ui.navigation.MediaTabNavItem
|
||||||
import com.owenlejeune.tvtime.ui.screens.main.tabs.top.Tabs
|
import com.owenlejeune.tvtime.ui.components.Tabs
|
||||||
import com.owenlejeune.tvtime.api.tmdb.viewmodel.MediaTabViewModel
|
import com.owenlejeune.tvtime.api.tmdb.viewmodel.MediaTabViewModel
|
||||||
|
import com.owenlejeune.tvtime.utils.types.MediaViewType
|
||||||
|
|
||||||
@OptIn(ExperimentalPagerApi::class)
|
@OptIn(ExperimentalPagerApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
@@ -72,7 +73,7 @@ fun MediaTabContent(appNavController: NavHostController, mediaType: MediaViewTyp
|
|||||||
lazyPagingItems = mediaListItems,
|
lazyPagingItems = mediaListItems,
|
||||||
onClick = { id ->
|
onClick = { id ->
|
||||||
appNavController.navigate(
|
appNavController.navigate(
|
||||||
"${MainNavItem.DetailView.route}/${mediaType}/${id}"
|
"${AppNavItem.DetailView.route}/${mediaType}/${id}"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -1,20 +1,18 @@
|
|||||||
package com.owenlejeune.tvtime.ui.screens.main
|
package com.owenlejeune.tvtime.ui.screens.tabs
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.MutableState
|
import androidx.compose.runtime.MutableState
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import androidx.paging.compose.collectAsLazyPagingItems
|
import androidx.paging.compose.collectAsLazyPagingItems
|
||||||
import com.owenlejeune.tvtime.R
|
import com.owenlejeune.tvtime.R
|
||||||
import com.owenlejeune.tvtime.ui.components.PagingPeoplePosterGrid
|
import com.owenlejeune.tvtime.ui.components.PagingPeoplePosterGrid
|
||||||
import com.owenlejeune.tvtime.ui.components.SearchView
|
import com.owenlejeune.tvtime.ui.components.SearchView
|
||||||
import com.owenlejeune.tvtime.ui.navigation.MainNavItem
|
import com.owenlejeune.tvtime.ui.navigation.AppNavItem
|
||||||
import com.owenlejeune.tvtime.api.tmdb.viewmodel.PeopleTabViewModel
|
import com.owenlejeune.tvtime.api.tmdb.viewmodel.PeopleTabViewModel
|
||||||
|
import com.owenlejeune.tvtime.utils.types.MediaViewType
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun PeopleTab(
|
fun PeopleTab(
|
||||||
@@ -45,7 +43,7 @@ fun PeopleTab(
|
|||||||
},
|
},
|
||||||
onClick = { id ->
|
onClick = { id ->
|
||||||
appNavController.navigate(
|
appNavController.navigate(
|
||||||
"${MainNavItem.DetailView.route}/${MediaViewType.PERSON}/${id}"
|
"${AppNavItem.DetailView.route}/${MediaViewType.PERSON}/${id}"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -17,7 +17,7 @@ import androidx.compose.ui.res.painterResource
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.owenlejeune.tvtime.ui.navigation.BottomNavItem
|
import com.owenlejeune.tvtime.ui.navigation.HomeScreenNavItem
|
||||||
import org.koin.core.component.KoinComponent
|
import org.koin.core.component.KoinComponent
|
||||||
import org.koin.core.component.get
|
import org.koin.core.component.get
|
||||||
|
|
||||||
@@ -27,14 +27,14 @@ class HomeTabRecyclerAdapter: RecyclerView.Adapter<HomeTabRecyclerAdapter.TabVie
|
|||||||
|
|
||||||
class TabViewHolder(itemView: ComposeView): RecyclerView.ViewHolder(itemView)
|
class TabViewHolder(itemView: ComposeView): RecyclerView.ViewHolder(itemView)
|
||||||
|
|
||||||
private val pages: MutableList<BottomNavItem?>
|
private val pages: MutableList<HomeScreenNavItem?>
|
||||||
private val indexOfDivider
|
private val indexOfDivider
|
||||||
get() = pages.indexOf(null)
|
get() = pages.indexOf(null)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val visiblePages = BottomNavItem.Items.filter { it.order > -1 }.sortedBy { it.order }
|
val visiblePages = HomeScreenNavItem.Items.filter { it.order > -1 }.sortedBy { it.order }
|
||||||
val hiddenPages = BottomNavItem.Items.filter { it.order == -1 }
|
val hiddenPages = HomeScreenNavItem.Items.filter { it.order == -1 }
|
||||||
pages = ArrayList<BottomNavItem?>().apply {
|
pages = ArrayList<HomeScreenNavItem?>().apply {
|
||||||
addAll(visiblePages)
|
addAll(visiblePages)
|
||||||
add(null)
|
add(null)
|
||||||
addAll(hiddenPages)
|
addAll(hiddenPages)
|
||||||
@@ -67,7 +67,7 @@ class HomeTabRecyclerAdapter: RecyclerView.Adapter<HomeTabRecyclerAdapter.TabVie
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun ItemRow(page: BottomNavItem) {
|
private fun ItemRow(page: HomeScreenNavItem) {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.height(50.dp),
|
.height(50.dp),
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import com.owenlejeune.tvtime.api.tmdb.api.v4.model.AuthDeleteBody
|
|||||||
import com.owenlejeune.tvtime.api.tmdb.api.v4.model.AuthRequestBody
|
import com.owenlejeune.tvtime.api.tmdb.api.v4.model.AuthRequestBody
|
||||||
import com.owenlejeune.tvtime.api.tmdb.api.v4.model.AccountList
|
import com.owenlejeune.tvtime.api.tmdb.api.v4.model.AccountList
|
||||||
import com.owenlejeune.tvtime.preferences.AppPreferences
|
import com.owenlejeune.tvtime.preferences.AppPreferences
|
||||||
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
|
import com.owenlejeune.tvtime.utils.types.MediaViewType
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.owenlejeune.tvtime.ui.screens.main
|
package com.owenlejeune.tvtime.utils.types
|
||||||
|
|
||||||
import com.google.gson.annotations.SerializedName
|
import com.google.gson.annotations.SerializedName
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.owenlejeune.tvtime.ui.navigation
|
package com.owenlejeune.tvtime.utils.types
|
||||||
|
|
||||||
import org.koin.core.component.KoinComponent
|
import org.koin.core.component.KoinComponent
|
||||||
|
|
||||||
Reference in New Issue
Block a user