diff --git a/app/build.gradle.kts b/app/build.gradle.kts index f32ecac..c976d38 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -79,15 +79,15 @@ dependencies { // AndroidX implementation("androidx.core:core-ktx:1.9.0") implementation("androidx.core:core-splashscreen:1.0.0") - implementation("androidx.activity:activity-compose:1.6.0") - implementation("androidx.paging:paging-compose:1.0.0-alpha16") + implementation("androidx.activity:activity-compose:1.6.1") + implementation("androidx.paging:paging-compose:1.0.0-alpha17") // Compose val composeVersion = "1.3.0-beta02" implementation("androidx.compose.ui:ui:$composeVersion") implementation("androidx.compose.ui:ui-tooling:$composeVersion") implementation("androidx.compose.material:material-icons-extended:$composeVersion") - implementation("androidx.compose.material3:material3:1.0.0-rc01") + implementation("androidx.compose.material3:material3:1.0.0") // accompanist dependencies val accompanistVersion = "0.26.3-beta" @@ -107,17 +107,17 @@ dependencies { implementation("io.insert-koin:koin-androidx-compose:$koinVersion") // Other + implementation("dev.olshevski.navigation:reimagined:1.3.0") implementation("io.coil-kt:coil-compose:2.2.1") - implementation("com.github.X1nto:Taxi:1.2.0") implementation("org.bouncycastle:bcpkix-jdk15on:1.70") implementation("io.github.diamondminer88:zip-android:2.1.0@aar") implementation("com.aliucord:axml:1.0.1") - implementation("com.android.tools.build:apksig:7.4.0-beta02") + implementation("com.android.tools.build:apksig:7.4.0-beta04") implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.0") } fun getCurrentBranch(): String? = - exec("git", "name-rev", "--name-only", "HEAD") + exec("git", "symbolic-ref", "--short", "HEAD") fun getLatestCommit(): String? = exec("git", "rev-parse", "--short", "HEAD") diff --git a/app/src/main/kotlin/com/aliucord/manager/MainActivity.kt b/app/src/main/kotlin/com/aliucord/manager/MainActivity.kt index e694d97..12e41ee 100644 --- a/app/src/main/kotlin/com/aliucord/manager/MainActivity.kt +++ b/app/src/main/kotlin/com/aliucord/manager/MainActivity.kt @@ -11,18 +11,16 @@ import androidx.activity.compose.BackHandler import androidx.activity.compose.setContent import androidx.compose.animation.* import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.ui.Modifier +import androidx.compose.foundation.layout.* import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import com.aliucord.manager.domain.manager.PreferencesManager import com.aliucord.manager.ui.dialog.StoragePermissionsDialog import com.aliucord.manager.ui.dialog.UpdaterDialog -import com.aliucord.manager.ui.navigation.AppDestination -import com.aliucord.manager.ui.navigation.HomeDestination +import com.aliucord.manager.ui.navigation.* import com.aliucord.manager.ui.screen.* import com.aliucord.manager.ui.theme.ManagerTheme import com.aliucord.manager.ui.theme.Theme -import com.xinto.taxi.* +import dev.olshevski.navigation.reimagined.* import org.koin.android.ext.android.inject val aliucordDir = Environment.getExternalStorageDirectory().resolve("Aliucord") @@ -41,16 +39,10 @@ class MainActivity : ComponentActivity() { isDarkTheme = preferences.theme == Theme.DARK || preferences.theme == Theme.SYSTEM && isSystemInDarkTheme(), isDynamicColor = preferences.dynamicColor ) { - val navigator = rememberBackstackNavigator(AppDestination.Home) - val homeRootNavigator = rememberNavigator(HomeDestination.HOME) + val navController = rememberNavController(AppDestination.Home) BackHandler { - if (!navigator.pop()) { - if (homeRootNavigator.currentDestination == HomeDestination.HOME) - finish() - else - homeRootNavigator.replace(HomeDestination.HOME) - } + navController.back() } StoragePermissionsDialog() @@ -64,28 +56,34 @@ class MainActivity : ComponentActivity() { UpdaterDialog() } - Taxi( - modifier = Modifier.fillMaxSize(), - navigator = navigator, - transitionSpec = { fadeIn() with fadeOut() } - ) { destination -> - when (destination) { - is AppDestination.Home -> MainRootScreen( - mainRootNavigator = homeRootNavigator, - onInstallClick = { navigator.push(AppDestination.Install(it)) }, - onAboutClick = { navigator.push(AppDestination.About) } - ) - + AnimatedNavHost( + controller = navController, + transitionSpec = { _, _, _ -> fadeIn() with fadeOut() } + ) { + when (val dest = this.currentHostEntry.destination) { + is BaseScreenDestination -> BaseScreen( + currentNavItem = dest, + bottomNavItems = listOf(AppDestination.Home, AppDestination.Plugins, AppDestination.Settings), + onNavChanged = { navController.navigate(it) } + ) { + when (dest) { + is AppDestination.Home -> HomeScreen( + onClickInstall = { data -> + navController.replaceAll(AppDestination.Install(data)) + } + ) + is AppDestination.Plugins -> PluginsScreen() + is AppDestination.Settings -> SettingsScreen() + } + } is AppDestination.Install -> InstallerScreen( - installData = destination.installData, - onBackClick = navigator::pop + installData = dest.installData, + onBackClick = { navController.back() } ) is AppDestination.About -> AboutScreen( - onBackClick = navigator::pop + onBackClick = { navController.back() } ) - - else -> {} } } } diff --git a/app/src/main/kotlin/com/aliucord/manager/ui/navigation/AppDestination.kt b/app/src/main/kotlin/com/aliucord/manager/ui/navigation/AppDestination.kt index 435ea4d..8f40fc2 100644 --- a/app/src/main/kotlin/com/aliucord/manager/ui/navigation/AppDestination.kt +++ b/app/src/main/kotlin/com/aliucord/manager/ui/navigation/AppDestination.kt @@ -1,5 +1,7 @@ package com.aliucord.manager.ui.navigation +import android.app.Activity +import android.os.Parcelable import androidx.annotation.StringRes import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.* @@ -7,32 +9,58 @@ import androidx.compose.material.icons.outlined.* import androidx.compose.ui.graphics.vector.ImageVector import com.aliucord.manager.R import com.aliucord.manager.ui.screen.InstallData -import com.xinto.taxi.Destination +import dev.olshevski.navigation.reimagined.* import kotlinx.parcelize.Parcelize import kotlinx.parcelize.RawValue @Parcelize -sealed interface AppDestination : Destination { +sealed class BaseScreenDestination( + val selectedIcon: @RawValue ImageVector, + val unselectedIcon: @RawValue ImageVector, + @StringRes val label: Int +) : AppDestination + +@Parcelize +sealed interface AppDestination : Parcelable { @Parcelize - object Home : AppDestination + object Home : BaseScreenDestination( + Icons.Default.Home, + Icons.Outlined.Home, + R.string.navigation_home + ) + + @Parcelize + object Plugins : BaseScreenDestination( + Icons.Default.Extension, + Icons.Outlined.Extension, + R.string.plugins_title + ) + + @Parcelize + object Settings : BaseScreenDestination( + Icons.Default.Settings, + Icons.Outlined.Settings, + R.string.navigation_settings + ) @Parcelize data class Install(val installData: InstallData) : AppDestination @Parcelize object About : AppDestination - - @Parcelize - object Settings : AppDestination } -@Parcelize -enum class HomeDestination( - val selectedIcon: @RawValue ImageVector, - val unselectedIcon: @RawValue ImageVector, - @StringRes val label: Int -) : Destination { - HOME(Icons.Default.Home, Icons.Outlined.Home, R.string.navigation_home), - PLUGINS(Icons.Default.Extension, Icons.Outlined.Extension, R.string.plugins_title), - SETTINGS(Icons.Default.Settings, Icons.Outlined.Settings, R.string.navigation_settings) +context(Activity) +fun NavController.back() { + val topDest = backstack.entries.lastOrNull()?.destination + + if (topDest == AppDestination.Home) { + finish() + } else if (topDest is BaseScreenDestination) { + replaceAll(AppDestination.Home) + } else if (backstack.entries.size > 1) { + pop() + } else { + replaceAll(AppDestination.Home) + } } diff --git a/app/src/main/kotlin/com/aliucord/manager/ui/navigation/BaseScreen.kt b/app/src/main/kotlin/com/aliucord/manager/ui/navigation/BaseScreen.kt new file mode 100644 index 0000000..59a831a --- /dev/null +++ b/app/src/main/kotlin/com/aliucord/manager/ui/navigation/BaseScreen.kt @@ -0,0 +1,83 @@ +@file:OptIn(ExperimentalMaterial3Api::class) + +package com.aliucord.manager.ui.navigation + +import androidx.compose.animation.* +import androidx.compose.foundation.layout.* +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Info +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalUriHandler +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import com.aliucord.manager.BuildConfig +import com.aliucord.manager.R +import com.aliucord.manager.ui.screen.* + +@Composable +fun BaseScreen( + currentNavItem: BaseScreenDestination, + bottomNavItems: List, + onNavChanged: (AppDestination) -> Unit, + content: @Composable () -> Unit +) { + Scaffold( + topBar = { + TopAppBar( + title = { Text(stringResource(currentNavItem.label)) }, + actions = { + val localUriHandler = LocalUriHandler.current + + IconButton( + onClick = { + localUriHandler.openUri("https://discord.gg/${BuildConfig.SUPPORT_SERVER}") + } + ) { + Icon( + painter = painterResource(R.drawable.ic_discord), + contentDescription = stringResource(R.string.support_server) + ) + } + + IconButton(onClick = { onNavChanged(AppDestination.About) }) { + Icon( + imageVector = Icons.Default.Info, + contentDescription = stringResource(R.string.navigation_about) + ) + } + } + ) + }, + bottomBar = { + NavigationBar { + for (navItem in bottomNavItems) { + NavigationBarItem( + selected = currentNavItem == navItem, + icon = { + Icon( + imageVector = if (currentNavItem == navItem) { + navItem.selectedIcon + } else { + navItem.unselectedIcon + }, + contentDescription = null, + ) + }, + label = { Text(stringResource(navItem.label)) }, + onClick = { onNavChanged(navItem) } + ) + } + } + } + ) { paddingValues -> + Box( + modifier = Modifier + .padding(paddingValues) + .fillMaxSize() + ) { + content() + } + } +} diff --git a/app/src/main/kotlin/com/aliucord/manager/ui/screen/HomeRootScreen.kt b/app/src/main/kotlin/com/aliucord/manager/ui/screen/HomeRootScreen.kt deleted file mode 100644 index 8f92d2f..0000000 --- a/app/src/main/kotlin/com/aliucord/manager/ui/screen/HomeRootScreen.kt +++ /dev/null @@ -1,98 +0,0 @@ -package com.aliucord.manager.ui.screen - -import androidx.compose.animation.* -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Info -import androidx.compose.material3.* -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalUriHandler -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource -import com.aliucord.manager.BuildConfig -import com.aliucord.manager.R -import com.aliucord.manager.ui.navigation.HomeDestination -import com.xinto.taxi.RegularNavigator -import com.xinto.taxi.Taxi - -@OptIn(ExperimentalMaterial3Api::class, ExperimentalAnimationApi::class) -@Composable -fun MainRootScreen( - mainRootNavigator: RegularNavigator, - onInstallClick: (InstallData) -> Unit, - onAboutClick: () -> Unit -) { - Scaffold( - topBar = { - TopAppBar( - title = { Text(stringResource(mainRootNavigator.currentDestination.label)) }, - actions = { - val localUriHandler = LocalUriHandler.current - - IconButton( - onClick = { - localUriHandler.openUri("https://discord.gg/${BuildConfig.SUPPORT_SERVER}") - } - ) { - Icon( - painter = painterResource(R.drawable.ic_discord), - contentDescription = stringResource(R.string.support_server) - ) - } - - IconButton(onClick = onAboutClick) { - Icon( - imageVector = Icons.Default.Info, - contentDescription = stringResource(R.string.navigation_about) - ) - } - } - ) - }, - bottomBar = { - val currentDestination = mainRootNavigator.currentDestination - - NavigationBar { - HomeDestination.values().forEach { destination -> - NavigationBarItem( - selected = currentDestination == destination, - icon = { - if (currentDestination == destination) { - Icon( - imageVector = destination.selectedIcon, - contentDescription = stringResource(destination.label) - ) - } else { - Icon( - imageVector = destination.unselectedIcon, - contentDescription = stringResource(destination.label) - ) - } - }, - label = { Text(stringResource(destination.label)) }, - onClick = { mainRootNavigator.replace(destination) } - ) - } - } - } - ) { paddingValues -> - Taxi( - modifier = Modifier - .padding(paddingValues) - .fillMaxSize(), - navigator = mainRootNavigator, - transitionSpec = { fadeIn() with fadeOut() } - ) { destination -> - when (destination) { - HomeDestination.HOME -> HomeScreen( - onClickInstall = onInstallClick - ) - - HomeDestination.PLUGINS -> PluginsScreen() - HomeDestination.SETTINGS -> SettingsScreen() - } - } - } -}