diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 0b0701f02..9e8746f58 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -209,6 +209,9 @@ dependencies { api(platform(libs.compose.bom)) implementation(libs.bundles.compose) + implementation(libs.compose.destinations.core) + ksp(libs.compose.destinations.compiler) + implementation(libs.androidx.core) implementation(libs.androidx.core.splashscreen) diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/ConfigureActivity.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/ConfigureActivity.kt index f72c7da1c..aa3ba6c4d 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/ConfigureActivity.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/ConfigureActivity.kt @@ -1,5 +1,6 @@ package com.hippo.ehviewer.ui +import android.app.Activity import android.os.Bundle import androidx.compose.animation.AnimatedContentTransitionScope import androidx.compose.animation.core.tween @@ -7,120 +8,57 @@ import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.foundation.layout.imePadding import androidx.compose.material3.windowsizeclass.calculateWindowSizeClass +import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.SideEffect -import androidx.compose.runtime.compositionLocalOf import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext import androidx.core.view.WindowCompat -import androidx.navigation.NavController -import androidx.navigation.compose.NavHost -import androidx.navigation.compose.composable -import androidx.navigation.compose.rememberNavController import com.hippo.ehviewer.Settings import com.hippo.ehviewer.client.EhCookieStore -import com.hippo.ehviewer.ui.login.CookieSignInScene -import com.hippo.ehviewer.ui.login.SelectSiteScreen -import com.hippo.ehviewer.ui.login.SignInScreen -import com.hippo.ehviewer.ui.login.WebViewSignInScreen -import com.hippo.ehviewer.ui.settings.AboutScreen -import com.hippo.ehviewer.ui.settings.AdvancedScreen -import com.hippo.ehviewer.ui.settings.BaseScreen -import com.hippo.ehviewer.ui.settings.DownloadScreen -import com.hippo.ehviewer.ui.settings.EhScreen -import com.hippo.ehviewer.ui.settings.FilterScreen -import com.hippo.ehviewer.ui.settings.LicenseScreen -import com.hippo.ehviewer.ui.settings.MyTagsScreen -import com.hippo.ehviewer.ui.settings.PrivacyScreen -import com.hippo.ehviewer.ui.settings.UConfigScreen +import com.hippo.ehviewer.ui.destinations.BaseScreenDestination +import com.hippo.ehviewer.ui.destinations.SelectSiteScreenDestination +import com.hippo.ehviewer.ui.destinations.SignInScreenDestination +import com.hippo.ehviewer.ui.tools.LocalWindowSizeClass +import com.hippo.ehviewer.util.findActivity +import com.ramcosta.composedestinations.DestinationsNavHost +import com.ramcosta.composedestinations.animations.defaults.RootNavGraphDefaultAnimations +import com.ramcosta.composedestinations.annotation.Destination +import com.ramcosta.composedestinations.rememberNavHostEngine class ConfigureActivity : EhActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) WindowCompat.setDecorFitsSystemWindows(window, false) + val rootNavGraphDefaultAnimations = RootNavGraphDefaultAnimations( + enterTransition = { slideIntoContainer(AnimatedContentTransitionScope.SlideDirection.Up, tween(200)) }, + exitTransition = { fadeOut(tween(200)) }, + popEnterTransition = { fadeIn(tween(200)) }, + popExitTransition = { slideOutOfContainer(AnimatedContentTransitionScope.SlideDirection.Down, tween(200)) }, + ) setMD3Content { - val navController = rememberNavController() val windowSizeClass = calculateWindowSizeClass(this) - CompositionLocalProvider(LocalNavController provides navController) { - NavHost( - navController = navController, - startDestination = if (Settings.needSignIn) { - if (EhCookieStore.hasSignedIn()) SELECT_SITE_ROUTE_NAME else SIGN_IN_ROUTE_NAME - } else { - BASE_SETTINGS_SCREEN - }, + CompositionLocalProvider(LocalWindowSizeClass provides windowSizeClass) { + DestinationsNavHost( + navGraph = NavGraphs.root, modifier = Modifier.imePadding(), - enterTransition = { slideIntoContainer(AnimatedContentTransitionScope.SlideDirection.Up, tween(200)) }, - exitTransition = { fadeOut(tween(200)) }, - popEnterTransition = { fadeIn(tween(200)) }, - popExitTransition = { slideOutOfContainer(AnimatedContentTransitionScope.SlideDirection.Down, tween(200)) }, - ) { - composable(SIGN_IN_ROUTE_NAME) { - SignInScreen(windowSizeClass) - } - composable(WEBVIEW_SIGN_IN_ROUTE_NAME) { - WebViewSignInScreen() - } - composable(COOKIE_SIGN_IN_ROUTE_NAME) { - CookieSignInScene(windowSizeClass) - } - composable(SELECT_SITE_ROUTE_NAME) { - SelectSiteScreen() - } - composable(BASE_SETTINGS_SCREEN) { - BaseScreen() - } - composable(ABOUT_SETTINGS_SCREEN) { - AboutScreen() - } - composable(LICENSE_SCREEN) { - LicenseScreen() - } - composable(PRIVACY_SETTINGS_SCREEN) { - PrivacyScreen() - } - composable(ADVANCED_SETTINGS_SCREEN) { - AdvancedScreen() - } - composable(DOWNLOAD_SETTINGS_SCREEN) { - DownloadScreen() - } - composable(EH_SETTINGS_SCREEN) { - EhScreen() - } - composable(UCONFIG_SCREEN) { - UConfigScreen() - } - composable(MYTAGS_SCREEN) { - MyTagsScreen() - } - composable(FILTER_SCREEN) { - FilterScreen() - } - composable(FINISH_ROUTE_NAME) { - SideEffect { - finish() - } - } - } + startRoute = if (Settings.needSignIn) { + if (EhCookieStore.hasSignedIn()) SelectSiteScreenDestination else SignInScreenDestination + } else { + BaseScreenDestination + }, + engine = rememberNavHostEngine(rootDefaultAnimations = rootNavGraphDefaultAnimations), + ) } } } } -val LocalNavController = compositionLocalOf { error("CompositionLocal LocalNavController not present!") } - -const val BASE_SETTINGS_SCREEN = "Base" -const val EH_SETTINGS_SCREEN = "Eh" -const val DOWNLOAD_SETTINGS_SCREEN = "Download" -const val PRIVACY_SETTINGS_SCREEN = "Privacy" -const val ADVANCED_SETTINGS_SCREEN = "Advanced" -const val ABOUT_SETTINGS_SCREEN = "About" -const val LICENSE_SCREEN = "License" -const val UCONFIG_SCREEN = "UConfig" -const val MYTAGS_SCREEN = "Mytags" -const val FILTER_SCREEN = "Filter" -const val SIGN_IN_ROUTE_NAME = "SignIn" -const val WEBVIEW_SIGN_IN_ROUTE_NAME = "WebViewSignIn" -const val COOKIE_SIGN_IN_ROUTE_NAME = "CookieSignIn" -const val SELECT_SITE_ROUTE_NAME = "SelectSite" -const val FINISH_ROUTE_NAME = "Finish" +@Destination +@Composable +fun Finish() { + val context = LocalContext.current + SideEffect { + context.findActivity().finish() + } +} diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/login/CookieSignInScreen.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/login/CookieSignInScreen.kt index cb0f9e757..36536a64f 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/login/CookieSignInScreen.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/login/CookieSignInScreen.kt @@ -28,7 +28,6 @@ import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text import androidx.compose.material3.TextButton -import androidx.compose.material3.windowsizeclass.WindowSizeClass import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue @@ -56,11 +55,13 @@ import com.hippo.ehviewer.client.EhCookieStore import com.hippo.ehviewer.client.EhEngine import com.hippo.ehviewer.client.EhUrl import com.hippo.ehviewer.client.EhUtils -import com.hippo.ehviewer.ui.FINISH_ROUTE_NAME -import com.hippo.ehviewer.ui.LocalNavController -import com.hippo.ehviewer.ui.SELECT_SITE_ROUTE_NAME +import com.hippo.ehviewer.ui.destinations.FinishDestination +import com.hippo.ehviewer.ui.destinations.SelectSiteScreenDestination import com.hippo.ehviewer.ui.tools.LocalDialogState +import com.hippo.ehviewer.ui.tools.LocalWindowSizeClass import com.hippo.ehviewer.util.ExceptionUtils +import com.ramcosta.composedestinations.annotation.Destination +import com.ramcosta.composedestinations.navigation.DestinationsNavigator import eu.kanade.tachiyomi.util.lang.launchIO import eu.kanade.tachiyomi.util.lang.withNonCancellableContext import eu.kanade.tachiyomi.util.lang.withUIContext @@ -69,9 +70,10 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.launch import okhttp3.Cookie +@Destination @Composable -fun CookieSignInScene(windowSizeClass: WindowSizeClass) { - val navController = LocalNavController.current +fun CookieSignInScene(navigator: DestinationsNavigator) { + val windowSizeClass = LocalWindowSizeClass.current val clipboardManager = LocalClipboardManager.current val focusManager = LocalFocusManager.current val snackbarHostState = remember { SnackbarHostState() } @@ -124,7 +126,7 @@ fun CookieSignInScene(windowSizeClass: WindowSizeClass) { EhEngine.getProfile() }.onSuccess { val canEx = withNonCancellableContext { postLogin() } - withUIContext { navController.navigate(if (canEx) SELECT_SITE_ROUTE_NAME else FINISH_ROUTE_NAME) } + withUIContext { navigator.navigate(if (canEx) SelectSiteScreenDestination else FinishDestination) } }.onFailure { EhCookieStore.signOut() dialogState.awaitPermissionOrCancel( diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/login/SelectSiteScreen.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/login/SelectSiteScreen.kt index 7ae41522a..1aafac749 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/login/SelectSiteScreen.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/login/SelectSiteScreen.kt @@ -29,13 +29,14 @@ import androidx.compose.ui.unit.dp import com.hippo.ehviewer.R import com.hippo.ehviewer.Settings import com.hippo.ehviewer.client.EhUrl -import com.hippo.ehviewer.ui.FINISH_ROUTE_NAME -import com.hippo.ehviewer.ui.LocalNavController +import com.hippo.ehviewer.ui.destinations.FinishDestination +import com.ramcosta.composedestinations.annotation.Destination +import com.ramcosta.composedestinations.navigation.DestinationsNavigator +@Destination @Composable -fun SelectSiteScreen() { +fun SelectSiteScreen(navigator: DestinationsNavigator) { var siteEx by remember { mutableStateOf(true) } - val navController = LocalNavController.current val context = LocalContext.current BackHandler { @@ -70,7 +71,7 @@ fun SelectSiteScreen() { // Gallery site was set to ex in sad panda check if (!siteEx) Settings.gallerySite = EhUrl.SITE_E Settings.needSignIn = false - navController.navigate(FINISH_ROUTE_NAME) + navigator.navigate(FinishDestination) }, modifier = Modifier.fillMaxWidth(), ) { diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/login/SignInScreen.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/login/SignInScreen.kt index 6d93f5ade..23bd3b2df 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/login/SignInScreen.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/login/SignInScreen.kt @@ -32,7 +32,6 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text import androidx.compose.material3.TextButton -import androidx.compose.material3.windowsizeclass.WindowSizeClass import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue @@ -62,22 +61,25 @@ import com.hippo.ehviewer.Settings import com.hippo.ehviewer.client.EhEngine import com.hippo.ehviewer.client.EhUrl import com.hippo.ehviewer.client.EhUtils -import com.hippo.ehviewer.ui.COOKIE_SIGN_IN_ROUTE_NAME -import com.hippo.ehviewer.ui.FINISH_ROUTE_NAME -import com.hippo.ehviewer.ui.LocalNavController -import com.hippo.ehviewer.ui.SELECT_SITE_ROUTE_NAME -import com.hippo.ehviewer.ui.WEBVIEW_SIGN_IN_ROUTE_NAME +import com.hippo.ehviewer.ui.destinations.CookieSignInSceneDestination +import com.hippo.ehviewer.ui.destinations.FinishDestination +import com.hippo.ehviewer.ui.destinations.SelectSiteScreenDestination +import com.hippo.ehviewer.ui.destinations.WebViewSignInScreenDestination import com.hippo.ehviewer.ui.openBrowser import com.hippo.ehviewer.ui.tools.LocalDialogState +import com.hippo.ehviewer.ui.tools.LocalWindowSizeClass import com.hippo.ehviewer.util.ExceptionUtils +import com.ramcosta.composedestinations.annotation.Destination +import com.ramcosta.composedestinations.navigation.DestinationsNavigator import eu.kanade.tachiyomi.util.lang.launchIO import eu.kanade.tachiyomi.util.lang.withNonCancellableContext import eu.kanade.tachiyomi.util.lang.withUIContext import kotlinx.coroutines.Job +@Destination @Composable -fun SignInScreen(windowSizeClass: WindowSizeClass) { - val navController = LocalNavController.current +fun SignInScreen(navigator: DestinationsNavigator) { + val windowSizeClass = LocalWindowSizeClass.current val coroutineScope = rememberCoroutineScope() val focusManager = LocalFocusManager.current var isProgressIndicatorVisible by rememberSaveable { mutableStateOf(false) } @@ -134,7 +136,7 @@ fun SignInScreen(windowSizeClass: WindowSizeClass) { } }.onSuccess { val canEx = withNonCancellableContext { postLogin() } - withUIContext { navController.navigate(if (canEx) SELECT_SITE_ROUTE_NAME else FINISH_ROUTE_NAME) } + withUIContext { navigator.navigate(if (canEx) SelectSiteScreenDestination else FinishDestination) } } } } @@ -216,7 +218,7 @@ fun SignInScreen(windowSizeClass: WindowSizeClass) { } Row(modifier = Modifier.padding(horizontal = 4.dp)) { TextButton( - onClick = { navController.navigate(WEBVIEW_SIGN_IN_ROUTE_NAME) }, + onClick = { navigator.navigate(WebViewSignInScreenDestination) }, modifier = Modifier.padding(horizontal = 8.dp), ) { Text( @@ -230,7 +232,7 @@ fun SignInScreen(windowSizeClass: WindowSizeClass) { ) } TextButton( - onClick = { navController.navigate(COOKIE_SIGN_IN_ROUTE_NAME) }, + onClick = { navigator.navigate(CookieSignInSceneDestination) }, modifier = Modifier.padding(horizontal = 8.dp), ) { Text( @@ -248,7 +250,7 @@ fun SignInScreen(windowSizeClass: WindowSizeClass) { onClick = { Settings.needSignIn = false Settings.gallerySite = EhUrl.SITE_E - navController.navigate(FINISH_ROUTE_NAME) + navigator.navigate(FinishDestination) }, ) { Text( @@ -312,7 +314,7 @@ fun SignInScreen(windowSizeClass: WindowSizeClass) { Spacer(modifier = Modifier.height(8.dp)) Row(horizontalArrangement = Arrangement.Center) { TextButton( - onClick = { navController.navigate(COOKIE_SIGN_IN_ROUTE_NAME) }, + onClick = { navigator.navigate(CookieSignInSceneDestination) }, modifier = Modifier.padding(horizontal = 4.dp), ) { Text( @@ -326,7 +328,7 @@ fun SignInScreen(windowSizeClass: WindowSizeClass) { ) } TextButton( - onClick = { navController.navigate(WEBVIEW_SIGN_IN_ROUTE_NAME) }, + onClick = { navigator.navigate(WebViewSignInScreenDestination) }, modifier = Modifier.padding(horizontal = 4.dp), ) { Text( @@ -344,7 +346,7 @@ fun SignInScreen(windowSizeClass: WindowSizeClass) { onClick = { Settings.needSignIn = false Settings.gallerySite = EhUrl.SITE_E - navController.navigate(FINISH_ROUTE_NAME) + navigator.navigate(FinishDestination) }, modifier = Modifier.padding(horizontal = 4.dp), ) { diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/login/WebViewSignInScreen.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/login/WebViewSignInScreen.kt index cff3bd18c..41b6944be 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/login/WebViewSignInScreen.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/login/WebViewSignInScreen.kt @@ -14,18 +14,19 @@ import com.google.accompanist.web.rememberWebViewState import com.hippo.ehviewer.client.EhCookieStore import com.hippo.ehviewer.client.EhUrl import com.hippo.ehviewer.client.EhUtils -import com.hippo.ehviewer.ui.FINISH_ROUTE_NAME -import com.hippo.ehviewer.ui.LocalNavController -import com.hippo.ehviewer.ui.SELECT_SITE_ROUTE_NAME +import com.hippo.ehviewer.ui.destinations.FinishDestination +import com.hippo.ehviewer.ui.destinations.SelectSiteScreenDestination import com.hippo.ehviewer.util.setDefaultSettings +import com.ramcosta.composedestinations.annotation.Destination +import com.ramcosta.composedestinations.navigation.DestinationsNavigator import eu.kanade.tachiyomi.util.lang.launchIO import eu.kanade.tachiyomi.util.lang.withNonCancellableContext import eu.kanade.tachiyomi.util.lang.withUIContext import okhttp3.HttpUrl.Companion.toHttpUrl +@Destination @Composable -fun WebViewSignInScreen() { - val navController = LocalNavController.current +fun WebViewSignInScreen(navigator: DestinationsNavigator) { val coroutineScope = rememberCoroutineScope() val state = rememberWebViewState(url = EhUrl.URL_SIGN_IN) val client = remember { @@ -49,7 +50,7 @@ fun WebViewSignInScreen() { present = true coroutineScope.launchIO { val canEx = withNonCancellableContext { postLogin() } - withUIContext { navController.navigate(if (canEx) SELECT_SITE_ROUTE_NAME else FINISH_ROUTE_NAME) } + withUIContext { navigator.navigate(if (canEx) SelectSiteScreenDestination else FinishDestination) } } } } diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/AboutScreen.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/AboutScreen.kt index 9b8865b58..cbce740ae 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/AboutScreen.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/AboutScreen.kt @@ -31,8 +31,7 @@ import androidx.core.text.parseAsHtml import com.hippo.ehviewer.BuildConfig import com.hippo.ehviewer.R import com.hippo.ehviewer.Settings -import com.hippo.ehviewer.ui.LICENSE_SCREEN -import com.hippo.ehviewer.ui.LocalNavController +import com.hippo.ehviewer.ui.destinations.LicenseScreenDestination import com.hippo.ehviewer.ui.tools.DialogState import com.hippo.ehviewer.ui.tools.LocalDialogState import com.hippo.ehviewer.ui.tools.observed @@ -42,6 +41,8 @@ import com.hippo.ehviewer.updater.Release import com.hippo.ehviewer.util.AppConfig import com.hippo.ehviewer.util.ExceptionUtils import com.hippo.ehviewer.util.installPackage +import com.ramcosta.composedestinations.annotation.Destination +import com.ramcosta.composedestinations.navigation.DestinationsNavigator import eu.kanade.tachiyomi.util.lang.withUIContext import java.io.File import kotlinx.coroutines.Dispatchers @@ -59,10 +60,10 @@ private fun versionCode() = "${BuildConfig.VERSION_NAME} (${BuildConfig.COMMIT_S @Stable private fun author() = stringResource(R.string.settings_about_author_summary).replace('$', '@').parseAsHtml().toAnnotatedString() +@Destination @Composable -fun AboutScreen() { +fun AboutScreen(navigator: DestinationsNavigator) { val context = LocalContext.current - val navController = LocalNavController.current val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior() val snackbarHostState = remember { SnackbarHostState() } val coroutineScope = rememberCoroutineScope { Dispatchers.IO } @@ -73,7 +74,7 @@ fun AboutScreen() { TopAppBar( title = { Text(text = stringResource(id = R.string.settings_about)) }, navigationIcon = { - IconButton(onClick = { navController.popBackStack() }) { + IconButton(onClick = { navigator.popBackStack() }) { Icon(imageVector = Icons.AutoMirrored.Default.ArrowBack, contentDescription = null) } }, @@ -100,7 +101,7 @@ fun AboutScreen() { url = REPO_URL, ) Preference(title = stringResource(id = R.string.license)) { - navController.navigate(LICENSE_SCREEN) + navigator.navigate(LicenseScreenDestination) } Preference( title = stringResource(id = R.string.settings_about_version), diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/AdvancedScreen.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/AdvancedScreen.kt index 0685248bf..b4a600886 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/AdvancedScreen.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/AdvancedScreen.kt @@ -37,11 +37,12 @@ import com.hippo.ehviewer.client.CHROME_USER_AGENT import com.hippo.ehviewer.client.EhCookieStore import com.hippo.ehviewer.client.EhEngine import com.hippo.ehviewer.client.data.FavListUrlBuilder -import com.hippo.ehviewer.ui.LocalNavController import com.hippo.ehviewer.ui.tools.LocalDialogState import com.hippo.ehviewer.ui.tools.observed import com.hippo.ehviewer.util.AppConfig import com.hippo.ehviewer.util.ReadableTime +import com.ramcosta.composedestinations.annotation.Destination +import com.ramcosta.composedestinations.navigation.DestinationsNavigator import java.io.File import java.util.zip.ZipEntry import java.util.zip.ZipOutputStream @@ -50,9 +51,9 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.launch import moe.tarsin.coroutines.runSuspendCatching +@Destination @Composable -fun AdvancedScreen() { - val navController = LocalNavController.current +fun AdvancedScreen(navigator: DestinationsNavigator) { val context = LocalContext.current val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior() val snackbarHostState = remember { SnackbarHostState() } @@ -64,7 +65,7 @@ fun AdvancedScreen() { TopAppBar( title = { Text(text = stringResource(id = R.string.settings_advanced)) }, navigationIcon = { - IconButton(onClick = { navController.popBackStack() }) { + IconButton(onClick = { navigator.popBackStack() }) { Icon(imageVector = Icons.AutoMirrored.Default.ArrowBack, contentDescription = null) } }, diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/BaseScreen.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/BaseScreen.kt index b633a5da6..ca86317f4 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/BaseScreen.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/BaseScreen.kt @@ -21,24 +21,27 @@ import androidx.compose.ui.res.stringResource import com.hippo.ehviewer.R import com.hippo.ehviewer.icons.EhIcons import com.hippo.ehviewer.icons.filled.SadPanda -import com.hippo.ehviewer.ui.ABOUT_SETTINGS_SCREEN -import com.hippo.ehviewer.ui.ADVANCED_SETTINGS_SCREEN -import com.hippo.ehviewer.ui.DOWNLOAD_SETTINGS_SCREEN -import com.hippo.ehviewer.ui.EH_SETTINGS_SCREEN -import com.hippo.ehviewer.ui.FINISH_ROUTE_NAME -import com.hippo.ehviewer.ui.LocalNavController -import com.hippo.ehviewer.ui.PRIVACY_SETTINGS_SCREEN +import com.hippo.ehviewer.ui.destinations.AboutScreenDestination +import com.hippo.ehviewer.ui.destinations.AdvancedScreenDestination +import com.hippo.ehviewer.ui.destinations.DownloadScreenDestination +import com.hippo.ehviewer.ui.destinations.EhScreenDestination +import com.hippo.ehviewer.ui.destinations.FinishDestination +import com.hippo.ehviewer.ui.destinations.PrivacyScreenDestination +import com.ramcosta.composedestinations.annotation.Destination +import com.ramcosta.composedestinations.annotation.RootNavGraph +import com.ramcosta.composedestinations.navigation.DestinationsNavigator +@RootNavGraph(start = true) +@Destination @Composable -fun BaseScreen() { - val navController = LocalNavController.current +fun BaseScreen(navigator: DestinationsNavigator) { val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior() Scaffold( topBar = { TopAppBar( title = { Text(text = stringResource(id = R.string.settings)) }, navigationIcon = { - IconButton(onClick = { navController.navigate(FINISH_ROUTE_NAME) }) { + IconButton(onClick = { navigator.navigate(FinishDestination) }) { Icon(imageVector = Icons.AutoMirrored.Default.ArrowBack, contentDescription = null) } }, @@ -50,27 +53,32 @@ fun BaseScreen() { PreferenceHeader( icon = EhIcons.Default.SadPanda, title = R.string.settings_eh, - childRouteName = EH_SETTINGS_SCREEN, + childRoute = EhScreenDestination, + navigator = navigator, ) PreferenceHeader( icon = Icons.Default.Download, title = R.string.settings_download, - childRouteName = DOWNLOAD_SETTINGS_SCREEN, + childRoute = DownloadScreenDestination, + navigator = navigator, ) PreferenceHeader( icon = Icons.Default.Security, title = R.string.settings_privacy, - childRouteName = PRIVACY_SETTINGS_SCREEN, + childRoute = PrivacyScreenDestination, + navigator = navigator, ) PreferenceHeader( icon = Icons.Default.Adb, title = R.string.settings_advanced, - childRouteName = ADVANCED_SETTINGS_SCREEN, + childRoute = AdvancedScreenDestination, + navigator = navigator, ) PreferenceHeader( icon = Icons.Default.Info, title = R.string.settings_about, - childRouteName = ABOUT_SETTINGS_SCREEN, + childRoute = AboutScreenDestination, + navigator = navigator, ) } } diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/DownloadScreen.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/DownloadScreen.kt index 73daf0a89..a3a3c2f41 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/DownloadScreen.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/DownloadScreen.kt @@ -38,22 +38,23 @@ import com.hippo.ehviewer.download.DownloadManager import com.hippo.ehviewer.download.downloadLocation import com.hippo.ehviewer.spider.SpiderQueen import com.hippo.ehviewer.spider.readCompatFromUniFile -import com.hippo.ehviewer.ui.LocalNavController import com.hippo.ehviewer.ui.keepNoMediaFileStatus import com.hippo.ehviewer.ui.legacy.BaseDialogBuilder import com.hippo.ehviewer.ui.tools.observed import com.hippo.ehviewer.ui.tools.rememberedAccessor import com.hippo.ehviewer.util.AppConfig import com.hippo.unifile.UniFile +import com.ramcosta.composedestinations.annotation.Destination +import com.ramcosta.composedestinations.navigation.DestinationsNavigator import eu.kanade.tachiyomi.util.lang.launchNonCancellable import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import moe.tarsin.coroutines.runSuspendCatching import splitties.init.appCtx +@Destination @Composable -fun DownloadScreen() { - val navController = LocalNavController.current +fun DownloadScreen(navigator: DestinationsNavigator) { val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior() val context = LocalContext.current val coroutineScope = rememberCoroutineScope { Dispatchers.IO } @@ -64,7 +65,7 @@ fun DownloadScreen() { TopAppBar( title = { Text(text = stringResource(id = R.string.settings_download)) }, navigationIcon = { - IconButton(onClick = { navController.popBackStack() }) { + IconButton(onClick = { navigator.popBackStack() }) { Icon(imageVector = Icons.AutoMirrored.Default.ArrowBack, contentDescription = null) } }, diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/EhScreen.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/EhScreen.kt index 9c11823cb..08ef3a8cd 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/EhScreen.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/EhScreen.kt @@ -43,17 +43,18 @@ import com.hippo.ehviewer.client.parser.HomeParser import com.hippo.ehviewer.dailycheck.schedHour import com.hippo.ehviewer.dailycheck.schedMinute import com.hippo.ehviewer.dailycheck.updateDailyCheckWork -import com.hippo.ehviewer.ui.FILTER_SCREEN -import com.hippo.ehviewer.ui.LocalNavController -import com.hippo.ehviewer.ui.MYTAGS_SCREEN -import com.hippo.ehviewer.ui.SIGN_IN_ROUTE_NAME -import com.hippo.ehviewer.ui.UCONFIG_SCREEN +import com.hippo.ehviewer.ui.destinations.FilterScreenDestination +import com.hippo.ehviewer.ui.destinations.MyTagsScreenDestination +import com.hippo.ehviewer.ui.destinations.SignInScreenDestination +import com.hippo.ehviewer.ui.destinations.UConfigScreenDestination import com.hippo.ehviewer.ui.legacy.BaseDialogBuilder import com.hippo.ehviewer.ui.tools.LocalDialogState import com.hippo.ehviewer.ui.tools.TimePickerDialog import com.hippo.ehviewer.ui.tools.observed import com.hippo.ehviewer.ui.tools.rememberedAccessor import com.hippo.ehviewer.util.copyTextToClipboard +import com.ramcosta.composedestinations.annotation.Destination +import com.ramcosta.composedestinations.navigation.DestinationsNavigator import eu.kanade.tachiyomi.util.lang.withUIContext import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async @@ -61,9 +62,9 @@ import kotlinx.coroutines.launch import moe.tarsin.coroutines.runSuspendCatching import okhttp3.HttpUrl.Companion.toHttpUrl +@Destination @Composable -fun EhScreen() { - val navController = LocalNavController.current +fun EhScreen(navigator: DestinationsNavigator) { val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior() val snackbarHostState = remember { SnackbarHostState() } val coroutineScope = rememberCoroutineScope { Dispatchers.IO } @@ -76,7 +77,7 @@ fun EhScreen() { TopAppBar( title = { Text(text = stringResource(id = R.string.settings_eh)) }, navigationIcon = { - IconButton(onClick = { navController.popBackStack() }) { + IconButton(onClick = { navigator.popBackStack() }) { Icon(imageVector = Icons.AutoMirrored.Default.ArrowBack, contentDescription = null) } }, @@ -119,7 +120,7 @@ fun EhScreen() { } setPositiveButton(R.string.settings_eh_sign_out) { _, _ -> EhUtils.signOut() - navController.navigate(SIGN_IN_ROUTE_NAME) + navigator.navigate(SignInScreenDestination) } }.show() } @@ -184,11 +185,11 @@ fun EhScreen() { Preference( title = stringResource(id = R.string.settings_u_config), summary = stringResource(id = R.string.settings_u_config_summary), - ) { navController.navigate(UCONFIG_SCREEN) } + ) { navigator.navigate(UConfigScreenDestination) } Preference( title = stringResource(id = R.string.settings_my_tags), summary = stringResource(id = R.string.settings_my_tags_summary), - ) { navController.navigate(MYTAGS_SCREEN) } + ) { navigator.navigate(MyTagsScreenDestination) } } var defaultFavSlot by Settings::defaultFavSlot.observed val disabled = stringResource(id = R.string.disabled_nav) @@ -306,7 +307,7 @@ fun EhScreen() { Preference( title = stringResource(id = R.string.settings_eh_filter), summary = stringResource(id = R.string.settings_eh_filter_summary), - ) { navController.navigate(FILTER_SCREEN) } + ) { navigator.navigate(FilterScreenDestination) } SwitchPreference( title = stringResource(id = R.string.settings_eh_metered_network_warning), value = Settings::meteredNetworkWarning, diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/FilterScreen.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/FilterScreen.kt index 6378ba399..84fd4f98a 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/FilterScreen.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/FilterScreen.kt @@ -46,19 +46,20 @@ import com.hippo.ehviewer.client.EhFilter.trigger import com.hippo.ehviewer.dao.Filter import com.hippo.ehviewer.dao.FilterMode import com.hippo.ehviewer.databinding.DialogAddFilterBinding -import com.hippo.ehviewer.ui.LocalNavController import com.hippo.ehviewer.ui.legacy.BaseDialogBuilder import com.hippo.ehviewer.ui.tools.Deferred import com.hippo.ehviewer.ui.tools.LocalDialogState +import com.ramcosta.composedestinations.annotation.Destination +import com.ramcosta.composedestinations.navigation.DestinationsNavigator import eu.kanade.tachiyomi.util.lang.withUIContext import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async import kotlinx.coroutines.launch import moe.tarsin.coroutines.groupByToObserved +@Destination @Composable -fun FilterScreen() { - val navController = LocalNavController.current +fun FilterScreen(navigator: DestinationsNavigator) { val context = LocalContext.current val scope = rememberCoroutineScope { Dispatchers.IO } val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior() @@ -112,7 +113,7 @@ fun FilterScreen() { TopAppBar( title = { Text(text = stringResource(id = R.string.filter)) }, navigationIcon = { - IconButton(onClick = { navController.popBackStack() }) { + IconButton(onClick = { navigator.popBackStack() }) { Icon(imageVector = Icons.AutoMirrored.Default.ArrowBack, contentDescription = null) } }, diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/LicenseScreen.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/LicenseScreen.kt index 57d22f461..f24740f6f 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/LicenseScreen.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/LicenseScreen.kt @@ -13,19 +13,20 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.res.stringResource import com.hippo.ehviewer.R -import com.hippo.ehviewer.ui.LocalNavController import com.mikepenz.aboutlibraries.ui.compose.LibrariesContainer +import com.ramcosta.composedestinations.annotation.Destination +import com.ramcosta.composedestinations.navigation.DestinationsNavigator +@Destination @Composable -fun LicenseScreen() { - val navController = LocalNavController.current +fun LicenseScreen(navigator: DestinationsNavigator) { val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior() Scaffold( topBar = { TopAppBar( title = { Text(text = stringResource(id = R.string.license)) }, navigationIcon = { - IconButton(onClick = { navController.popBackStack() }) { + IconButton(onClick = { navigator.popBackStack() }) { Icon(imageVector = Icons.AutoMirrored.Default.ArrowBack, contentDescription = null) } }, diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/MyTagsScreen.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/MyTagsScreen.kt index ff369e4c1..5a9dc0ca1 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/MyTagsScreen.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/MyTagsScreen.kt @@ -16,19 +16,20 @@ import com.google.accompanist.web.WebView import com.google.accompanist.web.rememberWebViewState import com.hippo.ehviewer.R import com.hippo.ehviewer.client.EhUrl -import com.hippo.ehviewer.ui.LocalNavController import com.hippo.ehviewer.util.setDefaultSettings +import com.ramcosta.composedestinations.annotation.Destination +import com.ramcosta.composedestinations.navigation.DestinationsNavigator +@Destination @Composable -fun MyTagsScreen() { - val navController = LocalNavController.current +fun MyTagsScreen(navigator: DestinationsNavigator) { val url = EhUrl.myTagsUrl Scaffold( topBar = { TopAppBar( title = { Text(text = stringResource(id = R.string.my_tags)) }, navigationIcon = { - IconButton(onClick = { navController.popBackStack() }) { + IconButton(onClick = { navigator.popBackStack() }) { Icon(imageVector = Icons.AutoMirrored.Default.ArrowBack, contentDescription = null) } }, diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/PreferenceWidgets.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/PreferenceWidgets.kt index 707e36b87..fb15e9d57 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/PreferenceWidgets.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/PreferenceWidgets.kt @@ -41,7 +41,7 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.window.DialogProperties import arrow.atomic.Atomic import com.hippo.ehviewer.R -import com.hippo.ehviewer.ui.LocalNavController +import com.hippo.ehviewer.ui.destinations.DirectionDestination import com.hippo.ehviewer.ui.openBrowser import com.hippo.ehviewer.ui.settings.PreferenceTokens.PreferenceTextPadding import com.jamal.composeprefs3.ui.prefs.DropDownPref @@ -50,6 +50,7 @@ import com.jamal.composeprefs3.ui.prefs.SliderPref import com.jamal.composeprefs3.ui.prefs.SpannedTextPref import com.jamal.composeprefs3.ui.prefs.SwitchPref import com.jamal.composeprefs3.ui.prefs.TextPref +import com.ramcosta.composedestinations.navigation.DestinationsNavigator import kotlin.coroutines.resume import kotlin.coroutines.suspendCoroutine import kotlin.math.roundToInt @@ -66,10 +67,14 @@ object PreferenceTokens { } @Composable -fun PreferenceHeader(icon: Painter, @StringRes title: Int, childRouteName: String) { - val navController = LocalNavController.current +fun PreferenceHeader( + icon: Painter, + @StringRes title: Int, + childRoute: DirectionDestination, + navigator: DestinationsNavigator, +) { Row( - modifier = Modifier.clickable { navController.navigate(childRouteName) }.fillMaxWidth().height(PreferenceTokens.PreferenceHeaderHeight), + modifier = Modifier.clickable { navigator.navigate(childRoute) }.fillMaxWidth().height(PreferenceTokens.PreferenceHeaderHeight), verticalAlignment = Alignment.CenterVertically, ) { Spacer(modifier = Modifier.size(PreferenceTokens.PreferenceIconPadding)) @@ -80,7 +85,17 @@ fun PreferenceHeader(icon: Painter, @StringRes title: Int, childRouteName: Strin } @Composable -fun PreferenceHeader(icon: ImageVector, @StringRes title: Int, childRouteName: String) = PreferenceHeader(icon = rememberVectorPainter(image = icon), title = title, childRouteName = childRouteName) +fun PreferenceHeader( + icon: ImageVector, + @StringRes title: Int, + childRoute: DirectionDestination, + navigator: DestinationsNavigator, +) = PreferenceHeader( + icon = rememberVectorPainter(image = icon), + title = title, + childRoute = childRoute, + navigator = navigator, +) @Composable fun Preference(title: String, summary: String? = null, onClick: () -> Unit = {}) { diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/PrivacyScreen.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/PrivacyScreen.kt index 55a74def3..169543423 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/PrivacyScreen.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/PrivacyScreen.kt @@ -22,17 +22,18 @@ import androidx.compose.ui.res.stringResource import com.hippo.ehviewer.EhApplication.Companion.searchDatabase import com.hippo.ehviewer.R import com.hippo.ehviewer.Settings -import com.hippo.ehviewer.ui.LocalNavController import com.hippo.ehviewer.ui.isAuthenticationSupported import com.hippo.ehviewer.ui.tools.LocalDialogState import com.hippo.ehviewer.ui.tools.observed import com.hippo.ehviewer.ui.tools.rememberedAccessor +import com.ramcosta.composedestinations.annotation.Destination +import com.ramcosta.composedestinations.navigation.DestinationsNavigator import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +@Destination @Composable -fun PrivacyScreen() { - val navController = LocalNavController.current +fun PrivacyScreen(navigator: DestinationsNavigator) { val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior() val snackbarHostState = remember { SnackbarHostState() } val coroutineScope = rememberCoroutineScope { Dispatchers.IO } @@ -43,7 +44,7 @@ fun PrivacyScreen() { TopAppBar( title = { Text(text = stringResource(id = R.string.settings_privacy)) }, navigationIcon = { - IconButton(onClick = { navController.popBackStack() }) { + IconButton(onClick = { navigator.popBackStack() }) { Icon(imageVector = Icons.AutoMirrored.Default.ArrowBack, contentDescription = null) } }, diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/UConfigScreen.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/UConfigScreen.kt index 61a6195c3..6abefbc7d 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/UConfigScreen.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/UConfigScreen.kt @@ -25,14 +25,15 @@ import com.google.accompanist.web.rememberWebViewState import com.hippo.ehviewer.R import com.hippo.ehviewer.client.EhCookieStore import com.hippo.ehviewer.client.EhUrl -import com.hippo.ehviewer.ui.LocalNavController import com.hippo.ehviewer.util.setDefaultSettings +import com.ramcosta.composedestinations.annotation.Destination +import com.ramcosta.composedestinations.navigation.DestinationsNavigator private const val APPLY_JS = "javascript:(function(){var apply = document.getElementById(\"apply\").children[0];apply.click();})();" +@Destination @Composable -fun UConfigScreen() { - val navController = LocalNavController.current +fun UConfigScreen(navigator: DestinationsNavigator) { val url = EhUrl.uConfigUrl val webview = remember { Atomic(null) } val snackbarHostState = remember { SnackbarHostState() } @@ -41,7 +42,7 @@ fun UConfigScreen() { TopAppBar( title = { Text(text = stringResource(id = R.string.u_config)) }, navigationIcon = { - IconButton(onClick = { navController.popBackStack() }) { + IconButton(onClick = { navigator.popBackStack() }) { Icon(imageVector = Icons.AutoMirrored.Default.ArrowBack, contentDescription = null) } }, @@ -49,7 +50,7 @@ fun UConfigScreen() { IconButton( onClick = { webview.get()?.loadUrl(APPLY_JS) - navController.popBackStack() + navigator.popBackStack() }, ) { Icon(imageVector = Icons.Default.Check, contentDescription = null) diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/tools/CompositionLocals.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/tools/CompositionLocals.kt new file mode 100644 index 000000000..59a78a6b7 --- /dev/null +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/tools/CompositionLocals.kt @@ -0,0 +1,6 @@ +package com.hippo.ehviewer.ui.tools + +import androidx.compose.material3.windowsizeclass.WindowSizeClass +import androidx.compose.runtime.compositionLocalOf + +val LocalWindowSizeClass = compositionLocalOf { error("CompositionLocal LocalWindowSizeClass not present!") } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ebbee25b1..c5b8de3f8 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -25,6 +25,7 @@ chucker = "4.0.0" coil = "2.5.0" compose-bom = "2023.11.00-alpha01" compose-compiler = "1.5.4-dev-k1.9.20-50f08dfa4b4" +compose-destinations = "1.9.54" google-accompanist = "0.32.0" google-android-material = "1.11.0-beta01" kotlin = "1.9.20" @@ -91,6 +92,9 @@ compose-material3-windowsizeclass = { module = "androidx.compose.material3:mater compose-ui-util = { module = "androidx.compose.ui:ui-util" } compose-ui-viewbinding = { module = "androidx.compose.ui:ui-viewbinding" } +compose-destinations-core = { module = "io.github.raamcosta.compose-destinations:core", version.ref = "compose-destinations" } +compose-destinations-compiler = { module = "io.github.raamcosta.compose-destinations:ksp", version.ref = "compose-destinations" } + cronet-api = "org.chromium.net:cronet-api:113.5672.61" cronet-gms = "com.google.android.gms:play-services-cronet:18.0.1"