refactor navigation

This commit is contained in:
Owen LeJeune
2023-06-11 12:54:24 -04:00
parent 7130930564
commit 97d7fbd841
52 changed files with 725 additions and 708 deletions

View File

@@ -1,59 +1,25 @@
package com.owenlejeune.tvtime
import android.os.Bundle
import androidx.activity.compose.BackHandler
import androidx.activity.compose.setContent
import androidx.compose.animation.rememberSplineBasedDecay
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
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.foundation.layout.Box
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.ui.platform.LocalContext
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.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.navArgument
import androidx.navigation.navDeepLink
import com.google.accompanist.systemuicontroller.rememberSystemUiController
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.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.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.navigation.AppNavigationHost
import com.owenlejeune.tvtime.ui.navigation.HomeScreenNavItem
import com.owenlejeune.tvtime.ui.theme.TVTimeTheme
import com.owenlejeune.tvtime.utils.KeyboardManager
import com.owenlejeune.tvtime.utils.NavConstants
import com.owenlejeune.tvtime.utils.SessionManager
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.koin.java.KoinJavaComponent.get
@OptIn(ExperimentalMaterial3Api::class)
class MainActivity : MonetCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
@@ -63,7 +29,7 @@ class MainActivity : MonetCompatActivity() {
SessionManager.initialize()
}
val mainNavStartRoute = BottomNavItem.SortedItems[0].route
val mainNavStartRoute = HomeScreenNavItem.SortedItems[0].route
lifecycleScope.launchWhenCreated {
monet.awaitMonetReady()
@@ -73,7 +39,7 @@ class MainActivity : MonetCompatActivity() {
val windowSize = rememberWindowSizeClass()
val appNavController = rememberNavController()
Box {
MainNavigationRoutes(
AppNavigationHost(
appNavController = appNavController,
mainNavStartRoute = mainNavStartRoute,
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
private fun AppKeyboardFocusManager() {
val context = LocalContext.current

View File

@@ -1,13 +1,12 @@
package com.owenlejeune.tvtime.api.tmdb.api.v3.deserializer
import com.google.gson.Gson
import com.google.gson.JsonObject
import com.google.gson.JsonParseException
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.KnownForMovie
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>() {

View File

@@ -1,6 +1,5 @@
package com.owenlejeune.tvtime.api.tmdb.api.v3.deserializer
import com.google.gson.Gson
import com.google.gson.JsonObject
import com.google.gson.JsonParseException
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.SearchResultTv
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>() {

View File

@@ -1,7 +1,7 @@
package com.owenlejeune.tvtime.api.tmdb.api.v3.model
import com.google.gson.annotations.SerializedName
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
import com.owenlejeune.tvtime.utils.types.MediaViewType
class DetailCast(
@SerializedName("id") val id: Int,

View File

@@ -1,7 +1,7 @@
package com.owenlejeune.tvtime.api.tmdb.api.v3.model
import com.google.gson.annotations.SerializedName
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
import com.owenlejeune.tvtime.utils.types.MediaViewType
class DetailCrew(
@SerializedName("id") val id: Int,

View File

@@ -1,7 +1,7 @@
package com.owenlejeune.tvtime.api.tmdb.api.v3.model
import com.google.gson.annotations.SerializedName
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
import com.owenlejeune.tvtime.utils.types.MediaViewType
abstract class KnownFor(
@SerializedName("backdrop_path") val backdropPath: String?,

View File

@@ -1,7 +1,7 @@
package com.owenlejeune.tvtime.api.tmdb.api.v3.model
import com.google.gson.annotations.SerializedName
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
import com.owenlejeune.tvtime.utils.types.MediaViewType
class KnownForMovie(
backdropPath: String?,

View File

@@ -1,7 +1,7 @@
package com.owenlejeune.tvtime.api.tmdb.api.v3.model
import com.google.gson.annotations.SerializedName
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
import com.owenlejeune.tvtime.utils.types.MediaViewType
class KnownForTv(
backdropPath: String?,

View File

@@ -1,7 +1,7 @@
package com.owenlejeune.tvtime.api.tmdb.api.v3.model
import com.google.gson.annotations.SerializedName
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
import com.owenlejeune.tvtime.utils.types.MediaViewType
class MarkAsFavoriteBody(
@SerializedName("media_type") val mediaType: MediaViewType,

View File

@@ -4,7 +4,7 @@ import androidx.paging.PagingSource
import androidx.paging.PagingState
import com.owenlejeune.tvtime.api.tmdb.api.v4.AccountV4Service
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.inject

View File

@@ -1,7 +1,7 @@
package com.owenlejeune.tvtime.api.tmdb.api.v3.model
import com.google.gson.annotations.SerializedName
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
import com.owenlejeune.tvtime.utils.types.MediaViewType
abstract class SearchResultMedia(
@SerializedName("overview") val overview: String,

View File

@@ -1,7 +1,7 @@
package com.owenlejeune.tvtime.api.tmdb.api.v3.model
import com.google.gson.annotations.SerializedName
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
import com.owenlejeune.tvtime.utils.types.MediaViewType
class SearchResultMovie(
id: Int,

View File

@@ -1,7 +1,7 @@
package com.owenlejeune.tvtime.api.tmdb.api.v3.model
import com.google.gson.annotations.SerializedName
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
import com.owenlejeune.tvtime.utils.types.MediaViewType
class SearchResultPerson(
@SerializedName("profile_path") val profilePath: String,

View File

@@ -1,7 +1,7 @@
package com.owenlejeune.tvtime.api.tmdb.api.v3.model
import com.google.gson.annotations.SerializedName
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
import com.owenlejeune.tvtime.utils.types.MediaViewType
class SearchResultTv(
id: Int,

View File

@@ -1,7 +1,7 @@
package com.owenlejeune.tvtime.api.tmdb.api.v3.model
import com.google.gson.annotations.SerializedName
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
import com.owenlejeune.tvtime.utils.types.MediaViewType
abstract class SortableSearchResult(
@SerializedName("media_type") val mediaType: MediaViewType,

View File

@@ -1,7 +1,7 @@
package com.owenlejeune.tvtime.api.tmdb.api.v3.model
import com.google.gson.annotations.SerializedName
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
import com.owenlejeune.tvtime.utils.types.MediaViewType
class WatchlistBody(
@SerializedName("media_type") val mediaType: MediaViewType,

View File

@@ -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.ListMovie
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>() {

View File

@@ -1,7 +1,7 @@
package com.owenlejeune.tvtime.api.tmdb.api.v4.model
import com.google.gson.annotations.SerializedName
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
import com.owenlejeune.tvtime.utils.types.MediaViewType
class AddToListBody(
@SerializedName("items") val items: List<AddToListBodyItem>

View File

@@ -1,7 +1,7 @@
package com.owenlejeune.tvtime.api.tmdb.api.v4.model
import com.google.gson.annotations.SerializedName
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
import com.owenlejeune.tvtime.utils.types.MediaViewType
class DeleteListItemsBody(
@SerializedName("items") val items: List<DeleteListItemsItem>

View File

@@ -1,7 +1,7 @@
package com.owenlejeune.tvtime.api.tmdb.api.v4.model
import com.google.gson.annotations.SerializedName
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
import com.owenlejeune.tvtime.utils.types.MediaViewType
abstract class ListItem(
@SerializedName("backdrop_path") val backdropPath: String?,

View File

@@ -1,7 +1,7 @@
package com.owenlejeune.tvtime.api.tmdb.api.v4.model
import com.google.gson.annotations.SerializedName
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
import com.owenlejeune.tvtime.utils.types.MediaViewType
class ListItemStatusResponse(
statusMessage: String,

View File

@@ -1,7 +1,7 @@
package com.owenlejeune.tvtime.api.tmdb.api.v4.model
import com.google.gson.annotations.SerializedName
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
import com.owenlejeune.tvtime.utils.types.MediaViewType
class ListMovie(
backdropPath: String?,

View File

@@ -1,7 +1,7 @@
package com.owenlejeune.tvtime.api.tmdb.api.v4.model
import com.google.gson.annotations.SerializedName
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
import com.owenlejeune.tvtime.utils.types.MediaViewType
class ListTv(
backdropPath: String?,

View File

@@ -1,7 +1,7 @@
package com.owenlejeune.tvtime.api.tmdb.api.v4.model
import com.google.gson.annotations.SerializedName
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
import com.owenlejeune.tvtime.utils.types.MediaViewType
class UpdateListItemBody(
@SerializedName("items") val items: List<UpdateListItemBodyItem>

View File

@@ -8,7 +8,7 @@ import androidx.paging.PagingData
import androidx.paging.cachedIn
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.RecommendedMediaPagingSource
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 org.koin.core.component.KoinComponent

View File

@@ -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.background
@@ -25,13 +25,9 @@ import com.google.accompanist.pager.PagerState
import com.google.accompanist.pager.rememberPagerState
import com.owenlejeune.tvtime.R
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 kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@OptIn(ExperimentalPagerApi::class)
@Composable

View File

@@ -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.clickable
@@ -12,7 +12,6 @@ import androidx.compose.ui.draw.blur
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
@@ -22,7 +21,8 @@ import androidx.constraintlayout.compose.Dimension
import androidx.navigation.NavController
import coil.compose.AsyncImage
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)
@Composable
@@ -45,7 +45,7 @@ fun MediaResultCard(
.clickable(
onClick = {
appNavController.navigate(
"${MainNavItem.DetailView.route}/${mediaViewType}/${id}"
"${AppNavItem.DetailView.route}/${mediaViewType}/${id}"
)
}
)

View File

@@ -39,14 +39,13 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
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.sp
import androidx.navigation.NavController
import com.google.accompanist.systemuicontroller.rememberSystemUiController
import com.owenlejeune.tvtime.BuildConfig
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 kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -130,7 +129,7 @@ fun ProfileMenuOverlay(
ProfileMenuItem(
onClick = {
onDismissRequest()
appNavController.navigate(MainNavItem.AccountView.route)
appNavController.navigate(AppNavItem.AccountView.route)
}
) {
Icon(
@@ -147,7 +146,7 @@ fun ProfileMenuOverlay(
ProfileMenuItem(
onClick = {
onDismissRequest()
appNavController.navigate(MainNavItem.SettingsView.route)
appNavController.navigate(AppNavItem.SettingsView.route)
}
) {
Icon(
@@ -163,7 +162,7 @@ fun ProfileMenuOverlay(
ProfileMenuItem(
onClick = {
onDismissRequest()
appNavController.navigate(MainNavItem.AboutView.route)
appNavController.navigate(AppNavItem.AboutView.route)
}
) {
Icon(
@@ -187,7 +186,7 @@ fun ProfileMenuOverlay(
} else {
SessionManager.signInPart1(context) {
appNavController.navigate(
MainNavItem.WebLinkView.route.plus("/$it")
AppNavItem.WebLinkView.route.plus("/$it")
)
}
}

View File

@@ -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.layout.Spacer
@@ -17,14 +17,11 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.google.accompanist.pager.ExperimentalPagerApi
import com.google.accompanist.pager.PagerState
import com.google.accompanist.pager.pagerTabIndicatorOffset
import com.google.accompanist.pager.rememberPagerState
import com.owenlejeune.tvtime.ui.navigation.MediaTabNavItem
import com.owenlejeune.tvtime.ui.navigation.TabNavItem
import com.owenlejeune.tvtime.utils.types.TabNavItem
import kotlinx.coroutines.launch
@OptIn(ExperimentalPagerApi::class)

View File

@@ -17,7 +17,6 @@ import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
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.ArrowDropUp
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.extensions.unlessEmpty
import com.owenlejeune.tvtime.preferences.AppPreferences
import com.owenlejeune.tvtime.ui.navigation.MainNavItem
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
import com.owenlejeune.tvtime.ui.navigation.AppNavItem
import com.owenlejeune.tvtime.utils.types.MediaViewType
import com.owenlejeune.tvtime.utils.SessionManager
import com.owenlejeune.tvtime.utils.TmdbUtils
import kotlinx.coroutines.delay
@@ -211,7 +210,7 @@ fun SearchView(
fab: MutableState<@Composable () -> Unit>,
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) {
SearchBar(
placeholder = title

View File

@@ -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.WatchlistTvSeries
import com.owenlejeune.tvtime.api.tmdb.api.v4.model.AccountList
import com.owenlejeune.tvtime.ui.screens.main.AccountTabContent
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
import com.owenlejeune.tvtime.ui.screens.main.RecommendedAccountTabContent
import com.owenlejeune.tvtime.ui.screens.AccountTabContent
import com.owenlejeune.tvtime.utils.types.MediaViewType
import com.owenlejeune.tvtime.ui.screens.RecommendedAccountTabContent
import com.owenlejeune.tvtime.utils.ResourceUtils
import com.owenlejeune.tvtime.utils.SessionManager
import com.owenlejeune.tvtime.utils.types.TabNavItem
import org.koin.core.component.inject
import kotlin.reflect.KClass

View File

@@ -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")
}

View File

@@ -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 }
)
}

View File

@@ -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 }
)
}

View File

@@ -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")
}

View File

@@ -5,10 +5,11 @@ import androidx.navigation.NavHostController
import com.owenlejeune.tvtime.R
import com.owenlejeune.tvtime.api.tmdb.api.v3.HomePageService
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.HomePageResponse
import com.owenlejeune.tvtime.ui.screens.main.MediaTabContent
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
import com.owenlejeune.tvtime.utils.types.MediaViewType
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.types.TabNavItem
import org.koin.core.component.inject
import retrofit2.Response

View File

@@ -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
)
}
}
}

View File

@@ -1,4 +1,4 @@
package com.owenlejeune.tvtime.ui.screens.main
package com.owenlejeune.tvtime.ui.screens
import android.content.Intent
import android.net.Uri
@@ -58,7 +58,7 @@ import dev.jeziellago.compose.markdowntext.MarkdownText
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AboutView(
fun AboutScreen(
appNavController: NavController
) {
val systemUiController = rememberSystemUiController()

View File

@@ -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.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.navigation.AccountTabNavItem
import com.owenlejeune.tvtime.ui.navigation.ListFetchFun
import com.owenlejeune.tvtime.ui.navigation.MainNavItem
import com.owenlejeune.tvtime.ui.screens.main.tabs.top.ScrollableTabs
import com.owenlejeune.tvtime.ui.navigation.AppNavItem
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.TmdbUtils
import kotlinx.coroutines.launch
@@ -45,7 +47,7 @@ import kotlin.reflect.KClass
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AccountView(
fun AccountScreen(
appNavController: NavHostController,
doSignInPartTwo: Boolean = false
) {
@@ -303,7 +305,7 @@ fun RecommendedAccountTabContent(
lazyPagingItems = mediaListItems,
onClick = { id ->
appNavController.navigate(
"${MainNavItem.DetailView.route}/${mediaViewType}/${id}"
"${AppNavItem.DetailView.route}/${mediaViewType}/${id}"
)
}
)

View File

@@ -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
)
}
}

View File

@@ -1,4 +1,4 @@
package com.owenlejeune.tvtime.ui.screens.main
package com.owenlejeune.tvtime.ui.screens
import android.content.Context
import android.content.Intent
import android.util.Log
@@ -45,7 +45,9 @@ import com.owenlejeune.tvtime.extensions.unlessEmpty
import com.owenlejeune.tvtime.preferences.AppPreferences
import com.owenlejeune.tvtime.ui.components.Spinner
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.utils.SessionManager
import com.owenlejeune.tvtime.utils.TmdbUtils
@@ -60,7 +62,7 @@ import kotlin.math.roundToInt
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ListDetailView(
fun ListDetailScreen(
appNavController: NavController,
itemId: Int?,
windowSize: WindowSizeClass,
@@ -457,7 +459,7 @@ private fun ListItemView(
.clickable(
onClick = {
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),
isSelected = isFavourited,
filledIconColor = FavoriteSelected,
onClick = ::addToFavorite
onClick = ::listAddToFavorite
)
ActionButton(
@@ -585,7 +587,7 @@ private fun ActionButtonRow(listItem: ListItem) {
contentDescription = "",
isSelected = isWatchlisted,
filledIconColor = WatchlistSelected,
onClick = ::addToWatchlist
onClick = ::listAddToWatchlist
)
val context = LocalContext.current
@@ -604,7 +606,7 @@ private fun ActionButtonRow(listItem: ListItem) {
}
}
private fun addToWatchlist(
private fun listAddToWatchlist(
context: Context,
itemId: Int,
type: MediaViewType,
@@ -629,7 +631,7 @@ private fun addToWatchlist(
}
}
private fun addToFavorite(
private fun listAddToFavorite(
context: Context,
itemId: Int,
type: MediaViewType,

View File

@@ -1,11 +1,9 @@
package com.owenlejeune.tvtime.ui.screens.main
package com.owenlejeune.tvtime.ui.screens
import android.content.Context
import android.widget.Toast
import androidx.compose.animation.*
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.foundation.*
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.Movie
import androidx.compose.material.icons.filled.Send
import androidx.compose.material.icons.outlined.ExpandLess
import androidx.compose.material.icons.outlined.ExpandMore
import androidx.compose.material3.*
import androidx.compose.runtime.*
@@ -31,7 +28,6 @@ import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
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.FontWeight
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.preferences.AppPreferences
import com.owenlejeune.tvtime.ui.components.*
import com.owenlejeune.tvtime.ui.navigation.MainNavItem
import com.owenlejeune.tvtime.ui.navigation.TabNavItem
import com.owenlejeune.tvtime.ui.screens.main.tabs.top.Tabs
import com.owenlejeune.tvtime.ui.navigation.AppNavItem
import com.owenlejeune.tvtime.utils.types.TabNavItem
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.RatingSelected
import com.owenlejeune.tvtime.ui.theme.WatchlistSelected
@@ -71,7 +69,7 @@ import java.text.DecimalFormat
@OptIn(ExperimentalMaterial3Api::class, ExperimentalPagerApi::class)
@Composable
fun MediaDetailView(
fun MediaDetailScreen(
appNavController: NavController,
itemId: Int?,
type: MediaViewType,
@@ -780,7 +778,7 @@ fun FavoriteButton(
contentDescription = "",
isSelected = isFavourited,
filledIconColor = FavoriteSelected,
onClick = ::addToFavorite
onClick = ::mediaAddToFavorite
)
}
@@ -1140,7 +1138,7 @@ private fun CastCrewCard(appNavController: NavController, person: Person) {
subtitleTextColor = MaterialTheme.colorScheme.onSecondary,
onItemClicked = {
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),
onItemClicked = {
appNavController.navigate(
"${MainNavItem.DetailView.route}/${mediaType}/${content.id}"
"${AppNavItem.DetailView.route}/${mediaType}/${content.id}"
)
},
placeholder = Icons.Filled.Movie
@@ -1573,7 +1571,7 @@ private fun addToWatchlist(
}
}
private fun addToFavorite(
private fun mediaAddToFavorite(
context: Context,
itemId: Int,
type: MediaViewType,

View File

@@ -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.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.ExpandableContentCard
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 kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -37,7 +39,7 @@ import kotlinx.coroutines.withContext
@OptIn(ExperimentalMaterial3Api::class, ExperimentalPagerApi::class)
@Composable
fun PersonDetailView(
fun PersonDetailScreen(
appNavController: NavController,
personId: Int?
) {
@@ -125,7 +127,7 @@ fun PersonDetailView(
onItemClicked = {
personId?.let {
appNavController.navigate(
"${MainNavItem.DetailView.route}/${content.mediaType}/${content.id}"
"${AppNavItem.DetailView.route}/${content.mediaType}/${content.id}"
)
}
}
@@ -171,7 +173,7 @@ fun PersonDetailView(
onItemClicked = {
personId?.let {
appNavController.navigate(
"${MainNavItem.DetailView.route}/${content.mediaType}/${content.id}"
"${AppNavItem.DetailView.route}/${content.mediaType}/${content.id}"
)
}
}

View File

@@ -1,11 +1,8 @@
package com.owenlejeune.tvtime.ui.screens
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
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.filled.ArrowBack
import androidx.compose.material.icons.filled.Clear
@@ -14,17 +11,13 @@ import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
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.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.navigation.NavHostController
import coil.compose.AsyncImage
import com.google.accompanist.systemuicontroller.rememberSystemUiController
import com.owenlejeune.tvtime.R
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.model.*
import com.owenlejeune.tvtime.extensions.listItems
import com.owenlejeune.tvtime.ui.navigation.MainNavItem
import com.owenlejeune.tvtime.ui.screens.main.MediaResultCard
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
import com.owenlejeune.tvtime.ui.components.MediaResultCard
import com.owenlejeune.tvtime.utils.types.MediaViewType
import com.owenlejeune.tvtime.utils.TmdbUtils
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers

View File

@@ -1,4 +1,4 @@
package com.owenlejeune.tvtime.ui.screens.main
package com.owenlejeune.tvtime.ui.screens
import android.os.Build
import android.widget.Toast
@@ -37,7 +37,7 @@ import com.owenlejeune.tvtime.OnboardingActivity
import com.owenlejeune.tvtime.R
import com.owenlejeune.tvtime.preferences.AppPreferences
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.ItemMoveCallback
import com.owenlejeune.tvtime.utils.ResourceUtils
@@ -49,9 +49,8 @@ import org.koin.java.KoinJavaComponent.get
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SettingsTab(
fun SettingsScreen(
appNavController: NavController,
activity: AppCompatActivity,
route: String? = null,
preferences: AppPreferences = get(AppPreferences::class.java)
) {
@@ -110,6 +109,7 @@ fun SettingsTab(
.padding(all = 24.dp),
verticalArrangement = Arrangement.spacedBy(18.dp)
) {
val activity = LocalContext.current as AppCompatActivity
SettingsPage.getByRoute(route).apply {
appBarTitle.value = name
restoreAction.value = resetPreferencesHandler
@@ -188,7 +188,7 @@ private fun TopLevelSettingsCard(
.wrapContentHeight()
.clickable(
onClick = {
appNavController.navigate("${MainNavItem.SettingsView.route}/${settingsView.route}")
appNavController.navigate("${AppNavItem.SettingsView.route}/${settingsView.route}")
}
)
) {

View File

@@ -1,4 +1,4 @@
package com.owenlejeune.tvtime.ui.screens.main
package com.owenlejeune.tvtime.ui.screens
import android.content.Context
import android.content.Intent
@@ -30,7 +30,7 @@ import org.koin.core.component.inject
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun WebLinkView(
fun WebLinkScreen(
url: String,
appNavController: NavController
) {

View File

@@ -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.material3.Text
@@ -15,10 +15,11 @@ import com.google.accompanist.pager.rememberPagerState
import com.owenlejeune.tvtime.R
import com.owenlejeune.tvtime.ui.components.PagingPosterGrid
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.screens.main.tabs.top.Tabs
import com.owenlejeune.tvtime.ui.components.Tabs
import com.owenlejeune.tvtime.api.tmdb.viewmodel.MediaTabViewModel
import com.owenlejeune.tvtime.utils.types.MediaViewType
@OptIn(ExperimentalPagerApi::class)
@Composable
@@ -72,7 +73,7 @@ fun MediaTabContent(appNavController: NavHostController, mediaType: MediaViewTyp
lazyPagingItems = mediaListItems,
onClick = { id ->
appNavController.navigate(
"${MainNavItem.DetailView.route}/${mediaType}/${id}"
"${AppNavItem.DetailView.route}/${mediaType}/${id}"
)
}
)

View File

@@ -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.padding
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.navigation.NavHostController
import androidx.paging.compose.collectAsLazyPagingItems
import com.owenlejeune.tvtime.R
import com.owenlejeune.tvtime.ui.components.PagingPeoplePosterGrid
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.utils.types.MediaViewType
@Composable
fun PeopleTab(
@@ -45,7 +43,7 @@ fun PeopleTab(
},
onClick = { id ->
appNavController.navigate(
"${MainNavItem.DetailView.route}/${MediaViewType.PERSON}/${id}"
"${AppNavItem.DetailView.route}/${MediaViewType.PERSON}/${id}"
)
}
)

View File

@@ -17,7 +17,7 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
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.get
@@ -27,14 +27,14 @@ class HomeTabRecyclerAdapter: RecyclerView.Adapter<HomeTabRecyclerAdapter.TabVie
class TabViewHolder(itemView: ComposeView): RecyclerView.ViewHolder(itemView)
private val pages: MutableList<BottomNavItem?>
private val pages: MutableList<HomeScreenNavItem?>
private val indexOfDivider
get() = pages.indexOf(null)
init {
val visiblePages = BottomNavItem.Items.filter { it.order > -1 }.sortedBy { it.order }
val hiddenPages = BottomNavItem.Items.filter { it.order == -1 }
pages = ArrayList<BottomNavItem?>().apply {
val visiblePages = HomeScreenNavItem.Items.filter { it.order > -1 }.sortedBy { it.order }
val hiddenPages = HomeScreenNavItem.Items.filter { it.order == -1 }
pages = ArrayList<HomeScreenNavItem?>().apply {
addAll(visiblePages)
add(null)
addAll(hiddenPages)
@@ -67,7 +67,7 @@ class HomeTabRecyclerAdapter: RecyclerView.Adapter<HomeTabRecyclerAdapter.TabVie
}
@Composable
private fun ItemRow(page: BottomNavItem) {
private fun ItemRow(page: HomeScreenNavItem) {
Row(
modifier = Modifier
.height(50.dp),

View File

@@ -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.AccountList
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.Dispatchers
import kotlinx.coroutines.launch

View File

@@ -1,4 +1,4 @@
package com.owenlejeune.tvtime.ui.screens.main
package com.owenlejeune.tvtime.utils.types
import com.google.gson.annotations.SerializedName

View File

@@ -1,4 +1,4 @@
package com.owenlejeune.tvtime.ui.navigation
package com.owenlejeune.tvtime.utils.types
import org.koin.core.component.KoinComponent