Skip to content

Commit 98f6412

Browse files
committed
refactor: migrate to compose navigation from voyager
1 parent 1a5a9f5 commit 98f6412

19 files changed

+192
-102
lines changed

app/build.gradle.kts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ plugins {
1010
alias(libs.plugins.room)
1111
alias(libs.plugins.detekt)
1212
alias(libs.plugins.about.libraries)
13+
alias(libs.plugins.kotlinx.serialization)
1314
}
1415

1516
android {
@@ -47,6 +48,7 @@ android {
4748
getDefaultProguardFile("proguard-android-optimize.txt"),
4849
"proguard-rules.pro",
4950
)
51+
signingConfig = signingConfigs.getByName("debug")
5052
}
5153
create("preview") {
5254
initWith(getByName("release"))
@@ -113,6 +115,7 @@ dependencies {
113115
implementation(libs.androidx.material3.android)
114116
implementation(libs.androidx.ui.tooling.preview)
115117
debugImplementation(libs.androidx.ui.tooling)
118+
implementation(libs.navigation.compose)
116119
implementation(libs.androidx.appcompat)
117120
implementation(libs.androidx.compose.constraintlayout)
118121
implementation(libs.androidx.material3.icons.extended)
@@ -127,7 +130,6 @@ dependencies {
127130
implementation(libs.bundles.koin)
128131

129132
implementation(libs.seeker)
130-
implementation(libs.bundles.voyager)
131133
implementation(libs.compose.prefs)
132134
implementation(libs.bundles.about.libs)
133135
implementation(libs.simple.icons)
@@ -141,6 +143,7 @@ dependencies {
141143
detektPlugins(libs.detekt.formatter)
142144

143145
implementation(libs.kotlinx.immutable.collections)
146+
implementation(libs.kotlinx.serialization.json)
144147
implementation(libs.truetype.parser)
145148
implementation(libs.fsaf)
146149
}

app/src/main/AndroidManifest.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
android:allowBackup="true"
1010
android:appCategory="video"
1111
android:dataExtractionRules="@xml/data_extraction_rules"
12+
android:enableOnBackInvokedCallback="true"
1213
android:fullBackupContent="@xml/backup_rules"
1314
android:icon="@mipmap/ic_launcher"
1415
android:label="@string/app_name"

app/src/main/java/live/mehiz/mpvkt/MainActivity.kt

Lines changed: 66 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,41 @@ import androidx.activity.ComponentActivity
55
import androidx.activity.SystemBarStyle
66
import androidx.activity.compose.setContent
77
import androidx.activity.enableEdgeToEdge
8+
import androidx.compose.animation.core.tween
9+
import androidx.compose.animation.fadeIn
10+
import androidx.compose.animation.fadeOut
11+
import androidx.compose.animation.scaleIn
12+
import androidx.compose.animation.scaleOut
13+
import androidx.compose.animation.slideIn
14+
import androidx.compose.animation.slideOut
815
import androidx.compose.foundation.isSystemInDarkTheme
16+
import androidx.compose.runtime.Composable
17+
import androidx.compose.runtime.CompositionLocalProvider
918
import androidx.compose.runtime.getValue
1019
import androidx.compose.ui.graphics.Color
20+
import androidx.compose.ui.graphics.TransformOrigin
1121
import androidx.compose.ui.graphics.toArgb
12-
import cafe.adriel.voyager.navigator.Navigator
13-
import cafe.adriel.voyager.transitions.SlideTransition
22+
import androidx.compose.ui.unit.IntOffset
23+
import androidx.navigation.compose.NavHost
24+
import androidx.navigation.compose.composable
25+
import androidx.navigation.compose.rememberNavController
26+
import androidx.navigation.toRoute
1427
import live.mehiz.mpvkt.preferences.AppearancePreferences
1528
import live.mehiz.mpvkt.preferences.preference.collectAsState
29+
import live.mehiz.mpvkt.ui.home.FilePickerScreen
1630
import live.mehiz.mpvkt.ui.home.HomeScreen
31+
import live.mehiz.mpvkt.ui.preferences.AboutScreen
32+
import live.mehiz.mpvkt.ui.preferences.AdvancedPreferencesScreen
33+
import live.mehiz.mpvkt.ui.preferences.AppearancePreferencesScreen
34+
import live.mehiz.mpvkt.ui.preferences.AudioPreferencesScreen
35+
import live.mehiz.mpvkt.ui.preferences.DecoderPreferencesScreen
36+
import live.mehiz.mpvkt.ui.preferences.GesturePreferencesScreen
37+
import live.mehiz.mpvkt.ui.preferences.PlayerPreferencesScreen
38+
import live.mehiz.mpvkt.ui.preferences.PreferencesScreen
39+
import live.mehiz.mpvkt.ui.preferences.SubtitlesPreferencesScreen
1740
import live.mehiz.mpvkt.ui.theme.DarkMode
1841
import live.mehiz.mpvkt.ui.theme.MpvKtTheme
42+
import live.mehiz.mpvkt.ui.utils.LocalNavController
1943
import org.koin.android.ext.android.inject
2044

2145
class MainActivity : ComponentActivity() {
@@ -29,11 +53,48 @@ class MainActivity : ComponentActivity() {
2953
enableEdgeToEdge(
3054
SystemBarStyle.auto(
3155
lightScrim = Color.White.toArgb(),
32-
darkScrim = Color.White.toArgb()
56+
darkScrim = Color.White.toArgb(),
3357
) { dark == DarkMode.Dark || (dark == DarkMode.System && isSystemInDarkTheme) },
3458
)
35-
MpvKtTheme {
36-
Navigator(HomeScreen) { SlideTransition(it) }
59+
MpvKtTheme { Navigator() }
60+
}
61+
}
62+
63+
@Composable
64+
fun Navigator() {
65+
CompositionLocalProvider(LocalNavController provides rememberNavController()) {
66+
NavHost(
67+
LocalNavController.current,
68+
startDestination = HomeScreen,
69+
enterTransition = {
70+
fadeIn(animationSpec = tween(220)) +
71+
slideIn(animationSpec = tween(220)) { IntOffset(it.width / 2, 0) }
72+
},
73+
exitTransition = {
74+
fadeOut(animationSpec = tween(220)) +
75+
slideOut(animationSpec = tween(220)) { IntOffset(-it.width / 2, 0) }
76+
},
77+
popEnterTransition = {
78+
fadeIn(animationSpec = tween(220)) +
79+
scaleIn(animationSpec = tween(220, delayMillis = 30), initialScale = .9f, TransformOrigin(-1f, .5f))
80+
},
81+
popExitTransition = {
82+
fadeOut(animationSpec = tween(220)) +
83+
scaleOut(animationSpec = tween(220, delayMillis = 30), targetScale = .9f, TransformOrigin(-1f, .5f))
84+
},
85+
) {
86+
composable<HomeScreen> { HomeScreen.Content() }
87+
composable<FilePickerScreen> { it.toRoute<FilePickerScreen>().Content() }
88+
89+
composable<AboutScreen> { AboutScreen.Content() }
90+
composable<AdvancedPreferencesScreen> { AdvancedPreferencesScreen.Content() }
91+
composable<AppearancePreferencesScreen> { AppearancePreferencesScreen.Content() }
92+
composable<AudioPreferencesScreen> { AudioPreferencesScreen.Content() }
93+
composable<DecoderPreferencesScreen> { DecoderPreferencesScreen.Content() }
94+
composable<GesturePreferencesScreen> { DecoderPreferencesScreen.Content() }
95+
composable<PlayerPreferencesScreen> { PlayerPreferencesScreen.Content() }
96+
composable<PreferencesScreen> { PreferencesScreen.Content() }
97+
composable<SubtitlesPreferencesScreen> { SubtitlesPreferencesScreen.Content() }
3798
}
3899
}
39100
}
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package live.mehiz.mpvkt.presentation
22

3-
import cafe.adriel.voyager.core.screen.Screen
4-
import cafe.adriel.voyager.core.screen.uniqueScreenKey
3+
import androidx.compose.runtime.Composable
54

6-
abstract class Screen : Screen {
7-
final override val key = uniqueScreenKey
5+
interface Screen {
6+
@Composable
7+
fun Content()
88
}

app/src/main/java/live/mehiz/mpvkt/ui/custombuttons/CustomButtonsScreen.kt

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ import androidx.compose.runtime.Composable
44
import androidx.compose.runtime.collectAsState
55
import androidx.compose.runtime.getValue
66
import androidx.compose.ui.platform.LocalUriHandler
7-
import cafe.adriel.voyager.navigator.LocalNavigator
8-
import cafe.adriel.voyager.navigator.currentOrThrow
97
import kotlinx.collections.immutable.toImmutableList
108
import live.mehiz.mpvkt.database.entities.CustomButtonEntity
119
import live.mehiz.mpvkt.preferences.PlayerPreferences
@@ -15,13 +13,14 @@ import live.mehiz.mpvkt.presentation.custombuttons.CustomButtonsScreen
1513
import live.mehiz.mpvkt.presentation.custombuttons.components.CustomButtonAddDialog
1614
import live.mehiz.mpvkt.presentation.custombuttons.components.CustomButtonDeleteDialog
1715
import live.mehiz.mpvkt.presentation.custombuttons.components.CustomButtonEditDialog
16+
import live.mehiz.mpvkt.ui.utils.LocalNavController
1817
import org.koin.compose.koinInject
1918
import org.koin.compose.viewmodel.koinViewModel
2019

21-
object CustomButtonsScreen : Screen() {
20+
object CustomButtonsScreen : Screen {
2221
@Composable
2322
override fun Content() {
24-
val navigator = LocalNavigator.currentOrThrow
23+
val navigator = LocalNavController.current
2524
val uriHandler = LocalUriHandler.current
2625
val viewModel = koinViewModel<CustomButtonsScreenViewModel>()
2726
val playerPreferences = koinInject<PlayerPreferences>()
@@ -40,7 +39,7 @@ object CustomButtonsScreen : Screen() {
4039
onClickMoveUp = viewModel::moveUp,
4140
onClickMoveDown = viewModel::moveDown,
4241
onClickFaq = { uriHandler.openUri(CUSTOM_BUTTONS_DOC_URL) },
43-
onNavigateBack = navigator::pop,
42+
onNavigateBack = navigator::popBackStack,
4443
)
4544

4645
when (dialog) {

app/src/main/java/live/mehiz/mpvkt/ui/home/FilePickerScreen.kt

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,18 +40,18 @@ import androidx.compose.ui.res.pluralStringResource
4040
import androidx.compose.ui.res.stringResource
4141
import androidx.compose.ui.unit.dp
4242
import androidx.core.net.toUri
43-
import cafe.adriel.voyager.navigator.LocalNavigator
44-
import cafe.adriel.voyager.navigator.currentOrThrow
4543
import com.github.k1rakishou.fsaf.FileManager
4644
import com.github.k1rakishou.fsaf.file.AbstractFile
4745
import `is`.xyz.mpv.Utils
46+
import kotlinx.serialization.Serializable
4847
import live.mehiz.mpvkt.R
4948
import live.mehiz.mpvkt.presentation.Screen
5049
import live.mehiz.mpvkt.ui.player.audioExtensions
5150
import live.mehiz.mpvkt.ui.player.imageExtensions
5251
import live.mehiz.mpvkt.ui.player.videoExtensions
5352
import live.mehiz.mpvkt.ui.theme.spacing
5453
import live.mehiz.mpvkt.ui.utils.FilesComparator
54+
import live.mehiz.mpvkt.ui.utils.LocalNavController
5555
import org.koin.compose.koinInject
5656
import java.lang.Long.signum
5757
import java.text.StringCharacterIterator
@@ -60,20 +60,26 @@ import java.time.ZoneId
6060
import java.time.format.DateTimeFormatter
6161
import kotlin.math.abs
6262

63-
data class FilePickerScreen(val uri: String) : Screen() {
63+
@Serializable
64+
data class FilePickerScreen(val uri: String) : Screen {
6465

6566
@OptIn(ExperimentalMaterial3Api::class)
6667
@Composable
6768
override fun Content() {
68-
val navigator = LocalNavigator.currentOrThrow
69+
val navigator = LocalNavController.current
6970
val fileManager = koinInject<FileManager>()
7071
val context = LocalContext.current
7172
Scaffold(
7273
topBar = {
7374
TopAppBar(
7475
title = { Text(text = stringResource(id = R.string.home_pick_file)) },
7576
navigationIcon = {
76-
IconButton(onClick = { navigator.replaceAll(HomeScreen) }) {
77+
IconButton(
78+
onClick = {
79+
navigator.navigate(HomeScreen)
80+
navigator.clearBackStack<HomeScreen>()
81+
},
82+
) {
7783
Icon(Icons.AutoMirrored.Default.ArrowBack, null)
7884
}
7985
},
@@ -87,7 +93,7 @@ data class FilePickerScreen(val uri: String) : Screen() {
8793
HomeScreen.playFile(newFile.getFullPath(), context)
8894
return@FilePicker
8995
}
90-
navigator.push(FilePickerScreen(newFile.getFullPath()))
96+
navigator.navigate(FilePickerScreen(newFile.getFullPath()))
9197
},
9298
modifier = Modifier
9399
.fillMaxSize()
@@ -102,7 +108,7 @@ data class FilePickerScreen(val uri: String) : Screen() {
102108
modifier: Modifier = Modifier,
103109
onNavigate: (AbstractFile) -> Unit,
104110
) {
105-
val navigator = LocalNavigator.currentOrThrow
111+
val navigator = LocalNavController.current
106112
val fileManager = koinInject<FileManager>()
107113
val fileList = fileManager.listFiles(directory).filterNot {
108114
!Utils.MEDIA_EXTENSIONS.contains(fileManager.getName(it).substringAfterLast('.')) &&
@@ -116,7 +122,7 @@ data class FilePickerScreen(val uri: String) : Screen() {
116122
isDirectory = true,
117123
lastModified = null,
118124
length = 0L,
119-
onClick = { navigator.pop() },
125+
onClick = { navigator.popBackStack() },
120126
modifier = Modifier.background(MaterialTheme.colorScheme.surfaceContainerLow),
121127
)
122128
}

app/src/main/java/live/mehiz/mpvkt/ui/home/HomeScreen.kt

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,28 +37,29 @@ import androidx.compose.ui.platform.LocalContext
3737
import androidx.compose.ui.res.painterResource
3838
import androidx.compose.ui.res.stringResource
3939
import androidx.core.net.toUri
40-
import cafe.adriel.voyager.navigator.LocalNavigator
41-
import cafe.adriel.voyager.navigator.currentOrThrow
4240
import com.github.k1rakishou.fsaf.FileManager
4341
import `is`.xyz.mpv.Utils.PROTOCOLS
42+
import kotlinx.serialization.Serializable
4443
import live.mehiz.mpvkt.R
4544
import live.mehiz.mpvkt.presentation.Screen
4645
import live.mehiz.mpvkt.ui.player.PlayerActivity
4746
import live.mehiz.mpvkt.ui.preferences.PreferencesScreen
4847
import live.mehiz.mpvkt.ui.theme.spacing
48+
import live.mehiz.mpvkt.ui.utils.LocalNavController
4949

50-
object HomeScreen : Screen() {
50+
@Serializable
51+
object HomeScreen : Screen {
5152
@OptIn(ExperimentalMaterial3Api::class)
5253
@Composable
5354
override fun Content() {
5455
val context = LocalContext.current
55-
val navigator = LocalNavigator.currentOrThrow
56+
val navigator = LocalNavController.current
5657
Scaffold(
5758
topBar = {
5859
TopAppBar(
5960
title = { Text(text = stringResource(id = R.string.app_name)) },
6061
actions = {
61-
IconButton(onClick = { navigator.push(PreferencesScreen) }) {
62+
IconButton(onClick = { navigator.navigate(PreferencesScreen) }) {
6263
Icon(Icons.Default.Settings, null)
6364
}
6465
},
@@ -130,7 +131,7 @@ object HomeScreen : Screen() {
130131
ActivityResultContracts.OpenDocumentTree(),
131132
) {
132133
if (it == null) return@rememberLauncherForActivityResult
133-
navigator.push(FilePickerScreen(fileManager.fromUri(it)!!.getFullPath()))
134+
navigator.navigate(FilePickerScreen(fileManager.fromUri(it)!!.getFullPath()))
134135
}
135136
OutlinedButton(onClick = { directoryPicker.launch(null) }) {
136137
Row(

app/src/main/java/live/mehiz/mpvkt/ui/player/controls/PlayerControls.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@ import androidx.compose.ui.unit.LayoutDirection
5757
import androidx.compose.ui.unit.dp
5858
import androidx.constraintlayout.compose.ConstraintLayout
5959
import androidx.constraintlayout.compose.Dimension
60-
import cafe.adriel.voyager.navigator.currentOrThrow
6160
import `is`.xyz.mpv.MPVLib
6261
import `is`.xyz.mpv.Utils
6362
import kotlinx.collections.immutable.toImmutableList
@@ -493,7 +492,7 @@ fun PlayerControls(
493492
end.linkTo(seekbar.end)
494493
},
495494
) {
496-
val activity = LocalActivity.currentOrThrow as PlayerActivity
495+
val activity = LocalActivity.current!! as PlayerActivity
497496
BottomRightPlayerControls(
498497
customButton = customButton,
499498
customButtonTitle = customButtonTitle,

app/src/main/java/live/mehiz/mpvkt/ui/preferences/AboutScreen.kt

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,32 +30,33 @@ import androidx.compose.ui.res.stringResource
3030
import androidx.compose.ui.text.AnnotatedString
3131
import androidx.compose.ui.unit.dp
3232
import androidx.core.net.toUri
33-
import cafe.adriel.voyager.navigator.LocalNavigator
34-
import cafe.adriel.voyager.navigator.currentOrThrow
3533
import com.mikepenz.aboutlibraries.ui.compose.m3.LibrariesContainer
3634
import compose.icons.SimpleIcons
3735
import compose.icons.simpleicons.Github
36+
import kotlinx.serialization.Serializable
3837
import live.mehiz.mpvkt.BuildConfig
3938
import live.mehiz.mpvkt.R
4039
import live.mehiz.mpvkt.presentation.Screen
4140
import live.mehiz.mpvkt.presentation.crash.CrashActivity.Companion.collectDeviceInfo
4241
import live.mehiz.mpvkt.ui.theme.spacing
42+
import live.mehiz.mpvkt.ui.utils.LocalNavController
4343
import me.zhanghai.compose.preference.Preference
4444
import me.zhanghai.compose.preference.ProvidePreferenceLocals
4545

46-
object AboutScreen : Screen() {
46+
@Serializable
47+
object AboutScreen : Screen {
4748
@OptIn(ExperimentalMaterial3Api::class)
4849
@Composable
4950
override fun Content() {
5051
val context = LocalContext.current
5152
val clipboard = LocalClipboardManager.current
52-
val navigator = LocalNavigator.currentOrThrow
53+
val navigator = LocalNavController.current
5354
Scaffold(
5455
topBar = {
5556
TopAppBar(
5657
title = { Text(text = stringResource(id = R.string.pref_about_title)) },
5758
navigationIcon = {
58-
IconButton(onClick = { navigator.pop() }) {
59+
IconButton(onClick = navigator::popBackStack) {
5960
Icon(imageVector = Icons.AutoMirrored.Default.ArrowBack, contentDescription = null)
6061
}
6162
},
@@ -102,7 +103,7 @@ object AboutScreen : Screen() {
102103
)
103104
Preference(
104105
title = { Text(text = stringResource(id = R.string.pref_about_oss_libraries)) },
105-
onClick = { navigator.push(LibrariesScreen) },
106+
onClick = { navigator.navigate(LibrariesScreen) },
106107
)
107108
Preference(
108109
title = { Text(text = stringResource(id = R.string.pref_about_privacy_policy)) },
@@ -135,17 +136,17 @@ object AboutScreen : Screen() {
135136
}
136137
}
137138

138-
object LibrariesScreen : Screen() {
139+
object LibrariesScreen : Screen {
139140
@OptIn(ExperimentalMaterial3Api::class)
140141
@Composable
141142
override fun Content() {
142-
val navigator = LocalNavigator.currentOrThrow
143+
val navigator = LocalNavController.current
143144
Scaffold(
144145
topBar = {
145146
TopAppBar(
146147
title = { Text(text = stringResource(R.string.pref_about_oss_libraries)) },
147148
navigationIcon = {
148-
IconButton(onClick = navigator::pop) {
149+
IconButton(onClick = navigator::popBackStack) {
149150
Icon(imageVector = Icons.AutoMirrored.Default.ArrowBack, contentDescription = null)
150151
}
151152
},

0 commit comments

Comments
 (0)