From 1d34ea4995b74b6a88e795debe3c3771ded35107 Mon Sep 17 00:00:00 2001 From: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com> Date: Tue, 3 Jun 2025 01:02:08 +0800 Subject: [PATCH] Rename files and update package structure Add tool classes related to displaying and refreshing data --- .../java/com/sukisu/ultra/ui/MainActivity.kt | 417 ++---------------- .../zakoui/activity/component/BottomBar.kt | 222 ++++++++++ .../zakoui/activity/util/AnimatedBottomBar.kt | 24 + .../zako/zakoui/activity/util}/AppData.kt | 6 +- .../zakoui/activity/util/DataRefreshUtils.kt | 46 ++ .../zako/zakoui/activity/util/DisplayUtils.kt | 24 + .../zako/zakoui/activity/util/LocaleUtils.kt | 48 ++ .../zakoui/activity/util/NavigationUtils.kt | 19 + .../zako/zakoui/activity/util/ThemeUtils.kt | 96 ++++ .../zako/zako/zakoui}/flash/KernelFlash.kt | 2 +- .../zako/zako/zakoui}/screen/KernelFlash.kt | 14 +- .../zako/zako/zakoui}/screen/MoreSettings.kt | 2 +- 12 files changed, 522 insertions(+), 398 deletions(-) create mode 100644 manager/app/src/main/java/zako/zako/zako/zakoui/activity/component/BottomBar.kt create mode 100644 manager/app/src/main/java/zako/zako/zako/zakoui/activity/util/AnimatedBottomBar.kt rename manager/app/src/main/java/{com/sukisu/ultra/ui/data => zako/zako/zako/zakoui/activity/util}/AppData.kt (98%) create mode 100644 manager/app/src/main/java/zako/zako/zako/zakoui/activity/util/DataRefreshUtils.kt create mode 100644 manager/app/src/main/java/zako/zako/zako/zakoui/activity/util/DisplayUtils.kt create mode 100644 manager/app/src/main/java/zako/zako/zako/zakoui/activity/util/LocaleUtils.kt create mode 100644 manager/app/src/main/java/zako/zako/zako/zakoui/activity/util/NavigationUtils.kt create mode 100644 manager/app/src/main/java/zako/zako/zako/zakoui/activity/util/ThemeUtils.kt rename manager/app/src/main/java/{com/sukisu/ultra => zako/zako/zako/zakoui}/flash/KernelFlash.kt (99%) rename manager/app/src/main/java/{com/sukisu/ultra/ui => zako/zako/zako/zakoui}/screen/KernelFlash.kt (97%) rename manager/app/src/main/java/{com/sukisu/ultra/ui => zako/zako/zako/zakoui}/screen/MoreSettings.kt (99%) diff --git a/manager/app/src/main/java/com/sukisu/ultra/ui/MainActivity.kt b/manager/app/src/main/java/com/sukisu/ultra/ui/MainActivity.kt index 4eea49e2..2c1d7a76 100644 --- a/manager/app/src/main/java/com/sukisu/ultra/ui/MainActivity.kt +++ b/manager/app/src/main/java/com/sukisu/ultra/ui/MainActivity.kt @@ -1,60 +1,36 @@ package com.sukisu.ultra.ui -import android.annotation.SuppressLint import android.content.Context import android.content.res.Configuration -import android.database.ContentObserver import android.os.Build import android.os.Bundle -import android.os.Handler import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge -import androidx.compose.animation.* -import androidx.compose.animation.core.tween import androidx.compose.foundation.layout.* import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.stringResource -import androidx.navigation.NavBackStackEntry -import androidx.navigation.NavHostController +import androidx.lifecycle.lifecycleScope +import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.rememberNavController import com.ramcosta.composedestinations.DestinationsNavHost -import com.ramcosta.composedestinations.animations.NavHostAnimatedDestinationStyle import com.ramcosta.composedestinations.generated.NavGraphs import com.ramcosta.composedestinations.generated.destinations.ExecuteModuleActionScreenDestination import com.ramcosta.composedestinations.spec.NavHostGraphSpec -import com.ramcosta.composedestinations.spec.RouteOrDirection -import com.ramcosta.composedestinations.utils.isRouteOnBackStackAsState -import com.ramcosta.composedestinations.utils.rememberDestinationsNavigator import io.sukisu.ultra.UltraToolInstall -import com.sukisu.ultra.Natives import com.sukisu.ultra.ksuApp -import com.sukisu.ultra.ui.data.AppData -import com.sukisu.ultra.ui.screen.BottomBarDestination +import zako.zako.zako.zakoui.activity.util.AppData import com.sukisu.ultra.ui.theme.* -import com.sukisu.ultra.ui.theme.CardConfig.cardAlpha -import com.sukisu.ultra.ui.util.* -import androidx.core.content.edit -import com.sukisu.ultra.ui.data.AppData.DataRefreshManager -import com.sukisu.ultra.ui.theme.CardConfig.cardElevation -import com.sukisu.ultra.ui.webui.initPlatform -import java.util.Locale -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.animation.slideInVertically -import androidx.compose.animation.slideOutVertically -import androidx.navigation.compose.currentBackStackEntryAsState -import androidx.lifecycle.lifecycleScope -import com.sukisu.ultra.ui.data.AppData.getKpmVersionUse +import zako.zako.zako.zakoui.activity.util.* +import zako.zako.zako.zakoui.activity.component.BottomBar +import com.sukisu.ultra.ui.util.LocalSnackbarHost +import com.sukisu.ultra.ui.util.install import com.sukisu.ultra.ui.viewmodel.HomeViewModel import com.sukisu.ultra.ui.viewmodel.SuperUserViewModel -import kotlinx.coroutines.delay -import kotlinx.coroutines.isActive -import kotlinx.coroutines.launch -import kotlinx.coroutines.Dispatchers +import com.sukisu.ultra.ui.webui.initPlatform import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.launch class MainActivity : ComponentActivity() { private lateinit var superUserViewModel: SuperUserViewModel @@ -66,62 +42,19 @@ class MainActivity : ComponentActivity() { val showKpmInfo: Boolean = true ) - private inner class ThemeChangeContentObserver( - handler: Handler, - private val onThemeChanged: () -> Unit - ) : ContentObserver(handler) { - override fun onChange(selfChange: Boolean) { - super.onChange(selfChange) - onThemeChanged() - } - } - - // 应用保存的语言设置 - @SuppressLint("ObsoleteSdkInt") - private fun applyLanguageSetting() { - val prefs = getSharedPreferences("settings", MODE_PRIVATE) - val languageCode = prefs.getString("app_language", "") ?: "" - - if (languageCode.isNotEmpty()) { - val locale = Locale.forLanguageTag(languageCode) - Locale.setDefault(locale) - - val resources = resources - val config = Configuration(resources.configuration) - config.setLocale(locale) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - createConfigurationContext(config) - } else { - @Suppress("DEPRECATION") - resources.updateConfiguration(config, resources.displayMetrics) - } - } - } + private lateinit var themeChangeObserver: ThemeChangeContentObserver override fun attachBaseContext(newBase: Context) { - val prefs = newBase.getSharedPreferences("settings", MODE_PRIVATE) - val languageCode = prefs.getString("app_language", "") ?: "" - - var context = newBase - if (languageCode.isNotEmpty()) { - val locale = Locale.forLanguageTag(languageCode) - Locale.setDefault(locale) - - val config = Configuration(newBase.resources.configuration) - config.setLocale(locale) - context = newBase.createConfigurationContext(config) - } - + val context = LocaleUtils.applyLocale(newBase) super.attachBaseContext(context) } - @SuppressLint("RestrictedApi") override fun onCreate(savedInstanceState: Bundle?) { // 确保应用正确的语言设置 - applyLanguageSetting() + LocaleUtils.applyLanguageSetting(this) // 应用自定义 DPI - applyCustomDpi() + DisplayUtils.applyCustomDpi(this) // Enable edge to edge enableEdgeToEdge() @@ -148,51 +81,15 @@ class MainActivity : ComponentActivity() { } // 数据刷新协程 - startDataRefreshCoroutine() + DataRefreshUtils.startDataRefreshCoroutine(lifecycleScope) - startSettingsMonitorCoroutine() + DataRefreshUtils.startSettingsMonitorCoroutine(lifecycleScope, this, settingsStateFlow) - val prefs = getSharedPreferences("settings", MODE_PRIVATE) - val isFirstRun = prefs.getBoolean("is_first_run", true) + // 初始化主题相关设置 + ThemeUtils.initializeThemeSettings(this, settingsStateFlow) - settingsStateFlow.value = SettingsState( - isHideOtherInfo = prefs.getBoolean("is_hide_other_info", false), - showKpmInfo = prefs.getBoolean("show_kpm_info", true) - ) - - if (isFirstRun) { - ThemeConfig.preventBackgroundRefresh = false - getSharedPreferences("theme_prefs", MODE_PRIVATE).edit { - putBoolean("prevent_background_refresh", false) - } - prefs.edit { putBoolean("is_first_run", false) } - } - - // 加载保存的背景设置 - loadThemeMode() - loadThemeColors() - loadDynamicColorState() - CardConfig.load(applicationContext) - - val contentObserver = ThemeChangeContentObserver(Handler(mainLooper)) { - runOnUiThread { - if (!ThemeConfig.preventBackgroundRefresh) { - ThemeConfig.backgroundImageLoaded = false - loadCustomBackground() - } - } - } - - contentResolver.registerContentObserver( - android.provider.Settings.System.getUriFor("ui_night_mode"), - false, - contentObserver - ) - - val destroyListeners = mutableListOf<() -> Unit>() - destroyListeners.add { - contentResolver.unregisterContentObserver(contentObserver) - } + // 设置主题变化监听器 + themeChangeObserver = ThemeUtils.registerThemeChangeObserver(this) val isManager = AppData.isManager(ksuApp.packageName) if (isManager) { @@ -221,13 +118,10 @@ class MainActivity : ComponentActivity() { ) { Scaffold( bottomBar = { - AnimatedVisibility( - visible = showBottomBar, - enter = slideInVertically(initialOffsetY = { it }) + fadeIn(), - exit = slideOutVertically(targetOffsetY = { it }) + fadeOut() - ) { - BottomBar(navController) - } + AnimatedBottomBar.AnimatedBottomBarWrapper( + showBottomBar = showBottomBar, + content = { BottomBar(navController) } + ) }, contentWindowInsets = WindowInsets(0, 0, 0, 0) ) { innerPadding -> @@ -235,12 +129,7 @@ class MainActivity : ComponentActivity() { modifier = Modifier.padding(innerPadding), navGraph = NavGraphs.root as NavHostGraphSpec, navController = navController, - defaultTransitions = object : NavHostAnimatedDestinationStyle() { - override val enterTransition: AnimatedContentTransitionScope.() -> EnterTransition - get() = { fadeIn(animationSpec = tween(340)) } - override val exitTransition: AnimatedContentTransitionScope.() -> ExitTransition - get() = { fadeOut(animationSpec = tween(340)) } - } + defaultTransitions = NavigationUtils.defaultTransitions() ) } } @@ -248,64 +137,15 @@ class MainActivity : ComponentActivity() { } } - // 数据刷新协程 - private fun startDataRefreshCoroutine() { - lifecycleScope.launch(Dispatchers.IO) { - while (isActive) { - DataRefreshManager.refreshData() - delay(5000) - } - } - } - - private fun startSettingsMonitorCoroutine() { - lifecycleScope.launch(Dispatchers.IO) { - while (isActive) { - val prefs = getSharedPreferences("settings", MODE_PRIVATE) - settingsStateFlow.value = SettingsState( - isHideOtherInfo = prefs.getBoolean("is_hide_other_info", false), - showKpmInfo = prefs.getBoolean("show_kpm_info", true) - ) - delay(1000) - } - } - } - - // 应用自定义DPI设置 - private fun applyCustomDpi() { - val prefs = getSharedPreferences("settings", MODE_PRIVATE) - val customDpi = prefs.getInt("app_dpi", 0) - - if (customDpi > 0) { - try { - val resources = resources - val metrics = resources.displayMetrics - metrics.density = customDpi / 160f - @Suppress("DEPRECATION") - metrics.scaledDensity = customDpi / 160f - metrics.densityDpi = customDpi - } catch (e: Exception) { - e.printStackTrace() - } - } - } - override fun onPause() { super.onPause() - CardConfig.save(applicationContext) - getSharedPreferences("theme_prefs", MODE_PRIVATE).edit { - putBoolean("prevent_background_refresh", true) - } - ThemeConfig.preventBackgroundRefresh = true + ThemeUtils.onActivityPause(this) } override fun onResume() { super.onResume() - applyLanguageSetting() - - if (!ThemeConfig.backgroundImageLoaded && !ThemeConfig.preventBackgroundRefresh) { - loadCustomBackground() - } + LocaleUtils.applyLanguageSetting(this) + ThemeUtils.onActivityResume() lifecycleScope.launch { superUserViewModel.fetchAppList() @@ -315,213 +155,16 @@ class MainActivity : ComponentActivity() { homeViewModel.initializeData() } - lifecycleScope.launch { - DataRefreshManager.refreshData() - } + DataRefreshUtils.refreshData(lifecycleScope) } - private val destroyListeners = mutableListOf<() -> Unit>() - override fun onDestroy() { - destroyListeners.forEach { it() } + ThemeUtils.unregisterThemeChangeObserver(this, themeChangeObserver) super.onDestroy() } override fun onConfigurationChanged(newConfig: Configuration) { super.onConfigurationChanged(newConfig) - applyLanguageSetting() - } -} - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -private fun BottomBar(navController: NavHostController) { - val navigator = navController.rememberDestinationsNavigator() - val isFullFeatured = AppData.isFullFeatured(ksuApp.packageName) - val kpmVersion = getKpmVersionUse() - val cardColor = MaterialTheme.colorScheme.surfaceContainer - val activity = LocalContext.current as MainActivity - val settings by activity.settingsStateFlow.collectAsState() - - // 检查是否隐藏红点 - val isHideOtherInfo = settings.isHideOtherInfo - val showKpmInfo = settings.showKpmInfo - - // 收集计数数据 - val superuserCount by DataRefreshManager.superuserCount.collectAsState() - val moduleCount by DataRefreshManager.moduleCount.collectAsState() - val kpmModuleCount by DataRefreshManager.kpmModuleCount.collectAsState() - - - NavigationBar( - modifier = Modifier.windowInsetsPadding( - WindowInsets.navigationBars.only(WindowInsetsSides.Horizontal) - ), - containerColor = TopAppBarDefaults.topAppBarColors( - containerColor = cardColor.copy(alpha = cardAlpha), - scrolledContainerColor = cardColor.copy(alpha = cardAlpha) - ).containerColor, - tonalElevation = cardElevation - ) { - BottomBarDestination.entries.forEach { destination -> - if (destination == BottomBarDestination.Kpm) { - if (kpmVersion.isNotEmpty() && !kpmVersion.startsWith("Error") && showKpmInfo && Natives.version >= Natives.MINIMAL_SUPPORTED_KPM) { - if (!isFullFeatured && destination.rootRequired) return@forEach - val isCurrentDestOnBackStack by navController.isRouteOnBackStackAsState(destination.direction) - NavigationBarItem( - selected = isCurrentDestOnBackStack, - onClick = { - if (!isCurrentDestOnBackStack) { - navigator.popBackStack(destination.direction, false) - } - navigator.navigate(destination.direction) { - popUpTo(NavGraphs.root as RouteOrDirection) { - saveState = true - } - launchSingleTop = true - restoreState = true - } - }, - icon = { - BadgedBox( - badge = { - if (kpmModuleCount > 0 && !isHideOtherInfo) { - Badge( - containerColor = MaterialTheme.colorScheme.secondary - ) { - Text( - text = kpmModuleCount.toString(), - style = MaterialTheme.typography.labelSmall - ) - } - } - } - ) { - if (isCurrentDestOnBackStack) { - Icon(destination.iconSelected, stringResource(destination.label)) - } else { - Icon(destination.iconNotSelected, stringResource(destination.label)) - } - } - }, - label = { Text(stringResource(destination.label),style = MaterialTheme.typography.labelMedium) }, - alwaysShowLabel = false - ) - } - } else if (destination == BottomBarDestination.SuperUser) { - if (!isFullFeatured && destination.rootRequired) return@forEach - val isCurrentDestOnBackStack by navController.isRouteOnBackStackAsState(destination.direction) - - NavigationBarItem( - selected = isCurrentDestOnBackStack, - onClick = { - if (isCurrentDestOnBackStack) { - navigator.popBackStack(destination.direction, false) - } - navigator.navigate(destination.direction) { - popUpTo(NavGraphs.root) { - saveState = true - } - launchSingleTop = true - restoreState = true - } - }, - icon = { - BadgedBox( - badge = { - if (superuserCount > 0 && !isHideOtherInfo) { - Badge( - containerColor = MaterialTheme.colorScheme.secondary - ) { - Text( - text = superuserCount.toString(), - style = MaterialTheme.typography.labelSmall - ) - } - } - } - ) { - if (isCurrentDestOnBackStack) { - Icon(destination.iconSelected, stringResource(destination.label)) - } else { - Icon(destination.iconNotSelected, stringResource(destination.label)) - } - } - }, - label = { Text(stringResource(destination.label),style = MaterialTheme.typography.labelMedium) }, - alwaysShowLabel = false - ) - } else if (destination == BottomBarDestination.Module) { - if (!isFullFeatured && destination.rootRequired) return@forEach - val isCurrentDestOnBackStack by navController.isRouteOnBackStackAsState(destination.direction) - - NavigationBarItem( - selected = isCurrentDestOnBackStack, - onClick = { - if (isCurrentDestOnBackStack) { - navigator.popBackStack(destination.direction, false) - } - navigator.navigate(destination.direction) { - popUpTo(NavGraphs.root) { - saveState = true - } - launchSingleTop = true - restoreState = true - } - }, - icon = { - BadgedBox( - badge = { - if (moduleCount > 0 && !isHideOtherInfo) { - Badge( - containerColor = MaterialTheme.colorScheme.secondary ) { - Text( - text = moduleCount.toString(), - style = MaterialTheme.typography.labelSmall - ) - } - } - } - ) { - if (isCurrentDestOnBackStack) { - Icon(destination.iconSelected, stringResource(destination.label)) - } else { - Icon(destination.iconNotSelected, stringResource(destination.label)) - } - } - }, - label = { Text(stringResource(destination.label),style = MaterialTheme.typography.labelMedium) }, - alwaysShowLabel = false - ) - } else { - if (!isFullFeatured && destination.rootRequired) return@forEach - val isCurrentDestOnBackStack by navController.isRouteOnBackStackAsState(destination.direction) - - NavigationBarItem( - selected = isCurrentDestOnBackStack, - onClick = { - if (isCurrentDestOnBackStack) { - navigator.popBackStack(destination.direction, false) - } - navigator.navigate(destination.direction) { - popUpTo(NavGraphs.root) { - saveState = true - } - launchSingleTop = true - restoreState = true - } - }, - icon = { - if (isCurrentDestOnBackStack) { - Icon(destination.iconSelected, stringResource(destination.label)) - } else { - Icon(destination.iconNotSelected, stringResource(destination.label)) - } - }, - label = { Text(stringResource(destination.label),style = MaterialTheme.typography.labelMedium) }, - alwaysShowLabel = false - ) - } - } + LocaleUtils.applyLanguageSetting(this) } } \ No newline at end of file diff --git a/manager/app/src/main/java/zako/zako/zako/zakoui/activity/component/BottomBar.kt b/manager/app/src/main/java/zako/zako/zako/zakoui/activity/component/BottomBar.kt new file mode 100644 index 00000000..4ffda32c --- /dev/null +++ b/manager/app/src/main/java/zako/zako/zako/zakoui/activity/component/BottomBar.kt @@ -0,0 +1,222 @@ +package zako.zako.zako.zakoui.activity.component + +import android.annotation.SuppressLint +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.navigation.NavHostController +import com.ramcosta.composedestinations.utils.isRouteOnBackStackAsState +import com.ramcosta.composedestinations.utils.rememberDestinationsNavigator +import com.ramcosta.composedestinations.spec.RouteOrDirection +import com.ramcosta.composedestinations.generated.NavGraphs +import com.sukisu.ultra.Natives +import com.sukisu.ultra.ksuApp +import com.sukisu.ultra.ui.MainActivity +import zako.zako.zako.zakoui.activity.util.AppData +import zako.zako.zako.zakoui.activity.util.AppData.getKpmVersionUse +import com.sukisu.ultra.ui.screen.BottomBarDestination +import com.sukisu.ultra.ui.theme.CardConfig.cardAlpha +import com.sukisu.ultra.ui.theme.CardConfig.cardElevation +import androidx.compose.foundation.layout.windowInsetsPadding +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.WindowInsetsSides +import androidx.compose.foundation.layout.only +import androidx.compose.foundation.layout.navigationBars +import zako.zako.zako.zakoui.activity.util.AppData.DataRefreshManager + +@SuppressLint("ContextCastToActivity") +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun BottomBar(navController: NavHostController) { + val navigator = navController.rememberDestinationsNavigator() + val isFullFeatured = AppData.isFullFeatured(ksuApp.packageName) + val kpmVersion = getKpmVersionUse() + val cardColor = MaterialTheme.colorScheme.surfaceContainer + val activity = LocalContext.current as MainActivity + val settings by activity.settingsStateFlow.collectAsState() + + // 检查是否隐藏红点 + val isHideOtherInfo = settings.isHideOtherInfo + val showKpmInfo = settings.showKpmInfo + + // 收集计数数据 + val superuserCount by DataRefreshManager.superuserCount.collectAsState() + val moduleCount by DataRefreshManager.moduleCount.collectAsState() + val kpmModuleCount by DataRefreshManager.kpmModuleCount.collectAsState() + + + NavigationBar( + modifier = Modifier.windowInsetsPadding( + WindowInsets.navigationBars.only(WindowInsetsSides.Horizontal) + ), + containerColor = TopAppBarDefaults.topAppBarColors( + containerColor = cardColor.copy(alpha = cardAlpha), + scrolledContainerColor = cardColor.copy(alpha = cardAlpha) + ).containerColor, + tonalElevation = cardElevation + ) { + BottomBarDestination.entries.forEach { destination -> + if (destination == BottomBarDestination.Kpm) { + if (kpmVersion.isNotEmpty() && !kpmVersion.startsWith("Error") && showKpmInfo && Natives.version >= Natives.MINIMAL_SUPPORTED_KPM) { + if (!isFullFeatured && destination.rootRequired) return@forEach + val isCurrentDestOnBackStack by navController.isRouteOnBackStackAsState(destination.direction) + NavigationBarItem( + selected = isCurrentDestOnBackStack, + onClick = { + if (!isCurrentDestOnBackStack) { + navigator.popBackStack(destination.direction, false) + } + navigator.navigate(destination.direction) { + popUpTo(NavGraphs.root as RouteOrDirection) { + saveState = true + } + launchSingleTop = true + restoreState = true + } + }, + icon = { + BadgedBox( + badge = { + if (kpmModuleCount > 0 && !isHideOtherInfo) { + Badge( + containerColor = MaterialTheme.colorScheme.secondary + ) { + Text( + text = kpmModuleCount.toString(), + style = MaterialTheme.typography.labelSmall + ) + } + } + } + ) { + if (isCurrentDestOnBackStack) { + Icon(destination.iconSelected, stringResource(destination.label)) + } else { + Icon(destination.iconNotSelected, stringResource(destination.label)) + } + } + }, + label = { Text(stringResource(destination.label),style = MaterialTheme.typography.labelMedium) }, + alwaysShowLabel = false + ) + } + } else if (destination == BottomBarDestination.SuperUser) { + if (!isFullFeatured && destination.rootRequired) return@forEach + val isCurrentDestOnBackStack by navController.isRouteOnBackStackAsState(destination.direction) + + NavigationBarItem( + selected = isCurrentDestOnBackStack, + onClick = { + if (isCurrentDestOnBackStack) { + navigator.popBackStack(destination.direction, false) + } + navigator.navigate(destination.direction) { + popUpTo(NavGraphs.root) { + saveState = true + } + launchSingleTop = true + restoreState = true + } + }, + icon = { + BadgedBox( + badge = { + if (superuserCount > 0 && !isHideOtherInfo) { + Badge( + containerColor = MaterialTheme.colorScheme.secondary + ) { + Text( + text = superuserCount.toString(), + style = MaterialTheme.typography.labelSmall + ) + } + } + } + ) { + if (isCurrentDestOnBackStack) { + Icon(destination.iconSelected, stringResource(destination.label)) + } else { + Icon(destination.iconNotSelected, stringResource(destination.label)) + } + } + }, + label = { Text(stringResource(destination.label),style = MaterialTheme.typography.labelMedium) }, + alwaysShowLabel = false + ) + } else if (destination == BottomBarDestination.Module) { + if (!isFullFeatured && destination.rootRequired) return@forEach + val isCurrentDestOnBackStack by navController.isRouteOnBackStackAsState(destination.direction) + + NavigationBarItem( + selected = isCurrentDestOnBackStack, + onClick = { + if (isCurrentDestOnBackStack) { + navigator.popBackStack(destination.direction, false) + } + navigator.navigate(destination.direction) { + popUpTo(NavGraphs.root) { + saveState = true + } + launchSingleTop = true + restoreState = true + } + }, + icon = { + BadgedBox( + badge = { + if (moduleCount > 0 && !isHideOtherInfo) { + Badge( + containerColor = MaterialTheme.colorScheme.secondary) + { + Text( + text = moduleCount.toString(), + style = MaterialTheme.typography.labelSmall + ) + } + } + } + ) { + if (isCurrentDestOnBackStack) { + Icon(destination.iconSelected, stringResource(destination.label)) + } else { + Icon(destination.iconNotSelected, stringResource(destination.label)) + } + } + }, + label = { Text(stringResource(destination.label),style = MaterialTheme.typography.labelMedium) }, + alwaysShowLabel = false + ) + } else { + if (!isFullFeatured && destination.rootRequired) return@forEach + val isCurrentDestOnBackStack by navController.isRouteOnBackStackAsState(destination.direction) + + NavigationBarItem( + selected = isCurrentDestOnBackStack, + onClick = { + if (isCurrentDestOnBackStack) { + navigator.popBackStack(destination.direction, false) + } + navigator.navigate(destination.direction) { + popUpTo(NavGraphs.root) { + saveState = true + } + launchSingleTop = true + restoreState = true + } + }, + icon = { + if (isCurrentDestOnBackStack) { + Icon(destination.iconSelected, stringResource(destination.label)) + } else { + Icon(destination.iconNotSelected, stringResource(destination.label)) + } + }, + label = { Text(stringResource(destination.label),style = MaterialTheme.typography.labelMedium) }, + alwaysShowLabel = false + ) + } + } + } +} \ No newline at end of file diff --git a/manager/app/src/main/java/zako/zako/zako/zakoui/activity/util/AnimatedBottomBar.kt b/manager/app/src/main/java/zako/zako/zako/zakoui/activity/util/AnimatedBottomBar.kt new file mode 100644 index 00000000..51e7f64d --- /dev/null +++ b/manager/app/src/main/java/zako/zako/zako/zakoui/activity/util/AnimatedBottomBar.kt @@ -0,0 +1,24 @@ +package zako.zako.zako.zakoui.activity.util + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.slideInVertically +import androidx.compose.animation.slideOutVertically +import androidx.compose.runtime.Composable + +object AnimatedBottomBar { + @Composable + fun AnimatedBottomBarWrapper( + showBottomBar: Boolean, + content: @Composable () -> Unit + ) { + AnimatedVisibility( + visible = showBottomBar, + enter = slideInVertically(initialOffsetY = { it }) + fadeIn(), + exit = slideOutVertically(targetOffsetY = { it }) + fadeOut() + ) { + content() + } + } +} \ No newline at end of file diff --git a/manager/app/src/main/java/com/sukisu/ultra/ui/data/AppData.kt b/manager/app/src/main/java/zako/zako/zako/zakoui/activity/util/AppData.kt similarity index 98% rename from manager/app/src/main/java/com/sukisu/ultra/ui/data/AppData.kt rename to manager/app/src/main/java/zako/zako/zako/zakoui/activity/util/AppData.kt index eff53f10..1ec67f81 100644 --- a/manager/app/src/main/java/com/sukisu/ultra/ui/data/AppData.kt +++ b/manager/app/src/main/java/zako/zako/zako/zakoui/activity/util/AppData.kt @@ -1,16 +1,16 @@ -package com.sukisu.ultra.ui.data +package zako.zako.zako.zakoui.activity.util import com.sukisu.ultra.Natives import com.sukisu.ultra.ui.util.getKpmModuleCount import com.sukisu.ultra.ui.util.getKpmVersion +import com.sukisu.ultra.ui.util.getModuleCount +import com.sukisu.ultra.ui.util.getSuperuserCount import com.sukisu.ultra.ui.util.rootAvailable import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.withContext -import com.sukisu.ultra.ui.util.getModuleCount -import com.sukisu.ultra.ui.util.getSuperuserCount object AppData { object DataRefreshManager { diff --git a/manager/app/src/main/java/zako/zako/zako/zakoui/activity/util/DataRefreshUtils.kt b/manager/app/src/main/java/zako/zako/zako/zakoui/activity/util/DataRefreshUtils.kt new file mode 100644 index 00000000..143a293f --- /dev/null +++ b/manager/app/src/main/java/zako/zako/zako/zakoui/activity/util/DataRefreshUtils.kt @@ -0,0 +1,46 @@ +package zako.zako.zako.zakoui.activity.util + +import android.content.Context +import androidx.lifecycle.LifecycleCoroutineScope +import com.sukisu.ultra.ui.MainActivity +import zako.zako.zako.zakoui.activity.util.AppData.DataRefreshManager +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.isActive +import kotlinx.coroutines.launch + +object DataRefreshUtils { + + fun startDataRefreshCoroutine(scope: LifecycleCoroutineScope) { + scope.launch(Dispatchers.IO) { + while (isActive) { + DataRefreshManager.refreshData() + delay(5000) + } + } + } + + fun startSettingsMonitorCoroutine( + scope: LifecycleCoroutineScope, + activity: MainActivity, + settingsStateFlow: MutableStateFlow + ) { + scope.launch(Dispatchers.IO) { + while (isActive) { + val prefs = activity.getSharedPreferences("settings", Context.MODE_PRIVATE) + settingsStateFlow.value = MainActivity.SettingsState( + isHideOtherInfo = prefs.getBoolean("is_hide_other_info", false), + showKpmInfo = prefs.getBoolean("show_kpm_info", true) + ) + delay(1000) + } + } + } + + fun refreshData(scope: LifecycleCoroutineScope) { + scope.launch { + DataRefreshManager.refreshData() + } + } +} \ No newline at end of file diff --git a/manager/app/src/main/java/zako/zako/zako/zakoui/activity/util/DisplayUtils.kt b/manager/app/src/main/java/zako/zako/zako/zakoui/activity/util/DisplayUtils.kt new file mode 100644 index 00000000..bd6c1282 --- /dev/null +++ b/manager/app/src/main/java/zako/zako/zako/zakoui/activity/util/DisplayUtils.kt @@ -0,0 +1,24 @@ +package zako.zako.zako.zakoui.activity.util + +import android.content.Context + +object DisplayUtils { + + fun applyCustomDpi(context: Context) { + val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE) + val customDpi = prefs.getInt("app_dpi", 0) + + if (customDpi > 0) { + try { + val resources = context.resources + val metrics = resources.displayMetrics + metrics.density = customDpi / 160f + @Suppress("DEPRECATION") + metrics.scaledDensity = customDpi / 160f + metrics.densityDpi = customDpi + } catch (e: Exception) { + e.printStackTrace() + } + } + } +} \ No newline at end of file diff --git a/manager/app/src/main/java/zako/zako/zako/zakoui/activity/util/LocaleUtils.kt b/manager/app/src/main/java/zako/zako/zako/zakoui/activity/util/LocaleUtils.kt new file mode 100644 index 00000000..9ccd08b4 --- /dev/null +++ b/manager/app/src/main/java/zako/zako/zako/zakoui/activity/util/LocaleUtils.kt @@ -0,0 +1,48 @@ +package zako.zako.zako.zakoui.activity.util + +import android.annotation.SuppressLint +import android.content.Context +import android.content.res.Configuration +import android.os.Build +import java.util.Locale + +object LocaleUtils { + + @SuppressLint("ObsoleteSdkInt") + fun applyLanguageSetting(context: Context) { + val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE) + val languageCode = prefs.getString("app_language", "") ?: "" + + if (languageCode.isNotEmpty()) { + val locale = Locale.forLanguageTag(languageCode) + Locale.setDefault(locale) + + val resources = context.resources + val config = Configuration(resources.configuration) + config.setLocale(locale) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + context.createConfigurationContext(config) + } else { + @Suppress("DEPRECATION") + resources.updateConfiguration(config, resources.displayMetrics) + } + } + } + + fun applyLocale(context: Context): Context { + val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE) + val languageCode = prefs.getString("app_language", "") ?: "" + + var newContext = context + if (languageCode.isNotEmpty()) { + val locale = Locale.forLanguageTag(languageCode) + Locale.setDefault(locale) + + val config = Configuration(context.resources.configuration) + config.setLocale(locale) + newContext = context.createConfigurationContext(config) + } + + return newContext + } +} \ No newline at end of file diff --git a/manager/app/src/main/java/zako/zako/zako/zakoui/activity/util/NavigationUtils.kt b/manager/app/src/main/java/zako/zako/zako/zakoui/activity/util/NavigationUtils.kt new file mode 100644 index 00000000..31112416 --- /dev/null +++ b/manager/app/src/main/java/zako/zako/zako/zakoui/activity/util/NavigationUtils.kt @@ -0,0 +1,19 @@ +package zako.zako.zako.zakoui.activity.util + +import androidx.compose.animation.AnimatedContentTransitionScope +import androidx.compose.animation.EnterTransition +import androidx.compose.animation.ExitTransition +import androidx.compose.animation.core.tween +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.navigation.NavBackStackEntry +import com.ramcosta.composedestinations.animations.NavHostAnimatedDestinationStyle + +object NavigationUtils { + fun defaultTransitions() = object : NavHostAnimatedDestinationStyle() { + override val enterTransition: AnimatedContentTransitionScope.() -> EnterTransition + get() = { fadeIn(animationSpec = tween(340)) } + override val exitTransition: AnimatedContentTransitionScope.() -> ExitTransition + get() = { fadeOut(animationSpec = tween(340)) } + } +} \ No newline at end of file diff --git a/manager/app/src/main/java/zako/zako/zako/zakoui/activity/util/ThemeUtils.kt b/manager/app/src/main/java/zako/zako/zako/zakoui/activity/util/ThemeUtils.kt new file mode 100644 index 00000000..df3880f9 --- /dev/null +++ b/manager/app/src/main/java/zako/zako/zako/zakoui/activity/util/ThemeUtils.kt @@ -0,0 +1,96 @@ +package zako.zako.zako.zakoui.activity.util + +import android.content.Context +import android.database.ContentObserver +import android.os.Handler +import androidx.core.content.edit +import com.sukisu.ultra.ui.MainActivity +import com.sukisu.ultra.ui.theme.CardConfig +import com.sukisu.ultra.ui.theme.ThemeConfig +import kotlinx.coroutines.flow.MutableStateFlow + +class ThemeChangeContentObserver( + handler: Handler, + private val onThemeChanged: () -> Unit +) : ContentObserver(handler) { + override fun onChange(selfChange: Boolean) { + super.onChange(selfChange) + onThemeChanged() + } +} + +object ThemeUtils { + + fun initializeThemeSettings(activity: MainActivity, settingsStateFlow: MutableStateFlow) { + val prefs = activity.getSharedPreferences("settings", Context.MODE_PRIVATE) + val isFirstRun = prefs.getBoolean("is_first_run", true) + + settingsStateFlow.value = MainActivity.SettingsState( + isHideOtherInfo = prefs.getBoolean("is_hide_other_info", false), + showKpmInfo = prefs.getBoolean("show_kpm_info", true) + ) + + if (isFirstRun) { + ThemeConfig.preventBackgroundRefresh = false + activity.getSharedPreferences("theme_prefs", Context.MODE_PRIVATE).edit { + putBoolean("prevent_background_refresh", false) + } + prefs.edit { putBoolean("is_first_run", false) } + } + + // 加载保存的背景设置 + loadThemeMode() + loadThemeColors() + loadDynamicColorState() + CardConfig.load(activity.applicationContext) + } + + fun registerThemeChangeObserver(activity: MainActivity): ThemeChangeContentObserver { + val contentObserver = ThemeChangeContentObserver(Handler(activity.mainLooper)) { + activity.runOnUiThread { + if (!ThemeConfig.preventBackgroundRefresh) { + ThemeConfig.backgroundImageLoaded = false + loadCustomBackground() + } + } + } + + activity.contentResolver.registerContentObserver( + android.provider.Settings.System.getUriFor("ui_night_mode"), + false, + contentObserver + ) + + return contentObserver + } + + fun unregisterThemeChangeObserver(activity: MainActivity, observer: ThemeChangeContentObserver) { + activity.contentResolver.unregisterContentObserver(observer) + } + + fun onActivityPause(activity: MainActivity) { + CardConfig.save(activity.applicationContext) + activity.getSharedPreferences("theme_prefs", Context.MODE_PRIVATE).edit { + putBoolean("prevent_background_refresh", true) + } + ThemeConfig.preventBackgroundRefresh = true + } + + fun onActivityResume() { + if (!ThemeConfig.backgroundImageLoaded && !ThemeConfig.preventBackgroundRefresh) { + loadCustomBackground() + } + } + + private fun loadThemeMode() { + } + + private fun loadThemeColors() { + } + + private fun loadDynamicColorState() { + } + + private fun loadCustomBackground() { + } +} \ No newline at end of file diff --git a/manager/app/src/main/java/com/sukisu/ultra/flash/KernelFlash.kt b/manager/app/src/main/java/zako/zako/zako/zakoui/flash/KernelFlash.kt similarity index 99% rename from manager/app/src/main/java/com/sukisu/ultra/flash/KernelFlash.kt rename to manager/app/src/main/java/zako/zako/zako/zakoui/flash/KernelFlash.kt index f584e9ec..b71b0ef1 100644 --- a/manager/app/src/main/java/com/sukisu/ultra/flash/KernelFlash.kt +++ b/manager/app/src/main/java/zako/zako/zako/zakoui/flash/KernelFlash.kt @@ -1,4 +1,4 @@ -package com.sukisu.ultra.flash +package zako.zako.zako.zakoui.flash import android.app.Activity import android.content.Context diff --git a/manager/app/src/main/java/com/sukisu/ultra/ui/screen/KernelFlash.kt b/manager/app/src/main/java/zako/zako/zako/zakoui/screen/KernelFlash.kt similarity index 97% rename from manager/app/src/main/java/com/sukisu/ultra/ui/screen/KernelFlash.kt rename to manager/app/src/main/java/zako/zako/zako/zakoui/screen/KernelFlash.kt index 512289a6..d695199a 100644 --- a/manager/app/src/main/java/com/sukisu/ultra/ui/screen/KernelFlash.kt +++ b/manager/app/src/main/java/zako/zako/zako/zakoui/screen/KernelFlash.kt @@ -1,4 +1,4 @@ -package com.sukisu.ultra.ui.screen +package zako.zako.zako.zakoui.screen import android.net.Uri import android.os.Environment @@ -29,8 +29,8 @@ import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.annotation.RootGraph import com.ramcosta.composedestinations.navigation.DestinationsNavigator import com.sukisu.ultra.R -import com.sukisu.ultra.flash.HorizonKernelState -import com.sukisu.ultra.flash.HorizonKernelWorker +import zako.zako.zako.zakoui.flash.HorizonKernelState +import zako.zako.zako.zakoui.flash.HorizonKernelWorker import com.sukisu.ultra.ui.component.KeyEventBlocker import com.sukisu.ultra.ui.util.* import kotlinx.coroutines.Dispatchers @@ -42,6 +42,8 @@ import java.util.* import androidx.compose.ui.input.key.Key import androidx.compose.ui.input.key.key import com.sukisu.ultra.ui.theme.CardConfig +import zako.zako.zako.zakoui.flash.FlashState +import kotlinx.coroutines.delay /** * @author ShirkNeko @@ -117,7 +119,7 @@ fun KernelFlashScreen( logContent.clear() logContent.append(logText) } - kotlinx.coroutines.delay(100) + delay(100) } if (flashState.error.isNotEmpty()) { @@ -239,7 +241,7 @@ fun KernelFlashScreen( } @Composable -private fun FlashProgressIndicator(flashState: com.sukisu.ultra.flash.FlashState) { +private fun FlashProgressIndicator(flashState: FlashState) { val progressColor = when { flashState.error.isNotEmpty() -> MaterialTheme.colorScheme.error flashState.isCompleted -> MaterialTheme.colorScheme.tertiary @@ -355,7 +357,7 @@ private fun FlashProgressIndicator(flashState: com.sukisu.ultra.flash.FlashState @OptIn(ExperimentalMaterial3Api::class) @Composable private fun TopBar( - flashState: com.sukisu.ultra.flash.FlashState, + flashState: FlashState, onBack: () -> Unit, onSave: () -> Unit = {}, scrollBehavior: TopAppBarScrollBehavior? = null diff --git a/manager/app/src/main/java/com/sukisu/ultra/ui/screen/MoreSettings.kt b/manager/app/src/main/java/zako/zako/zako/zakoui/screen/MoreSettings.kt similarity index 99% rename from manager/app/src/main/java/com/sukisu/ultra/ui/screen/MoreSettings.kt rename to manager/app/src/main/java/zako/zako/zako/zakoui/screen/MoreSettings.kt index 8b6728cc..6b03ed31 100644 --- a/manager/app/src/main/java/com/sukisu/ultra/ui/screen/MoreSettings.kt +++ b/manager/app/src/main/java/zako/zako/zako/zakoui/screen/MoreSettings.kt @@ -1,4 +1,4 @@ -package com.sukisu.ultra.ui.screen +package zako.zako.zako.zakoui.screen import android.annotation.SuppressLint import android.app.Activity