manager: Fixed flickering on activity refresh using a clever method.

- Add Activity lifecycle callback and method to refresh current Activity
This commit is contained in:
ShirkNeko
2025-06-03 01:43:31 +08:00
parent 1d34ea4995
commit 07c9cce4b9
3 changed files with 166 additions and 88 deletions

View File

@@ -1,11 +1,14 @@
package com.sukisu.ultra package com.sukisu.ultra
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.Activity
import android.app.ActivityOptions
import android.app.Application import android.app.Application
import android.content.Context import android.content.Context
import android.content.res.Configuration import android.content.res.Configuration
import android.content.res.Resources import android.content.res.Resources
import android.os.Build import android.os.Build
import android.os.Bundle
import coil.Coil import coil.Coil
import coil.ImageLoader import coil.ImageLoader
import com.dergoogler.mmrl.platform.Platform import com.dergoogler.mmrl.platform.Platform
@@ -14,9 +17,31 @@ import me.zhanghai.android.appiconloader.coil.AppIconKeyer
import java.io.File import java.io.File
import java.util.Locale import java.util.Locale
@SuppressLint("StaticFieldLeak")
lateinit var ksuApp: KernelSUApplication lateinit var ksuApp: KernelSUApplication
class KernelSUApplication : Application() { class KernelSUApplication : Application() {
private var currentActivity: Activity? = null
private val activityLifecycleCallbacks = object : ActivityLifecycleCallbacks {
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
currentActivity = activity
}
override fun onActivityStarted(activity: Activity) {
currentActivity = activity
}
override fun onActivityResumed(activity: Activity) {
currentActivity = activity
}
override fun onActivityPaused(activity: Activity) {}
override fun onActivityStopped(activity: Activity) {}
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}
override fun onActivityDestroyed(activity: Activity) {
if (currentActivity == activity) {
currentActivity = null
}
}
}
override fun attachBaseContext(base: Context) { override fun attachBaseContext(base: Context) {
val prefs = base.getSharedPreferences("settings", MODE_PRIVATE) val prefs = base.getSharedPreferences("settings", MODE_PRIVATE)
@@ -62,6 +87,9 @@ class KernelSUApplication : Application() {
super.onCreate() super.onCreate()
ksuApp = this ksuApp = this
// 注册Activity生命周期回调
registerActivityLifecycleCallbacks(activityLifecycleCallbacks)
Platform.setHiddenApiExemptions() Platform.setHiddenApiExemptions()
val context = this val context = this
@@ -107,4 +135,17 @@ class KernelSUApplication : Application() {
} }
} }
} }
}
// 添加刷新当前Activity的方法
fun refreshCurrentActivity() {
currentActivity?.let { activity ->
val intent = activity.intent
activity.finish()
val options = ActivityOptions.makeCustomAnimation(
activity, android.R.anim.fade_in, android.R.anim.fade_out
)
activity.startActivity(intent, options.toBundle())
}
}
}

View File

