Rename files and update package structure
Add tool classes related to displaying and refreshing data
This commit is contained in:
@@ -1,60 +1,36 @@
|
|||||||
package com.sukisu.ultra.ui
|
package com.sukisu.ultra.ui
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import android.database.ContentObserver
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Handler
|
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.activity.enableEdgeToEdge
|
import androidx.activity.enableEdgeToEdge
|
||||||
import androidx.compose.animation.*
|
|
||||||
import androidx.compose.animation.core.tween
|
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.navigation.compose.currentBackStackEntryAsState
|
||||||
import androidx.navigation.NavBackStackEntry
|
|
||||||
import androidx.navigation.NavHostController
|
|
||||||
import androidx.navigation.compose.rememberNavController
|
import androidx.navigation.compose.rememberNavController
|
||||||
import com.ramcosta.composedestinations.DestinationsNavHost
|
import com.ramcosta.composedestinations.DestinationsNavHost
|
||||||
import com.ramcosta.composedestinations.animations.NavHostAnimatedDestinationStyle
|
|
||||||
import com.ramcosta.composedestinations.generated.NavGraphs
|
import com.ramcosta.composedestinations.generated.NavGraphs
|
||||||
import com.ramcosta.composedestinations.generated.destinations.ExecuteModuleActionScreenDestination
|
import com.ramcosta.composedestinations.generated.destinations.ExecuteModuleActionScreenDestination
|
||||||
import com.ramcosta.composedestinations.spec.NavHostGraphSpec
|
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 io.sukisu.ultra.UltraToolInstall
|
||||||
import com.sukisu.ultra.Natives
|
|
||||||
import com.sukisu.ultra.ksuApp
|
import com.sukisu.ultra.ksuApp
|
||||||
import com.sukisu.ultra.ui.data.AppData
|
import zako.zako.zako.zakoui.activity.util.AppData
|
||||||
import com.sukisu.ultra.ui.screen.BottomBarDestination
|
|
||||||
import com.sukisu.ultra.ui.theme.*
|
import com.sukisu.ultra.ui.theme.*
|
||||||
import com.sukisu.ultra.ui.theme.CardConfig.cardAlpha
|
import zako.zako.zako.zakoui.activity.util.*
|
||||||
import com.sukisu.ultra.ui.util.*
|
import zako.zako.zako.zakoui.activity.component.BottomBar
|
||||||
import androidx.core.content.edit
|
import com.sukisu.ultra.ui.util.LocalSnackbarHost
|
||||||
import com.sukisu.ultra.ui.data.AppData.DataRefreshManager
|
import com.sukisu.ultra.ui.util.install
|
||||||
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 com.sukisu.ultra.ui.viewmodel.HomeViewModel
|
import com.sukisu.ultra.ui.viewmodel.HomeViewModel
|
||||||
import com.sukisu.ultra.ui.viewmodel.SuperUserViewModel
|
import com.sukisu.ultra.ui.viewmodel.SuperUserViewModel
|
||||||
import kotlinx.coroutines.delay
|
import com.sukisu.ultra.ui.webui.initPlatform
|
||||||
import kotlinx.coroutines.isActive
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
private lateinit var superUserViewModel: SuperUserViewModel
|
private lateinit var superUserViewModel: SuperUserViewModel
|
||||||
@@ -66,62 +42,19 @@ class MainActivity : ComponentActivity() {
|
|||||||
val showKpmInfo: Boolean = true
|
val showKpmInfo: Boolean = true
|
||||||
)
|
)
|
||||||
|
|
||||||
private inner class ThemeChangeContentObserver(
|
private lateinit var themeChangeObserver: 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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun attachBaseContext(newBase: Context) {
|
override fun attachBaseContext(newBase: Context) {
|
||||||
val prefs = newBase.getSharedPreferences("settings", MODE_PRIVATE)
|
val context = LocaleUtils.applyLocale(newBase)
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
super.attachBaseContext(context)
|
super.attachBaseContext(context)
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("RestrictedApi")
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
// 确保应用正确的语言设置
|
// 确保应用正确的语言设置
|
||||||
applyLanguageSetting()
|
LocaleUtils.applyLanguageSetting(this)
|
||||||
|
|
||||||
// 应用自定义 DPI
|
// 应用自定义 DPI
|
||||||
applyCustomDpi()
|
DisplayUtils.applyCustomDpi(this)
|
||||||
|
|
||||||
// Enable edge to edge
|
// Enable edge to edge
|
||||||
enableEdgeToEdge()
|
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),
|
themeChangeObserver = ThemeUtils.registerThemeChangeObserver(this)
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
val isManager = AppData.isManager(ksuApp.packageName)
|
val isManager = AppData.isManager(ksuApp.packageName)
|
||||||
if (isManager) {
|
if (isManager) {
|
||||||
@@ -221,13 +118,10 @@ class MainActivity : ComponentActivity() {
|
|||||||
) {
|
) {
|
||||||
Scaffold(
|
Scaffold(
|
||||||
bottomBar = {
|
bottomBar = {
|
||||||
AnimatedVisibility(
|
AnimatedBottomBar.AnimatedBottomBarWrapper(
|
||||||
visible = showBottomBar,
|
showBottomBar = showBottomBar,
|
||||||
enter = slideInVertically(initialOffsetY = { it }) + fadeIn(),
|
content = { BottomBar(navController) }
|
||||||
exit = slideOutVertically(targetOffsetY = { it }) + fadeOut()
|
)
|
||||||
) {
|
|
||||||
BottomBar(navController)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
contentWindowInsets = WindowInsets(0, 0, 0, 0)
|
contentWindowInsets = WindowInsets(0, 0, 0, 0)
|
||||||
) { innerPadding ->
|
) { innerPadding ->
|
||||||
@@ -235,12 +129,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
modifier = Modifier.padding(innerPadding),
|
modifier = Modifier.padding(innerPadding),
|
||||||
navGraph = NavGraphs.root as NavHostGraphSpec,
|
navGraph = NavGraphs.root as NavHostGraphSpec,
|
||||||
navController = navController,
|
navController = navController,
|
||||||
defaultTransitions = object : NavHostAnimatedDestinationStyle() {
|
defaultTransitions = NavigationUtils.defaultTransitions()
|
||||||
override val enterTransition: AnimatedContentTransitionScope<NavBackStackEntry>.() -> EnterTransition
|
|
||||||
get() = { fadeIn(animationSpec = tween(340)) }
|
|
||||||
override val exitTransition: AnimatedContentTransitionScope<NavBackStackEntry>.() -> ExitTransition
|
|
||||||
get() = { fadeOut(animationSpec = tween(340)) }
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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() {
|
override fun onPause() {
|
||||||
super.onPause()
|
super.onPause()
|
||||||
CardConfig.save(applicationContext)
|
ThemeUtils.onActivityPause(this)
|
||||||
getSharedPreferences("theme_prefs", MODE_PRIVATE).edit {
|
|
||||||
putBoolean("prevent_background_refresh", true)
|
|
||||||
}
|
|
||||||
ThemeConfig.preventBackgroundRefresh = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
applyLanguageSetting()
|
LocaleUtils.applyLanguageSetting(this)
|
||||||
|
ThemeUtils.onActivityResume()
|
||||||
if (!ThemeConfig.backgroundImageLoaded && !ThemeConfig.preventBackgroundRefresh) {
|
|
||||||
loadCustomBackground()
|
|
||||||
}
|
|
||||||
|
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
superUserViewModel.fetchAppList()
|
superUserViewModel.fetchAppList()
|
||||||
@@ -315,213 +155,16 @@ class MainActivity : ComponentActivity() {
|
|||||||
homeViewModel.initializeData()
|
homeViewModel.initializeData()
|
||||||
}
|
}
|
||||||
|
|
||||||
lifecycleScope.launch {
|
DataRefreshUtils.refreshData(lifecycleScope)
|
||||||
DataRefreshManager.refreshData()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private val destroyListeners = mutableListOf<() -> Unit>()
|
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
destroyListeners.forEach { it() }
|
ThemeUtils.unregisterThemeChangeObserver(this, themeChangeObserver)
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||||
super.onConfigurationChanged(newConfig)
|
super.onConfigurationChanged(newConfig)
|
||||||
applyLanguageSetting()
|
LocaleUtils.applyLanguageSetting(this)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@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
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.Natives
|
||||||
import com.sukisu.ultra.ui.util.getKpmModuleCount
|
import com.sukisu.ultra.ui.util.getKpmModuleCount
|
||||||
import com.sukisu.ultra.ui.util.getKpmVersion
|
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 com.sukisu.ultra.ui.util.rootAvailable
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import com.sukisu.ultra.ui.util.getModuleCount
|
|
||||||
import com.sukisu.ultra.ui.util.getSuperuserCount
|
|
||||||
|
|
||||||
object AppData {
|
object AppData {
|
||||||
object DataRefreshManager {
|
object DataRefreshManager {
|
||||||
@@ -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<MainActivity.SettingsState>
|
||||||
|
) {
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<NavBackStackEntry>.() -> EnterTransition
|
||||||
|
get() = { fadeIn(animationSpec = tween(340)) }
|
||||||
|
override val exitTransition: AnimatedContentTransitionScope<NavBackStackEntry>.() -> ExitTransition
|
||||||
|
get() = { fadeOut(animationSpec = tween(340)) }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<MainActivity.SettingsState>) {
|
||||||
|
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() {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.sukisu.ultra.flash
|
package zako.zako.zako.zakoui.flash
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.sukisu.ultra.ui.screen
|
package zako.zako.zako.zakoui.screen
|
||||||
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
@@ -29,8 +29,8 @@ import com.ramcosta.composedestinations.annotation.Destination
|
|||||||
import com.ramcosta.composedestinations.annotation.RootGraph
|
import com.ramcosta.composedestinations.annotation.RootGraph
|
||||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||||
import com.sukisu.ultra.R
|
import com.sukisu.ultra.R
|
||||||
import com.sukisu.ultra.flash.HorizonKernelState
|
import zako.zako.zako.zakoui.flash.HorizonKernelState
|
||||||
import com.sukisu.ultra.flash.HorizonKernelWorker
|
import zako.zako.zako.zakoui.flash.HorizonKernelWorker
|
||||||
import com.sukisu.ultra.ui.component.KeyEventBlocker
|
import com.sukisu.ultra.ui.component.KeyEventBlocker
|
||||||
import com.sukisu.ultra.ui.util.*
|
import com.sukisu.ultra.ui.util.*
|
||||||
import kotlinx.coroutines.Dispatchers
|
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 androidx.compose.ui.input.key.key
|
import androidx.compose.ui.input.key.key
|
||||||
import com.sukisu.ultra.ui.theme.CardConfig
|
import com.sukisu.ultra.ui.theme.CardConfig
|
||||||
|
import zako.zako.zako.zakoui.flash.FlashState
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ShirkNeko
|
* @author ShirkNeko
|
||||||
@@ -117,7 +119,7 @@ fun KernelFlashScreen(
|
|||||||
logContent.clear()
|
logContent.clear()
|
||||||
logContent.append(logText)
|
logContent.append(logText)
|
||||||
}
|
}
|
||||||
kotlinx.coroutines.delay(100)
|
delay(100)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flashState.error.isNotEmpty()) {
|
if (flashState.error.isNotEmpty()) {
|
||||||
@@ -239,7 +241,7 @@ fun KernelFlashScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun FlashProgressIndicator(flashState: com.sukisu.ultra.flash.FlashState) {
|
private fun FlashProgressIndicator(flashState: FlashState) {
|
||||||
val progressColor = when {
|
val progressColor = when {
|
||||||
flashState.error.isNotEmpty() -> MaterialTheme.colorScheme.error
|
flashState.error.isNotEmpty() -> MaterialTheme.colorScheme.error
|
||||||
flashState.isCompleted -> MaterialTheme.colorScheme.tertiary
|
flashState.isCompleted -> MaterialTheme.colorScheme.tertiary
|
||||||
@@ -355,7 +357,7 @@ private fun FlashProgressIndicator(flashState: com.sukisu.ultra.flash.FlashState
|
|||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
private fun TopBar(
|
private fun TopBar(
|
||||||
flashState: com.sukisu.ultra.flash.FlashState,
|
flashState: FlashState,
|
||||||
onBack: () -> Unit,
|
onBack: () -> Unit,
|
||||||
onSave: () -> Unit = {},
|
onSave: () -> Unit = {},
|
||||||
scrollBehavior: TopAppBarScrollBehavior? = null
|
scrollBehavior: TopAppBarScrollBehavior? = null
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.sukisu.ultra.ui.screen
|
package zako.zako.zako.zakoui.screen
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
Reference in New Issue
Block a user