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 2e9b144e..646ab110 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,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>(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,16 +82,155 @@ 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? = 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() + val navController = rememberNavController() + val snackBarHostState = remember { SnackbarHostState() } + val currentDestination = navController.currentBackStackEntryAsState().value?.destination + + val bottomBarRoutes = remember { + BottomBarDestination.entries.map { it.direction.route }.toSet() + } + + val navigator = navController.rememberDestinationsNavigator() + + InstallConfirmationDialog( + show = showConfirmationDialog.value, + zipFiles = pendingZipFiles.value, + onConfirm = { confirmedFiles -> + showConfirmationDialog.value = false + UltraActivityUtils.navigateToFlashScreen(this, confirmedFiles, navigator) + }, + onDismiss = { + showConfirmationDialog.value = false + pendingZipFiles.value = emptyList() + finish() + } + ) + + LaunchedEffect(zipUri) { + 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 = when (currentDestination?.route) { + ExecuteModuleActionScreenDestination.route -> false + else -> true + } + + LaunchedEffect(Unit) { + initPlatform() + } + + CompositionLocalProvider( + LocalSnackbarHost provides snackBarHostState + ) { + Scaffold( + bottomBar = { + AnimatedBottomBar.AnimatedBottomBarWrapper( + showBottomBar = showBottomBar, + content = { BottomBar(navController) } + ) + }, + contentWindowInsets = WindowInsets(0, 0, 0, 0) + ) { innerPadding -> + DestinationsNavHost( + modifier = Modifier.padding(innerPadding), + navGraph = NavGraphs.root as NavHostGraphSpec, + navController = navController, + defaultTransitions = object : NavHostAnimatedDestinationStyle() { + override val enterTransition: AnimatedContentTransitionScope.() -> 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.() -> 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.() -> 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.() -> 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) { @@ -77,66 +238,33 @@ class MainActivity : ComponentActivity() { } } - @Composable - private fun MainScreenContent() { - val navController = rememberNavController() - val snackBarHostState = remember { SnackbarHostState() } - val currentDestination = navController.currentBackStackEntryAsState().value?.destination - val navigator = navController.rememberDestinationsNavigator() + private fun initializeViewModels() { + superUserViewModel = SuperUserViewModel() + homeViewModel = HomeViewModel() - // 处理ZIP文件 - var zipUri by remember { mutableStateOf?>(null) } + // 设置主题变化监听器 + themeChangeObserver = ThemeUtils.registerThemeChangeObserver(this) + } - // 在 LaunchedEffect 中处理 ZIP 文件 - LaunchedEffect(Unit) { - zipUri = ZipFileManager.handleZipFiles(intent) - } - - InstallConfirmationDialog( - show = ZipFileManager.showConfirmationDialog.value, - zipFiles = ZipFileManager.pendingZipFiles.value, - onConfirm = { confirmedFiles -> - ZipFileManager.navigateToFlashScreen( - this@MainActivity, - confirmedFiles, - navigator, - lifecycleScope - ) - ZipFileManager.clearZipFileState() - }, - onDismiss = { - ZipFileManager.clearZipFileState() - finish() - } - ) - - LaunchedEffect(zipUri) { - zipUri?.let { uris -> - ZipFileManager.detectZipTypeAndShowConfirmation(this@MainActivity, uris) + private fun initializeData() { + lifecycleScope.launch { + try { + superUserViewModel.fetchAppList() + } catch (e: Exception) { + e.printStackTrace() } } - val showBottomBar = NavigationUtils.shouldShowBottomBar(currentDestination?.route) + // 数据刷新协程 + DataRefreshUtils.startDataRefreshCoroutine(lifecycleScope) + DataRefreshUtils.startSettingsMonitorCoroutine(lifecycleScope, this, settingsStateFlow) - CompositionLocalProvider( - LocalSnackbarHost provides snackBarHostState - ) { - Scaffold( - bottomBar = { - AnimatedBottomBar.AnimatedBottomBarWrapper( - showBottomBar = showBottomBar, - content = { BottomBar(navController) } - ) - }, - contentWindowInsets = WindowInsets(0, 0, 0, 0) - ) { innerPadding -> - DestinationsNavHost( - modifier = Modifier.padding(innerPadding), - navGraph = NavGraphs.root as NavHostGraphSpec, - navController = navController, - defaultTransitions = NavigationUtils.createNavHostAnimations() - ) - } + // 初始化主题相关设置 + ThemeUtils.initializeThemeSettings(this, settingsStateFlow) + + val isManager = Natives.becomeManager(packageName) + if (isManager) { + install() } } @@ -157,8 +285,12 @@ class MainActivity : ComponentActivity() { private fun refreshData() { lifecycleScope.launch { - ViewModelManager.refreshViewModelData() - DataRefreshUtils.refreshData(lifecycleScope) + 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() diff --git a/manager/app/src/main/java/com/sukisu/ultra/ui/activity/component/BottomBar.kt b/manager/app/src/main/java/com/sukisu/ultra/ui/activity/component/BottomBar.kt index 6cb363de..4014ca82 100644 --- a/manager/app/src/main/java/com/sukisu/ultra/ui/activity/component/BottomBar.kt +++ b/manager/app/src/main/java/com/sukisu/ultra/ui/activity/component/BottomBar.kt @@ -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) diff --git a/manager/app/src/main/java/com/sukisu/ultra/ui/activity/util/NavigationUtils.kt b/manager/app/src/main/java/com/sukisu/ultra/ui/activity/util/NavigationUtils.kt deleted file mode 100644 index de9777ad..00000000 --- a/manager/app/src/main/java/com/sukisu/ultra/ui/activity/util/NavigationUtils.kt +++ /dev/null @@ -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 { - 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.() -> 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.() -> 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.() -> 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.() -> 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)) - } - } - } - } -} \ No newline at end of file diff --git a/manager/app/src/main/java/com/sukisu/ultra/ui/activity/util/ThemeUtils.kt b/manager/app/src/main/java/com/sukisu/ultra/ui/activity/util/ThemeUtils.kt index b6851b07..6786917e 100644 --- a/manager/app/src/main/java/com/sukisu/ultra/ui/activity/util/ThemeUtils.kt +++ b/manager/app/src/main/java/com/sukisu/ultra/ui/activity/util/ThemeUtils.kt @@ -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) { 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 -> - activity.contentResolver.unregisterContentObserver(observer) - } - themeChangeObserver = null + fun unregisterThemeChangeObserver(activity: MainActivity, observer: ThemeChangeContentObserver) { + activity.contentResolver.unregisterContentObserver(observer) } - /** - * 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() { - // 自定义背景加载逻辑 } } \ No newline at end of file diff --git a/manager/app/src/main/java/com/sukisu/ultra/ui/activity/util/MergedUtils.kt b/manager/app/src/main/java/com/sukisu/ultra/ui/activity/util/UltraActivityUtils.kt similarity index 60% rename from manager/app/src/main/java/com/sukisu/ultra/ui/activity/util/MergedUtils.kt rename to manager/app/src/main/java/com/sukisu/ultra/ui/activity/util/UltraActivityUtils.kt index 82bd4c2a..69b4b0d1 100644 --- a/manager/app/src/main/java/com/sukisu/ultra/ui/activity/util/MergedUtils.kt +++ b/manager/app/src/main/java/com/sukisu/ultra/ui/activity/util/UltraActivityUtils.kt @@ -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, + onResult: (List) -> Unit + ) { + val infos = ZipFileDetector.detectAndParseZipFiles(activity, zipUris) + withContext(Dispatchers.Main) { onResult(infos) } + } + + fun navigateToFlashScreen( + activity: MainActivity, + zipFiles: List, + 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>(emptyList()) - - /** - * 处理传入的ZIP文件URI - */ - fun handleZipFiles(intent: Intent?): ArrayList? { - 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) { - 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, - 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) { - // 初始化ViewModel - ViewModelManager.initializeViewModels() - - // 初始化数据 - initializeData(activity, settingsStateFlow) - - // 初始化平台 - initPlatform() - } - - private suspend fun initializeData(activity: MainActivity, settingsStateFlow: MutableStateFlow) { - // 获取应用列表 - 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)