manager: Fixed an issue where ksud failed to release properly during the first installation.

This commit is contained in:
ShirkNeko
2025-10-15 14:15:13 +08:00
parent b8eebcda5a
commit 8ff9fab414
5 changed files with 273 additions and 405 deletions

View File

@@ -1,6 +1,7 @@
package com.sukisu.ultra.ui
import android.content.Context
import android.content.Intent
import android.content.res.Configuration
import android.net.Uri
import android.os.Build
@@ -8,27 +9,40 @@ import android.os.Bundle
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.material.icons.filled.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.lifecycle.lifecycleScope
import androidx.navigation.NavBackStackEntry
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.utils.rememberDestinationsNavigator
import com.sukisu.ultra.ui.activity.component.BottomBar
import com.sukisu.ultra.ui.activity.util.*
import com.sukisu.ultra.ui.component.InstallConfirmationDialog
import com.sukisu.ultra.Natives
import com.sukisu.ultra.ui.screen.BottomBarDestination
import com.sukisu.ultra.ui.theme.KernelSUTheme
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 com.sukisu.ultra.ui.webui.initPlatform
import com.sukisu.ultra.ui.component.*
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
import com.sukisu.ultra.ui.activity.component.BottomBar
import com.sukisu.ultra.ui.activity.util.*
class MainActivity : ComponentActivity() {
private lateinit var superUserViewModel: SuperUserViewModel
private lateinit var homeViewModel: HomeViewModel
internal val settingsStateFlow = MutableStateFlow(SettingsState())
data class SettingsState(
@@ -36,6 +50,11 @@ class MainActivity : ComponentActivity() {
val showKpmInfo: Boolean = false
)
private var showConfirmationDialog = mutableStateOf(false)
private var pendingZipFiles = mutableStateOf<List<ZipFileInfo>>(emptyList())
private lateinit var themeChangeObserver: ThemeChangeContentObserver
// 标记避免重复初始化
private var isInitialized = false
@@ -46,8 +65,11 @@ class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
try {
// 应用主题配
ThemeUtils.applyFullThemeConfiguration(this)
// 确保应用正确的语言设
LocaleUtils.applyLanguageSetting(this)
// 应用自定义 DPI
DisplayUtils.applyCustomDpi(this)
// Enable edge to edge
enableEdgeToEdge()
@@ -60,63 +82,94 @@ class MainActivity : ComponentActivity() {
// 使用标记控制初始化流程
if (!isInitialized) {
lifecycleScope.launch {
ActivityInitializer.initialize(this@MainActivity, settingsStateFlow)
}
ThemeUtils.registerThemeChangeObserver(this)
initializeViewModels()
initializeData()
isInitialized = true
}
// Check if launched with a ZIP file
val zipUri: ArrayList<Uri>? = when (intent?.action) {
Intent.ACTION_SEND -> {
val uri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
intent.getParcelableExtra(Intent.EXTRA_STREAM, Uri::class.java)
} else {
@Suppress("DEPRECATION")
intent.getParcelableExtra(Intent.EXTRA_STREAM)
}
uri?.let { arrayListOf(it) }
}
Intent.ACTION_SEND_MULTIPLE -> {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM, Uri::class.java)
} else {
@Suppress("DEPRECATION")
intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM)
}
}
else -> when {
intent?.data != null -> arrayListOf(intent.data!!)
Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU -> {
intent.getParcelableArrayListExtra("uris", Uri::class.java)
}
else -> {
@Suppress("DEPRECATION")
intent.getParcelableArrayListExtra("uris")
}
}
}
setContent {
KernelSUTheme {
MainScreenContent()
}
}
} catch (e: Exception) {
e.printStackTrace()
}
}
@Composable
private fun MainScreenContent() {
val navController = rememberNavController()
val snackBarHostState = remember { SnackbarHostState() }
val currentDestination = navController.currentBackStackEntryAsState().value?.destination
val navigator = navController.rememberDestinationsNavigator()
// 处理ZIP文件
var zipUri by remember { mutableStateOf<ArrayList<Uri>?>(null) }
// 在 LaunchedEffect 中处理 ZIP 文件
LaunchedEffect(Unit) {
zipUri = ZipFileManager.handleZipFiles(intent)
val bottomBarRoutes = remember {
BottomBarDestination.entries.map { it.direction.route }.toSet()
}
val navigator = navController.rememberDestinationsNavigator()
InstallConfirmationDialog(
show = ZipFileManager.showConfirmationDialog.value,
zipFiles = ZipFileManager.pendingZipFiles.value,
show = showConfirmationDialog.value,
zipFiles = pendingZipFiles.value,
onConfirm = { confirmedFiles ->
ZipFileManager.navigateToFlashScreen(
this@MainActivity,
confirmedFiles,
navigator,
lifecycleScope
)
ZipFileManager.clearZipFileState()
showConfirmationDialog.value = false
UltraActivityUtils.navigateToFlashScreen(this, confirmedFiles, navigator)
},
onDismiss = {
ZipFileManager.clearZipFileState()
showConfirmationDialog.value = false
pendingZipFiles.value = emptyList()
finish()
}
)
LaunchedEffect(zipUri) {
zipUri?.let { uris ->
ZipFileManager.detectZipTypeAndShowConfirmation(this@MainActivity, uris)
if (!zipUri.isNullOrEmpty()) {
// 检测 ZIP 文件类型并显示确认对话框
lifecycleScope.launch {
UltraActivityUtils.detectZipTypeAndShowConfirmation(this@MainActivity, zipUri) { infos ->
if (infos.isNotEmpty()) {
pendingZipFiles.value = infos
showConfirmationDialog.value = true
} else {
finish()
}
}
}
}
}
val showBottomBar = NavigationUtils.shouldShowBottomBar(currentDestination?.route)
val showBottomBar = when (currentDestination?.route) {
ExecuteModuleActionScreenDestination.route -> false
else -> true
}
LaunchedEffect(Unit) {
initPlatform()
}
CompositionLocalProvider(
LocalSnackbarHost provides snackBarHostState
@@ -134,11 +187,86 @@ class MainActivity : ComponentActivity() {
modifier = Modifier.padding(innerPadding),
navGraph = NavGraphs.root as NavHostGraphSpec,
navController = navController,
defaultTransitions = NavigationUtils.createNavHostAnimations()
defaultTransitions = object : NavHostAnimatedDestinationStyle() {
override val enterTransition: AnimatedContentTransitionScope<NavBackStackEntry>.() -> EnterTransition = {
// If the target is a detail page (not a bottom navigation page), slide in from the right
if (targetState.destination.route !in bottomBarRoutes) {
slideInHorizontally(initialOffsetX = { it })
} else {
// Otherwise (switching between bottom navigation pages), use fade in
fadeIn(animationSpec = tween(340))
}
}
override val exitTransition: AnimatedContentTransitionScope<NavBackStackEntry>.() -> ExitTransition = {
// If navigating from the home page (bottom navigation page) to a detail page, slide out to the left
if (initialState.destination.route in bottomBarRoutes && targetState.destination.route !in bottomBarRoutes) {
slideOutHorizontally(targetOffsetX = { -it / 4 }) + fadeOut()
} else {
// Otherwise (switching between bottom navigation pages), use fade out
fadeOut(animationSpec = tween(340))
}
}
override val popEnterTransition: AnimatedContentTransitionScope<NavBackStackEntry>.() -> EnterTransition = {
// If returning to the home page (bottom navigation page), slide in from the left
if (targetState.destination.route in bottomBarRoutes) {
slideInHorizontally(initialOffsetX = { -it / 4 }) + fadeIn()
} else {
// Otherwise (e.g., returning between multiple detail pages), use default fade in
fadeIn(animationSpec = tween(340))
}
}
override val popExitTransition: AnimatedContentTransitionScope<NavBackStackEntry>.() -> ExitTransition = {
// If returning from a detail page (not a bottom navigation page), scale down and fade out
if (initialState.destination.route !in bottomBarRoutes) {
scaleOut(targetScale = 0.9f) + fadeOut()
} else {
// Otherwise, use default fade out
fadeOut(animationSpec = tween(340))
}
}
}
)
}
}
}
}
} catch (e: Exception) {
e.printStackTrace()
}
}
private fun initializeViewModels() {
superUserViewModel = SuperUserViewModel()
homeViewModel = HomeViewModel()
// 设置主题变化监听器
themeChangeObserver = ThemeUtils.registerThemeChangeObserver(this)
}
private fun initializeData() {
lifecycleScope.launch {
try {
superUserViewModel.fetchAppList()
} catch (e: Exception) {
e.printStackTrace()
}
}
// 数据刷新协程
DataRefreshUtils.startDataRefreshCoroutine(lifecycleScope)
DataRefreshUtils.startSettingsMonitorCoroutine(lifecycleScope, this, settingsStateFlow)
// 初始化主题相关设置
ThemeUtils.initializeThemeSettings(this, settingsStateFlow)
val isManager = Natives.becomeManager(packageName)
if (isManager) {
install()
}
}
override fun onResume() {
try {
@@ -157,8 +285,12 @@ class MainActivity : ComponentActivity() {
private fun refreshData() {
lifecycleScope.launch {
ViewModelManager.refreshViewModelData()
try {
superUserViewModel.fetchAppList()
DataRefreshUtils.refreshData(lifecycleScope)
} catch (e: Exception) {
e.printStackTrace()
}
}
}
@@ -173,7 +305,7 @@ class MainActivity : ComponentActivity() {
override fun onDestroy() {
try {
ThemeUtils.unregisterThemeChangeObserver(this)
ThemeUtils.unregisterThemeChangeObserver(this, themeChangeObserver)
super.onDestroy()
} catch (e: Exception) {
e.printStackTrace()

View File

@@ -22,6 +22,7 @@ import com.sukisu.ultra.ui.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 com.sukisu.ultra.ui.util.*
@SuppressLint("ContextCastToActivity")
@OptIn(ExperimentalMaterial3Api::class)

View File

@@ -1,77 +0,0 @@
package com.sukisu.ultra.ui.activity.util
import androidx.compose.animation.*
import androidx.compose.animation.core.tween
import androidx.navigation.NavBackStackEntry
import com.ramcosta.composedestinations.animations.NavHostAnimatedDestinationStyle
import com.ramcosta.composedestinations.generated.destinations.ExecuteModuleActionScreenDestination
import com.sukisu.ultra.ui.screen.BottomBarDestination
object NavigationUtils {
/**
* 获取底部导航栏路由集合
*/
fun getBottomBarRoutes(): Set<String> {
return BottomBarDestination.entries.map { it.direction.route }.toSet()
}
/**
* 判断是否应该显示底部导航栏
*/
fun shouldShowBottomBar(currentRoute: String?): Boolean {
return when (currentRoute) {
ExecuteModuleActionScreenDestination.route -> false
else -> true
}
}
/**
* 创建导航动画样式
*/
fun createNavHostAnimations(): NavHostAnimatedDestinationStyle {
val bottomBarRoutes = getBottomBarRoutes()
return object : NavHostAnimatedDestinationStyle() {
override val enterTransition: AnimatedContentTransitionScope<NavBackStackEntry>.() -> EnterTransition = {
// If the target is a detail page (not a bottom navigation page), slide in from the right
if (targetState.destination.route !in bottomBarRoutes) {
slideInHorizontally(initialOffsetX = { it })
} else {
// Otherwise (switching between bottom navigation pages), use fade in
fadeIn(animationSpec = tween(340))
}
}
override val exitTransition: AnimatedContentTransitionScope<NavBackStackEntry>.() -> ExitTransition = {
// If navigating from the home page (bottom navigation page) to a detail page, slide out to the left
if (initialState.destination.route in bottomBarRoutes && targetState.destination.route !in bottomBarRoutes) {
slideOutHorizontally(targetOffsetX = { -it / 4 }) + fadeOut()
} else {
// Otherwise (switching between bottom navigation pages), use fade out
fadeOut(animationSpec = tween(340))
}
}
override val popEnterTransition: AnimatedContentTransitionScope<NavBackStackEntry>.() -> EnterTransition = {
// If returning to the home page (bottom navigation page), slide in from the left
if (targetState.destination.route in bottomBarRoutes) {
slideInHorizontally(initialOffsetX = { -it / 4 }) + fadeIn()
} else {
// Otherwise (e.g., returning between multiple detail pages), use default fade in
fadeIn(animationSpec = tween(340))
}
}
override val popExitTransition: AnimatedContentTransitionScope<NavBackStackEntry>.() -> ExitTransition = {
// If returning from a detail page (not a bottom navigation page), scale down and fade out
if (initialState.destination.route !in bottomBarRoutes) {
scaleOut(targetScale = 0.9f) + fadeOut()
} else {
// Otherwise, use default fade out
fadeOut(animationSpec = tween(340))
}
}
}
}
}

View File

@@ -3,6 +3,7 @@ package com.sukisu.ultra.ui.activity.util
import android.content.Context
import android.database.ContentObserver
import android.os.Handler
import android.provider.Settings
import androidx.core.content.edit
import com.sukisu.ultra.ui.MainActivity
import com.sukisu.ultra.ui.theme.CardConfig
@@ -19,16 +20,8 @@ class ThemeChangeContentObserver(
}
}
/**
* 主题管理工具类
*/
object ThemeUtils {
private var themeChangeObserver: ThemeChangeContentObserver? = null
/**
* 初始化主题设置
*/
fun initializeThemeSettings(activity: MainActivity, settingsStateFlow: MutableStateFlow<MainActivity.SettingsState>) {
val prefs = activity.getSharedPreferences("settings", Context.MODE_PRIVATE)
val isFirstRun = prefs.getBoolean("is_first_run", true)
@@ -53,9 +46,6 @@ object ThemeUtils {
CardConfig.load(activity.applicationContext)
}
/**
* 注册主题变化观察者
*/
fun registerThemeChangeObserver(activity: MainActivity): ThemeChangeContentObserver {
val contentObserver = ThemeChangeContentObserver(Handler(activity.mainLooper)) {
activity.runOnUiThread {
@@ -67,28 +57,18 @@ object ThemeUtils {
}
activity.contentResolver.registerContentObserver(
android.provider.Settings.System.getUriFor("ui_night_mode"),
Settings.System.getUriFor("ui_night_mode"),
false,
contentObserver
)
themeChangeObserver = contentObserver
return contentObserver
}
/**
* 注销主题变化观察者
*/
fun unregisterThemeChangeObserver(activity: MainActivity) {
themeChangeObserver?.let { observer ->
fun unregisterThemeChangeObserver(activity: MainActivity, observer: ThemeChangeContentObserver) {
activity.contentResolver.unregisterContentObserver(observer)
}
themeChangeObserver = null
}
/**
* Activity暂停时的主题处理
*/
fun onActivityPause(activity: MainActivity) {
CardConfig.save(activity.applicationContext)
activity.getSharedPreferences("theme_prefs", Context.MODE_PRIVATE).edit {
@@ -97,39 +77,21 @@ object ThemeUtils {
ThemeConfig.preventBackgroundRefresh = true
}
/**
* Activity恢复时的主题处理
*/
fun onActivityResume() {
if (!ThemeConfig.backgroundImageLoaded && !ThemeConfig.preventBackgroundRefresh) {
loadCustomBackground()
}
}
/**
* 应用完整的主题配置到Activity
*/
fun applyFullThemeConfiguration(activity: MainActivity) {
// 确保应用正确的语言设置
LocaleUtils.applyLanguageSetting(activity)
// 应用自定义 DPI
DisplayUtils.applyCustomDpi(activity)
}
private fun loadThemeMode() {
// 主题模式加载逻辑
}
private fun loadThemeColors() {
// 主题颜色加载逻辑
}
private fun loadDynamicColorState() {
// 动态颜色状态加载逻辑
}
private fun loadCustomBackground() {
// 自定义背景加载逻辑
}
}

View File

@@ -2,29 +2,18 @@ package com.sukisu.ultra.ui.activity.util
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.content.res.Configuration
import android.net.Uri
import android.os.Build
import androidx.compose.animation.*
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
import androidx.compose.runtime.mutableStateOf
import androidx.core.content.edit
import androidx.lifecycle.LifecycleCoroutineScope
import androidx.lifecycle.lifecycleScope
import com.ramcosta.composedestinations.generated.destinations.FlashScreenDestination
import com.ramcosta.composedestinations.generated.destinations.InstallScreenDestination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import com.sukisu.ultra.Natives
import com.sukisu.ultra.ui.MainActivity
import com.sukisu.ultra.ui.component.ZipFileDetector
import com.sukisu.ultra.ui.component.ZipFileInfo
import com.sukisu.ultra.ui.component.ZipType
import com.sukisu.ultra.ui.screen.FlashIt
import com.sukisu.ultra.ui.util.*
import com.sukisu.ultra.ui.viewmodel.HomeViewModel
import com.sukisu.ultra.ui.viewmodel.SuperUserViewModel
import com.sukisu.ultra.ui.webui.initPlatform
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
@@ -32,8 +21,18 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.util.*
import android.net.Uri
import androidx.lifecycle.lifecycleScope
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import com.sukisu.ultra.ui.component.ZipFileDetector
import com.sukisu.ultra.ui.component.ZipFileInfo
import com.sukisu.ultra.ui.component.ZipType
import com.ramcosta.composedestinations.generated.destinations.FlashScreenDestination
import com.ramcosta.composedestinations.generated.destinations.InstallScreenDestination
import com.sukisu.ultra.ui.screen.FlashIt
import kotlinx.coroutines.withContext
import androidx.core.content.edit
object AnimatedBottomBar {
@Composable
@@ -51,9 +50,58 @@ object AnimatedBottomBar {
}
}
/**
* 应用数据管理工具类
*/
object UltraActivityUtils {
suspend fun detectZipTypeAndShowConfirmation(
activity: MainActivity,
zipUris: ArrayList<Uri>,
onResult: (List<ZipFileInfo>) -> Unit
) {
val infos = ZipFileDetector.detectAndParseZipFiles(activity, zipUris)
withContext(Dispatchers.Main) { onResult(infos) }
}
fun navigateToFlashScreen(
activity: MainActivity,
zipFiles: List<ZipFileInfo>,
navigator: DestinationsNavigator
) {
activity.lifecycleScope.launch {
val moduleUris = zipFiles.filter { it.type == ZipType.MODULE }.map { it.uri }
val kernelUris = zipFiles.filter { it.type == ZipType.KERNEL }.map { it.uri }
when {
kernelUris.isNotEmpty() && moduleUris.isEmpty() -> {
if (kernelUris.size == 1 && rootAvailable()) {
navigator.navigate(
InstallScreenDestination(
preselectedKernelUri = kernelUris.first().toString()
)
)
}
setAutoExitAfterFlash(activity)
}
moduleUris.isNotEmpty() -> {
navigator.navigate(
FlashScreenDestination(
FlashIt.FlashModules(ArrayList(moduleUris))
)
)
setAutoExitAfterFlash(activity)
}
}
}
}
private fun setAutoExitAfterFlash(activity: Context) {
activity.getSharedPreferences("kernel_flash_prefs", Context.MODE_PRIVATE)
.edit {
putBoolean("auto_exit_after_flash", true)
}
}
}
object AppData {
object DataRefreshManager {
// 私有状态流
@@ -136,160 +184,7 @@ object AppData {
}
}
/**
* ZIP文件处理工具类
*/
object ZipFileManager {
val showConfirmationDialog = mutableStateOf(false)
val pendingZipFiles = mutableStateOf<List<ZipFileInfo>>(emptyList())
/**
* 处理传入的ZIP文件URI
*/
fun handleZipFiles(intent: Intent?): ArrayList<Uri>? {
return when (intent?.action) {
Intent.ACTION_SEND -> {
val uri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
intent.getParcelableExtra(Intent.EXTRA_STREAM, Uri::class.java)
} else {
@Suppress("DEPRECATION")
intent.getParcelableExtra(Intent.EXTRA_STREAM)
}
uri?.let { arrayListOf(it) }
}
Intent.ACTION_SEND_MULTIPLE -> {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM, Uri::class.java)
} else {
@Suppress("DEPRECATION")
intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM)
}
}
else -> when {
intent?.data != null -> arrayListOf(intent.data!!)
Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU -> {
intent?.getParcelableArrayListExtra("uris", Uri::class.java)
}
else -> {
@Suppress("DEPRECATION")
(intent?.getParcelableArrayListExtra("uris"))
}
}
}
}
/**
* 检测ZIP文件类型并显示确认对话框
*/
suspend fun detectZipTypeAndShowConfirmation(context: Context, zipUris: ArrayList<Uri>) {
try {
val zipFileInfos = ZipFileDetector.detectAndParseZipFiles(context, zipUris)
withContext(Dispatchers.Main) {
if (zipFileInfos.isNotEmpty()) {
pendingZipFiles.value = zipFileInfos
showConfirmationDialog.value = true
} else {
(context as MainActivity).finish()
}
}
} catch (e: Exception) {
withContext(Dispatchers.Main) {
(context as MainActivity).finish()
}
e.printStackTrace()
}
}
/**
* 导航到内核刷写界面
*/
fun navigateToFlashScreen(
context: Context,
zipFiles: List<ZipFileInfo>,
navigator: DestinationsNavigator,
scope: LifecycleCoroutineScope
) {
scope.launch {
val moduleUris = zipFiles.filter { it.type == ZipType.MODULE }.map { it.uri }
val kernelUris = zipFiles.filter { it.type == ZipType.KERNEL }.map { it.uri }
when {
// 内核文件
kernelUris.isNotEmpty() && moduleUris.isEmpty() -> {
if (kernelUris.size == 1 && rootAvailable()) {
navigator.navigate(
InstallScreenDestination(
preselectedKernelUri = kernelUris.first().toString()
)
)
}
setAutoExitAfterFlash(context)
}
// 模块文件
moduleUris.isNotEmpty() -> {
navigator.navigate(
FlashScreenDestination(
FlashIt.FlashModules(ArrayList(moduleUris))
)
)
setAutoExitAfterFlash(context)
}
}
}
}
/**
* 设置内核刷写后自动退出
*/
private fun setAutoExitAfterFlash(context: Context) {
val sharedPref = context.getSharedPreferences("kernel_flash_prefs", Context.MODE_PRIVATE)
sharedPref.edit {
putBoolean("auto_exit_after_flash", true)
}
}
/**
* 清理ZIP文件状态
*/
fun clearZipFileState() {
showConfirmationDialog.value = false
pendingZipFiles.value = emptyList()
}
}
/**
* ViewModel管理工具类
*/
object ViewModelManager {
lateinit var superUserViewModel: SuperUserViewModel
lateinit var homeViewModel: HomeViewModel
/**
* 初始化ViewModel
*/
fun initializeViewModels() {
superUserViewModel = SuperUserViewModel()
homeViewModel = HomeViewModel()
}
/**
* 刷新ViewModel数据
*/
suspend fun refreshViewModelData() {
try {
superUserViewModel.fetchAppList()
} catch (e: Exception) {
e.printStackTrace()
}
}
}
/**
* 数据刷新工具类
*/
object DataRefreshUtils {
fun startDataRefreshCoroutine(scope: LifecycleCoroutineScope) {
scope.launch(Dispatchers.IO) {
while (isActive) {
@@ -323,48 +218,7 @@ object DataRefreshUtils {
}
}
/**
* Activity初始化工具类
*/
object ActivityInitializer {
/**
* 初始化Activity的所有组件
*/
suspend fun initialize(activity: MainActivity, settingsStateFlow: MutableStateFlow<MainActivity.SettingsState>) {
// 初始化ViewModel
ViewModelManager.initializeViewModels()
// 初始化数据
initializeData(activity, settingsStateFlow)
// 初始化平台
initPlatform()
}
private suspend fun initializeData(activity: MainActivity, settingsStateFlow: MutableStateFlow<MainActivity.SettingsState>) {
// 获取应用列表
ViewModelManager.refreshViewModelData()
// 启动数据刷新协程
DataRefreshUtils.startDataRefreshCoroutine(activity.lifecycleScope)
DataRefreshUtils.startSettingsMonitorCoroutine(activity.lifecycleScope, activity, settingsStateFlow)
// 初始化主题相关设置
ThemeUtils.initializeThemeSettings(activity, settingsStateFlow)
// 安装管理器
val isManager = Natives.becomeManager(activity.packageName)
if (isManager) {
install()
}
}
}
/**
* 显示设置工具类
*/
object DisplayUtils {
fun applyCustomDpi(context: Context) {
val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
val customDpi = prefs.getInt("app_dpi", 0)
@@ -384,11 +238,7 @@ object DisplayUtils {
}
}
/**
* 语言本地化工具类
*/
object LocaleUtils {
@SuppressLint("ObsoleteSdkInt")
fun applyLanguageSetting(context: Context) {
val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE)