Skip to content

Commit 80c9b83

Browse files
Use androidx.navigation for tabs
1 parent 7676548 commit 80c9b83

File tree

8 files changed

+91
-51
lines changed

8 files changed

+91
-51
lines changed

app/build.gradle.kts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ plugins {
33
alias(libs.plugins.kotlin.android)
44
alias(libs.plugins.kotlin.compose)
55
alias(libs.plugins.sqldelight)
6+
alias(libs.plugins.kotlin.serialization)
67
}
78

89
kotlin {
@@ -40,19 +41,21 @@ sqldelight {
4041
dependencies {
4142
// System
4243
implementation(libs.bundles.androidx.core)
43-
implementation(libs.androidx.tvprovider)
4444
implementation(libs.bundles.koin)
45+
implementation(libs.androidx.tvprovider)
4546
implementation(libs.timber)
4647

4748
// Data
4849
implementation(libs.bundles.sqldelight)
50+
implementation(libs.kotlinx.serialization.json)
4951

5052
// UI
51-
implementation(libs.androidx.appcompat)
52-
implementation(libs.androidx.activity.compose)
5353
implementation(libs.bundles.androidx.compose)
54-
debugImplementation(libs.androidx.compose.ui.tooling)
55-
implementation(libs.androidx.tv.material)
54+
implementation(libs.androidx.activity.compose)
55+
implementation(libs.androidx.appcompat)
56+
implementation(libs.androidx.navigation.compose)
5657
implementation(libs.androidx.palette)
58+
implementation(libs.androidx.tv.material)
5759
implementation(libs.coil.compose)
60+
debugImplementation(libs.androidx.compose.ui.tooling)
5861
}

app/src/main/kotlin/nl/ndat/tvlauncher/LauncherApplication.kt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import nl.ndat.tvlauncher.data.repository.InputRepository
1111
import nl.ndat.tvlauncher.data.resolver.AppResolver
1212
import nl.ndat.tvlauncher.data.resolver.ChannelResolver
1313
import nl.ndat.tvlauncher.data.resolver.InputResolver
14-
import nl.ndat.tvlauncher.ui.screen.launcher.LauncherScreenViewModel
1514
import nl.ndat.tvlauncher.ui.tab.apps.AppsTabViewModel
1615
import nl.ndat.tvlauncher.ui.tab.home.HomeTabViewModel
1716
import nl.ndat.tvlauncher.util.DefaultLauncherHelper
@@ -35,7 +34,6 @@ private val launcherModule = module {
3534
single { InputRepository(get(), get(), get()) }
3635
single { InputResolver() }
3736

38-
viewModel { LauncherScreenViewModel() }
3937
viewModel { HomeTabViewModel(get(), get()) }
4038
viewModel { AppsTabViewModel(get()) }
4139
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package nl.ndat.tvlauncher.data
2+
3+
import androidx.navigation.NavGraphBuilder
4+
import androidx.navigation.compose.composable
5+
import kotlinx.serialization.Serializable
6+
import nl.ndat.tvlauncher.ui.tab.apps.AppsTab
7+
import nl.ndat.tvlauncher.ui.tab.home.HomeTab
8+
9+
object Destinations {
10+
@Serializable
11+
object Home
12+
13+
@Serializable
14+
object Apps
15+
}
16+
17+
val DefaultDestination = Destinations.Home
18+
19+
fun NavGraphBuilder.createDestinationsGraph() {
20+
composable<Destinations.Home> { HomeTab() }
21+
composable<Destinations.Apps> { AppsTab() }
22+
}
Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,30 @@
11
package nl.ndat.tvlauncher.ui.screen.launcher
22

3-
import androidx.compose.animation.AnimatedContent
43
import androidx.compose.foundation.layout.Column
54
import androidx.compose.foundation.layout.fillMaxSize
65
import androidx.compose.foundation.layout.padding
76
import androidx.compose.runtime.Composable
87
import androidx.compose.runtime.LaunchedEffect
9-
import androidx.compose.runtime.collectAsState
10-
import androidx.compose.runtime.getValue
118
import androidx.compose.runtime.remember
129
import androidx.compose.ui.Modifier
1310
import androidx.compose.ui.focus.FocusRequester
1411
import androidx.compose.ui.focus.focusRequester
1512
import androidx.compose.ui.unit.dp
16-
import nl.ndat.tvlauncher.ui.tab.apps.AppsTab
17-
import nl.ndat.tvlauncher.ui.tab.home.HomeTab
13+
import androidx.navigation.compose.NavHost
14+
import androidx.navigation.compose.rememberNavController
15+
import androidx.navigation.createGraph
16+
import nl.ndat.tvlauncher.data.DefaultDestination
17+
import nl.ndat.tvlauncher.data.createDestinationsGraph
1818
import nl.ndat.tvlauncher.ui.toolbar.Toolbar
19-
import org.koin.androidx.compose.koinViewModel
2019

2120
@Composable
2221
fun LauncherScreen() {
23-
val viewModel = koinViewModel<LauncherScreenViewModel>()
24-
val tabIndex by viewModel.tabIndex.collectAsState()
22+
val navController = rememberNavController()
23+
val navGraph = remember(navController) {
24+
navController.createGraph(DefaultDestination) {
25+
createDestinationsGraph()
26+
}
27+
}
2528

2629
Column(
2730
modifier = Modifier
@@ -33,21 +36,20 @@ fun LauncherScreen() {
3336
}
3437

3538
Toolbar(
39+
navController = navController,
40+
navGraph = navGraph,
3641
modifier = Modifier
3742
.padding(
3843
vertical = 27.dp,
3944
horizontal = 48.dp,
4045
)
4146
)
4247

43-
AnimatedContent(
44-
targetState = tabIndex,
48+
NavHost(
49+
navController = navController,
50+
graph = navGraph,
4551
modifier = Modifier
46-
.focusRequester(contentFocusRequester),
47-
label = "content"
48-
) { tabIndex ->
49-
if (tabIndex == 0) HomeTab()
50-
else if (tabIndex == 1) AppsTab()
51-
}
52+
.focusRequester(contentFocusRequester)
53+
)
5254
}
5355
}

app/src/main/kotlin/nl/ndat/tvlauncher/ui/screen/launcher/LauncherScreenViewModel.kt

Lines changed: 0 additions & 14 deletions
This file was deleted.

app/src/main/kotlin/nl/ndat/tvlauncher/ui/toolbar/Toolbar.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,14 @@ import androidx.compose.ui.focus.FocusRequester
1313
import androidx.compose.ui.focus.focusRequester
1414
import androidx.compose.ui.focus.focusRestorer
1515
import androidx.compose.ui.unit.dp
16+
import androidx.navigation.NavController
17+
import androidx.navigation.NavGraph
1618

1719
@OptIn(ExperimentalComposeUiApi::class)
1820
@Composable
1921
fun Toolbar(
22+
navController: NavController,
23+
navGraph: NavGraph,
2024
modifier: Modifier = Modifier,
2125
) {
2226
val focusRequester = remember { FocusRequester() }
@@ -29,6 +33,8 @@ fun Toolbar(
2933
verticalAlignment = Alignment.CenterVertically,
3034
) {
3135
ToolbarTabs(
36+
navController = navController,
37+
navGraph = navGraph,
3238
modifier = Modifier
3339
.focusRequester(focusRequester)
3440
)

app/src/main/kotlin/nl/ndat/tvlauncher/ui/toolbar/ToolbarTabs.kt

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,33 +10,49 @@ import androidx.compose.ui.Modifier
1010
import androidx.compose.ui.focus.focusRestorer
1111
import androidx.compose.ui.res.stringResource
1212
import androidx.compose.ui.unit.dp
13+
import androidx.navigation.NavController
14+
import androidx.navigation.NavGraph
15+
import androidx.navigation.get
1316
import androidx.tv.material3.Tab
1417
import androidx.tv.material3.TabRow
1518
import androidx.tv.material3.Text
19+
import kotlinx.coroutines.flow.map
1620
import nl.ndat.tvlauncher.R
17-
import nl.ndat.tvlauncher.ui.screen.launcher.LauncherScreenViewModel
18-
import org.koin.androidx.compose.koinViewModel
21+
import nl.ndat.tvlauncher.data.Destinations
1922

2023
@OptIn(ExperimentalComposeUiApi::class)
2124
@Composable
22-
fun ToolbarTabs(modifier: Modifier) {
23-
val viewModel = koinViewModel<LauncherScreenViewModel>()
24-
val selectedTabIndex by viewModel.tabIndex.collectAsState()
25-
26-
val tabs = listOf(
27-
stringResource(R.string.tab_home),
28-
stringResource(R.string.tab_apps),
25+
fun ToolbarTabs(
26+
navController: NavController,
27+
navGraph: NavGraph,
28+
modifier: Modifier,
29+
) {
30+
val currentDestinationId by navController.currentBackStackEntryFlow.map { it.destination.id }.collectAsState(null)
31+
val tabs = mapOf(
32+
Destinations.Home to stringResource(R.string.tab_home),
33+
Destinations.Apps to stringResource(R.string.tab_apps),
2934
)
3035

3136
TabRow(
32-
selectedTabIndex = selectedTabIndex,
37+
selectedTabIndex = tabs.keys.indexOfFirst { destination -> navGraph[destination].id == currentDestinationId },
3338
modifier = modifier.focusRestorer(),
3439
) {
35-
tabs.forEachIndexed { tabIndex, name ->
36-
key(tabIndex) {
40+
tabs.toList().forEachIndexed { index, (destination, name) ->
41+
key(index) {
42+
val selected = navGraph[destination].id == currentDestinationId
3743
Tab(
38-
selected = selectedTabIndex == tabIndex,
39-
onFocus = { viewModel.setTabIndex(tabIndex) },
44+
selected = selected,
45+
onFocus = {
46+
if (!selected) {
47+
navController.navigate(destination) {
48+
if (currentDestinationId != null) {
49+
popUpTo(currentDestinationId!!) {
50+
inclusive = true
51+
}
52+
}
53+
}
54+
}
55+
},
4056
modifier = Modifier.padding(16.dp, 8.dp)
4157
) {
4258
Text(name)

gradle/libs.versions.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ androidx-compose-foundation = "1.8.1"
99
androidx-compose-ui = "1.8.1"
1010
androidx-core = "1.16.0"
1111
androidx-core-role = "1.1.0"
12+
androidx-navigation = "2.9.0"
1213
androidx-palette = "1.0.0"
1314
androidx-tv = "1.0.0"
1415
androidx-tvprovider = "1.1.0"
@@ -17,13 +18,15 @@ java-jdk = "21"
1718
koin-android = "4.0.4"
1819
koin-compose = "4.0.4"
1920
kotlin = "2.1.20"
21+
kotlinx-serialization = "1.8.1"
2022
sqldelight = "2.0.2"
2123
timber = "5.0.1"
2224

2325
[plugins]
2426
android-app = { id = "com.android.application", version.ref = "android-plugin" }
2527
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
2628
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
29+
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
2730
sqldelight = { id = "app.cash.sqldelight", version.ref = "sqldelight" }
2831

2932
[libraries]
@@ -35,12 +38,16 @@ androidx-compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling", versi
3538
androidx-compose-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview", version.ref = "androidx-compose-ui" }
3639
androidx-core = { module = "androidx.core:core-ktx", version.ref = "androidx-core" }
3740
androidx-core-role = { module = "androidx.core:core-role", version.ref = "androidx-core-role" }
41+
androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "androidx-navigation" }
42+
androidx-navigation-fragment = { module = "androidx.navigation:navigation-fragment", version.ref = "androidx-navigation" }
43+
androidx-navigation-ui = { module = "androidx.navigation:navigation-ui", version.ref = "androidx-navigation" }
3844
androidx-palette = { module = "androidx.palette:palette-ktx", version.ref = "androidx-palette" }
3945
androidx-tv-material = { module = "androidx.tv:tv-material", version.ref = "androidx-tv" }
4046
androidx-tvprovider = { module = "androidx.tvprovider:tvprovider", version.ref = "androidx-tvprovider" }
4147
coil-compose = { module = "io.coil-kt:coil-compose", version.ref = "coil" }
4248
koin-android = { module = "io.insert-koin:koin-android", version.ref = "koin-android" }
4349
koin-androidx-compose = { module = "io.insert-koin:koin-androidx-compose", version.ref = "koin-compose" }
50+
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinx-serialization" }
4451
sqldelight-android = { module = "app.cash.sqldelight:android-driver", version.ref = "sqldelight" }
4552
sqldelight-coroutines = { module = "app.cash.sqldelight:coroutines-extensions", version.ref = "sqldelight" }
4653
timber = { module = "com.jakewharton.timber:timber", version.ref = "timber" }

0 commit comments

Comments
 (0)