@@ -44,127 +44,170 @@ class MainActivity : ComponentActivity() {
private lateinit var themeChangeObserver: ThemeChangeContentObserver private lateinit var themeChangeObserver: ThemeChangeContentObserver
// 添加标记避免重复初始化
private var isInitialized = false
override fun attachBaseContext(newBase: Context) { override fun attachBaseContext(newBase: Context) {
val context = LocaleUtils.applyLocale(newBase) val context = LocaleUtils.applyLocale(newBase)
super.attachBaseContext(context) super.attachBaseContext(context)
} }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
// 确保应用正确的语言设置 try {
LocaleUtils.applyLanguageSetting(this) // 确保应用正确的语言设置
LocaleUtils.applyLanguageSetting(this)
// 应用自定义 DPI // 应用自定义 DPI
DisplayUtils.applyCustomDpi(this) DisplayUtils.applyCustomDpi(this)
// Enable edge to edge // Enable edge to edge
enableEdgeToEdge() enableEdgeToEdge()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
window.isNavigationBarContrastEnforced = false window.isNavigationBarContrastEnforced = false
}
super.onCreate(savedInstanceState)
// 使用标记控制初始化流程
if (!isInitialized) {
initializeViewModels()
initializeData()
isInitialized = true
}
setContent {
KernelSUTheme {
val navController = rememberNavController()
val snackBarHostState = remember { SnackbarHostState() }
val currentDestination = navController.currentBackStackEntryAsState().value?.destination
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 = NavigationUtils.defaultTransitions()
)
}
}
}
}
} catch (e: Exception) {
e.printStackTrace()
} }
}
super.onCreate(savedInstanceState) private fun initializeViewModels() {
// 初始化 SuperUserViewModel
superUserViewModel = SuperUserViewModel() superUserViewModel = SuperUserViewModel()
lifecycleScope.launch {
superUserViewModel.fetchAppList()
}
// 初始化 HomeViewModel
homeViewModel = HomeViewModel() homeViewModel = HomeViewModel()
// 预加载数据 // 设置主题变化监听器
themeChangeObserver = ThemeUtils.registerThemeChangeObserver(this)
}
private fun initializeData() {
lifecycleScope.launch { lifecycleScope.launch {
homeViewModel.initializeData() try {
superUserViewModel.fetchAppList()
} catch (e: Exception) {
e.printStackTrace()
}
}
lifecycleScope.launch {
try {
homeViewModel.initializeData()
} catch (e: Exception) {
e.printStackTrace()
}
} }
// 数据刷新协程 // 数据刷新协程
DataRefreshUtils.startDataRefreshCoroutine(lifecycleScope) DataRefreshUtils.startDataRefreshCoroutine(lifecycleScope)
DataRefreshUtils.startSettingsMonitorCoroutine(lifecycleScope, this, settingsStateFlow) DataRefreshUtils.startSettingsMonitorCoroutine(lifecycleScope, this, settingsStateFlow)
// 初始化主题相关设置 // 初始化主题相关设置
ThemeUtils.initializeThemeSettings(this, settingsStateFlow) ThemeUtils.initializeThemeSettings(this, settingsStateFlow)
// 设置主题变化监听器
themeChangeObserver = ThemeUtils.registerThemeChangeObserver(this)
val isManager = AppData.isManager(ksuApp.packageName) val isManager = AppData.isManager(ksuApp.packageName)
if (isManager) { if (isManager) {
install() install()
UltraToolInstall.tryToInstall() UltraToolInstall.tryToInstall()
} }
}
setContent { override fun onResume() {
KernelSUTheme { try {
val navController = rememberNavController() super.onResume()
val snackBarHostState = remember { SnackbarHostState() } LocaleUtils.applyLanguageSetting(this)
val currentDestination = navController.currentBackStackEntryAsState().value?.destination ThemeUtils.onActivityResume()
val showBottomBar = when (currentDestination?.route) { // 仅在需要时刷新数据
ExecuteModuleActionScreenDestination.route -> false // Hide for ExecuteModuleActionScreen if (isInitialized) {
else -> true refreshData()
} }
} catch (e: Exception) {
e.printStackTrace()
}
}
// pre-init platform to faster start WebUI X activities private fun refreshData() {
LaunchedEffect(Unit) { lifecycleScope.launch {
initPlatform() try {
} superUserViewModel.fetchAppList()
homeViewModel.initializeData()
CompositionLocalProvider( DataRefreshUtils.refreshData(lifecycleScope)
LocalSnackbarHost provides snackBarHostState } catch (e: Exception) {
) { e.printStackTrace()
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.defaultTransitions()
)
}
}
} }
} }
} }
override fun onPause() { override fun onPause() {
super.onPause() try {
ThemeUtils.onActivityPause(this) super.onPause()
} ThemeUtils.onActivityPause(this)
} catch (e: Exception) {
override fun onResume() { e.printStackTrace()
super.onResume()
LocaleUtils.applyLanguageSetting(this)
ThemeUtils.onActivityResume()
lifecycleScope.launch {
superUserViewModel.fetchAppList()
} }
lifecycleScope.launch {
homeViewModel.initializeData()
}
DataRefreshUtils.refreshData(lifecycleScope)
} }
override fun onDestroy() { override fun onDestroy() {
ThemeUtils.unregisterThemeChangeObserver(this, themeChangeObserver) try {
super.onDestroy() ThemeUtils.unregisterThemeChangeObserver(this, themeChangeObserver)
super.onDestroy()
} catch (e: Exception) {
e.printStackTrace()
}
} }
override fun onConfigurationChanged(newConfig: Configuration) { override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig) try {
LocaleUtils.applyLanguageSetting(this) super.onConfigurationChanged(newConfig)
LocaleUtils.applyLanguageSetting(this)
} catch (e: Exception) {
e.printStackTrace()
}
} }
} }

View File

@@ -90,6 +90,7 @@ import androidx.compose.material3.TextButton
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import com.ramcosta.composedestinations.navigation.DestinationsNavigator import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import com.sukisu.ultra.ksuApp
/** /**
* @author ShirkNeko * @author ShirkNeko
@@ -388,14 +389,7 @@ fun MoreSettingsScreen(
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
context.resources.updateConfiguration(config, context.resources.displayMetrics) context.resources.updateConfiguration(config, context.resources.displayMetrics)
} }
ksuApp.refreshCurrentActivity()
val intent = Intent(context, MainActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK)
context.startActivity(intent)
if (context is Activity) {
context.finish()
}
} }
} }