From baf593bd51ab585cbf4e554bc30a170dfb97c9cf Mon Sep 17 00:00:00 2001 From: Rahul Patel Date: Tue, 19 May 2026 12:10:59 +0530 Subject: [PATCH] compose: rewire navigation as the single entry point ComposeActivity becomes the sole launch target; the legacy MainActivity is removed. Destination / Screen pair drives navigation3, NavDisplay handles deep links plus microG-aware resume rechecks, and Theme.kt is tightened for edge-to-edge. --- app/src/main/AndroidManifest.xml | 30 ++- .../java/com/aurora/store/ComposeActivity.kt | 46 +++- .../java/com/aurora/store/MainActivity.kt | 157 ----------- .../store/compose/navigation/Destination.kt | 48 ++++ .../store/compose/navigation/NavDisplay.kt | 246 +++++++++++------- .../aurora/store/compose/navigation/Screen.kt | 30 +++ .../com/aurora/store/compose/theme/Theme.kt | 22 +- 7 files changed, 301 insertions(+), 278 deletions(-) delete mode 100644 app/src/main/java/com/aurora/store/MainActivity.kt create mode 100644 app/src/main/java/com/aurora/store/compose/navigation/Destination.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e7d15f5ec..ee020d093 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -82,9 +82,9 @@ tools:targetApi="tiramisu"> + android:windowSoftInputMode="adjustResize"> @@ -103,16 +103,28 @@ + + - + + + + + + + + + + + + + + + + + - - - UI.TV @@ -57,4 +49,32 @@ class ComposeActivity : ComponentActivity() { } } } + + private fun resolveStartDestination(): Screen { + // Parcel-based navigation (e.g. from NotificationUtil) + IntentCompat.getParcelableExtra(intent, Screen.PARCEL_KEY, Screen::class.java) + ?.let { return it } + + // Deep links via ACTION_VIEW + if (intent.action == Intent.ACTION_VIEW) { + val data = intent.data + val path = data?.path.orEmpty() + val id = data?.getQueryParameter("id") + return when { + id != null && path.contains("/apps/dev") -> Screen.DevProfile(id) + id != null -> Screen.AppDetails(id) + else -> defaultStart() + } + } + + // SEND / SHOW_APP_INFO — getPackageName() handles both + intent.getPackageName()?.let { return Screen.AppDetails(it) } + + return defaultStart() + } + + private fun defaultStart(): Screen = when { + !Preferences.getBoolean(this, Preferences.PREFERENCE_INTRO) -> Screen.Onboarding + else -> Screen.Splash + } } diff --git a/app/src/main/java/com/aurora/store/MainActivity.kt b/app/src/main/java/com/aurora/store/MainActivity.kt deleted file mode 100644 index 683f22517..000000000 --- a/app/src/main/java/com/aurora/store/MainActivity.kt +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Aurora Store - * Copyright (C) 2021, Rahul Kumar Patel - * Copyright (C) 2022, The Calyx Institute - * - * Aurora Store is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * Aurora Store is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Aurora Store. If not, see . - * - */ - -package com.aurora.store - -import android.os.Bundle -import android.view.View -import androidx.activity.addCallback -import androidx.activity.enableEdgeToEdge -import androidx.activity.viewModels -import androidx.appcompat.app.AppCompatActivity -import androidx.core.view.ViewCompat -import androidx.core.view.WindowInsetsCompat.Type.displayCutout -import androidx.core.view.WindowInsetsCompat.Type.ime -import androidx.core.view.WindowInsetsCompat.Type.systemBars -import androidx.lifecycle.lifecycleScope -import androidx.navigation.FloatingWindow -import androidx.navigation.fragment.NavHostFragment -import androidx.navigation.ui.setupWithNavController -import com.aurora.store.data.model.NetworkStatus -import com.aurora.store.data.receiver.MigrationReceiver -import com.aurora.store.databinding.ActivityMainBinding -import com.aurora.store.util.PackageUtil -import com.aurora.store.util.Preferences -import com.aurora.store.util.Preferences.PREFERENCE_DEFAULT_SELECTED_TAB -import com.aurora.store.view.ui.sheets.NetworkDialogSheet -import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.flow.collectLatest -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.launch - -@AndroidEntryPoint -class MainActivity : AppCompatActivity() { - - private val viewModel: MainViewModel by viewModels() - - private lateinit var binding: ActivityMainBinding - - // TopLevelFragments - private val topLevelFrags = listOf( - R.id.appsContainerFragment, - R.id.gamesContainerFragment, - R.id.updatesFragment - ) - - override fun onCreate(savedInstanceState: Bundle?) { - // Check and run migrations first if required - // This is needed thanks to OEMs breaking the MY_PACKAGE_REPLACED API - MigrationReceiver.runMigrationsIfRequired(this) - - enableEdgeToEdge() - super.onCreate(savedInstanceState) - - binding = ActivityMainBinding.inflate(layoutInflater) - setContentView(binding.root) - - // Adjust root view's paddings for edgeToEdge display - ViewCompat.setOnApplyWindowInsetsListener(binding.root) { root, windowInsets -> - val insets = windowInsets.getInsets(systemBars() or displayCutout() or ime()) - root.setPadding(insets.left, insets.top, insets.right, 0) - windowInsets - } - - val navHostFragment = - supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment - val navController = navHostFragment.navController - - if (!PackageUtil.isTv(this)) { - viewModel.networkProvider.status.onEach { networkStatus -> - when (networkStatus) { - NetworkStatus.AVAILABLE -> { - if (!supportFragmentManager.isDestroyed && isIntroDone()) { - val fragment = supportFragmentManager - .findFragmentByTag(NetworkDialogSheet.TAG) - fragment?.let { - supportFragmentManager.beginTransaction() - .remove(fragment) - .commitAllowingStateLoss() - } - } - } - - NetworkStatus.UNAVAILABLE -> { - if (!supportFragmentManager.isDestroyed && isIntroDone()) { - supportFragmentManager.beginTransaction() - .add(NetworkDialogSheet.newInstance(), NetworkDialogSheet.TAG) - .commitAllowingStateLoss() - } - } - } - }.launchIn(AuroraApp.scope) - } - - binding.navView.setupWithNavController(navController) - - // Handle quick exit from back actions - val defaultTab = when (Preferences.getInteger(this, PREFERENCE_DEFAULT_SELECTED_TAB)) { - 1 -> R.id.gamesContainerFragment - 2 -> R.id.updatesFragment - else -> R.id.appsContainerFragment - } - onBackPressedDispatcher.addCallback(this) { - if (navController.currentDestination?.id in topLevelFrags) { - if (navController.currentDestination?.id == defaultTab) { - finish() - } else { - navController.navigate(defaultTab) - } - } else if (navHostFragment.childFragmentManager.backStackEntryCount == 0) { - // We are on either on onboarding or splash fragment - finish() - } else { - navController.navigateUp() - } - } - - // Handle views on fragments - navController.addOnDestinationChangedListener { _, navDestination, _ -> - if (navDestination !is FloatingWindow) { - when (navDestination.id) { - in topLevelFrags -> binding.navView.visibility = View.VISIBLE - else -> binding.navView.visibility = View.GONE - } - } - } - - // Updates - lifecycleScope.launch { - viewModel.updateHelper.updates.collectLatest { list -> - binding.navView.getOrCreateBadge(R.id.updatesFragment).apply { - isVisible = !list.isNullOrEmpty() - number = list?.size ?: 0 - } - } - } - } - - private fun isIntroDone(): Boolean = Preferences.getBoolean(this, Preferences.PREFERENCE_INTRO) -} diff --git a/app/src/main/java/com/aurora/store/compose/navigation/Destination.kt b/app/src/main/java/com/aurora/store/compose/navigation/Destination.kt new file mode 100644 index 000000000..633597f05 --- /dev/null +++ b/app/src/main/java/com/aurora/store/compose/navigation/Destination.kt @@ -0,0 +1,48 @@ +/* + * SPDX-FileCopyrightText: 2026 Aurora OSS + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package com.aurora.store.compose.navigation + +import com.aurora.gplayapi.data.models.Category +import com.aurora.gplayapi.data.models.StreamCluster +import com.aurora.store.data.model.MinimalApp +import com.aurora.store.data.model.PermissionType + +/** + * All navigation actions available to composable screens. + * Screens emit one of these via a single `onNavigateTo: (Destination) -> Unit` callback. + */ +sealed class Destination { + data object Splash : Destination() + data class Main(val initialTab: Int) : Destination() + + data class AppDetails(val packageName: String) : Destination() + data class DevProfile(val devId: String) : Destination() + data class AppMenu(val app: MinimalApp) : Destination() + + data object Search : Destination() + data object Downloads : Destination() + + data class StreamBrowse(val cluster: StreamCluster) : Destination() + data class ExpandedStreamBrowse(val title: String, val browseUrl: String) : Destination() + data class CategoryBrowse(val category: Category) : Destination() + data class PermissionRationale(val permissions: Set) : Destination() + + data object Accounts : Destination() + data object GoogleLogin : Destination() + data object About : Destination() + data object Favourite : Destination() + data object Spoof : Destination() + data object Installed : Destination() + data object Blacklist : Destination() + + data object Settings : Destination() + data object InstallationPreference : Destination() + data object Installer : Destination() + data object NetworkPreference : Destination() + data object Dispenser : Destination() + data object UIPreference : Destination() + data object UpdatesPreference : Destination() +} diff --git a/app/src/main/java/com/aurora/store/compose/navigation/NavDisplay.kt b/app/src/main/java/com/aurora/store/compose/navigation/NavDisplay.kt index 96ff65379..d379dbf4b 100644 --- a/app/src/main/java/com/aurora/store/compose/navigation/NavDisplay.kt +++ b/app/src/main/java/com/aurora/store/compose/navigation/NavDisplay.kt @@ -7,16 +7,23 @@ package com.aurora.store.compose.navigation import android.content.Intent -import androidx.activity.compose.LocalActivity +import androidx.compose.animation.EnterTransition +import androidx.compose.animation.ExitTransition +import androidx.compose.animation.core.spring +import androidx.compose.animation.fadeIn +import androidx.compose.animation.slideInHorizontally +import androidx.compose.animation.slideOutHorizontally +import androidx.compose.animation.slideOutVertically +import androidx.compose.animation.togetherWith import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.platform.LocalContext import androidx.lifecycle.Lifecycle import androidx.lifecycle.compose.LifecycleEventEffect import androidx.lifecycle.viewmodel.navigation3.rememberViewModelStoreNavEntryDecorator -import androidx.navigation.NavDeepLinkBuilder import androidx.navigation3.runtime.NavKey import androidx.navigation3.runtime.entryProvider +import androidx.navigation3.runtime.metadata import androidx.navigation3.runtime.rememberNavBackStack import androidx.navigation3.runtime.rememberSaveableStateHolderNavEntryDecorator import androidx.navigation3.ui.NavDisplay @@ -24,11 +31,13 @@ import com.aurora.Constants.PACKAGE_NAME_GMS import com.aurora.extensions.toast import com.aurora.store.AuroraApp import com.aurora.store.ComposeActivity -import com.aurora.store.MainActivity import com.aurora.store.R import com.aurora.store.compose.ui.about.AboutScreen import com.aurora.store.compose.ui.accounts.AccountsScreen +import com.aurora.store.compose.ui.accounts.GoogleLoginScreen import com.aurora.store.compose.ui.blacklist.BlacklistScreen +import com.aurora.store.compose.ui.commons.CategoryBrowseScreen +import com.aurora.store.compose.ui.commons.ExpandedStreamBrowseScreen import com.aurora.store.compose.ui.commons.PermissionRationaleScreen import com.aurora.store.compose.ui.commons.StreamBrowseScreen import com.aurora.store.compose.ui.details.AppDetailsScreen @@ -37,9 +46,16 @@ import com.aurora.store.compose.ui.dispenser.DispenserScreen import com.aurora.store.compose.ui.downloads.DownloadsScreen import com.aurora.store.compose.ui.favourite.FavouriteScreen import com.aurora.store.compose.ui.installed.InstalledScreen +import com.aurora.store.compose.ui.main.MainScreen import com.aurora.store.compose.ui.onboarding.OnboardingScreen +import com.aurora.store.compose.ui.preferences.SettingsScreen +import com.aurora.store.compose.ui.preferences.UIPreferenceScreen +import com.aurora.store.compose.ui.preferences.installation.InstallationPreferenceScreen import com.aurora.store.compose.ui.preferences.installation.InstallerScreen +import com.aurora.store.compose.ui.preferences.network.NetworkPreferenceScreen +import com.aurora.store.compose.ui.preferences.updates.UpdatesPreferenceScreen import com.aurora.store.compose.ui.search.SearchScreen +import com.aurora.store.compose.ui.splash.SplashScreen import com.aurora.store.compose.ui.spoof.SpoofScreen import com.aurora.store.data.event.InstallerEvent import com.aurora.store.data.model.AccountType @@ -54,23 +70,6 @@ import com.aurora.store.util.Preferences @Composable fun NavDisplay(startDestination: NavKey) { val backstack = rememberNavBackStack(startDestination) - - // TODO: Rework when migrating splash fragment to compose - val splashIntent = NavDeepLinkBuilder(LocalContext.current) - .setGraph(R.navigation.mobile_navigation) - .setDestination(R.id.splashFragment) - .setComponentName(MainActivity::class.java) - .createTaskStackBuilder() - .intents - .first() - .apply { addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK) } - - // TODO: Drop this logic once everything is in compose - val activity = LocalActivity.current - fun onNavigateUp() { - if (backstack.size == 1) activity?.finish() else backstack.removeLastOrNull() - } - val context = LocalContext.current fun isMicroGAuthInvalidated(): Boolean = @@ -101,114 +100,169 @@ fun NavDisplay(startDestination: NavKey) { } } + fun navigate(destination: Destination) { + when (destination) { + Destination.Splash -> { + // Clear the backstack when navigating to Splash to prevent going back to the previous screen when the user is sent back to the splash screen (e.g. after logout). + backstack.clear() + backstack.add(Screen.Splash) + } + + is Destination.Main -> { + // Clear the backstack when navigating to Main to prevent going back to the splash screen or other screens. + backstack.clear() + backstack.add(Screen.Main(destination.initialTab)) + } + + is Destination.ExpandedStreamBrowse -> backstack.add( + Screen.ExpandedStreamBrowse(destination.title, destination.browseUrl) + ) + + is Destination.CategoryBrowse -> backstack.add( + Screen.CategoryBrowse(destination.category.title, destination.category.browseUrl) + ) + + is Destination.PermissionRationale -> backstack.add( + Screen.PermissionRationale(destination.permissions) + ) + + is Destination.AppDetails -> backstack.add(Screen.AppDetails(destination.packageName)) + is Destination.DevProfile -> backstack.add(Screen.DevProfile(destination.devId)) + is Destination.AppMenu -> Unit + is Destination.StreamBrowse -> backstack.add(Screen.StreamBrowse(destination.cluster)) + + Destination.Search -> backstack.add(Screen.Search) + Destination.Downloads -> backstack.add(Screen.Downloads) + Destination.Accounts -> backstack.add(Screen.Accounts) + Destination.GoogleLogin -> backstack.add(Screen.GoogleLogin) + Destination.About -> backstack.add(Screen.About) + Destination.Favourite -> backstack.add(Screen.Favourite) + Destination.Spoof -> backstack.add(Screen.Spoof) + Destination.Installed -> backstack.add(Screen.Installed) + Destination.Blacklist -> backstack.add(Screen.Blacklist) + Destination.Settings -> backstack.add(Screen.Settings) + Destination.InstallationPreference -> backstack.add(Screen.InstallationPreference) + Destination.Installer -> backstack.add(Screen.Installer) + Destination.NetworkPreference -> backstack.add(Screen.NetworkPreference) + Destination.Dispenser -> backstack.add(Screen.Dispenser) + Destination.UIPreference -> backstack.add(Screen.UIPreference) + Destination.UpdatesPreference -> backstack.add(Screen.UpdatesPreference) + } + } + NavDisplay( + onBack = { backstack.removeLastOrNull() }, backStack = backstack, entryDecorators = listOf( rememberSaveableStateHolderNavEntryDecorator(), rememberViewModelStoreNavEntryDecorator() ), + transitionSpec = { + slideInHorizontally( + animationSpec = spring(dampingRatio = 0.8f, stiffness = 380f), + initialOffsetX = { it } + ) togetherWith slideOutHorizontally(targetOffsetX = { -it }) + }, + popTransitionSpec = { + slideInHorizontally( + animationSpec = spring(dampingRatio = 0.8f, stiffness = 380f), + initialOffsetX = { -it } + ) togetherWith slideOutHorizontally(targetOffsetX = { it }) + }, + predictivePopTransitionSpec = { + slideInHorizontally( + animationSpec = spring(dampingRatio = 0.8f, stiffness = 380f), + initialOffsetX = { -it } + ) togetherWith slideOutHorizontally(targetOffsetX = { it }) + }, entryProvider = entryProvider { - entry { - BlacklistScreen(onNavigateUp = ::onNavigateUp) - } - - entry { - SearchScreen(onNavigateUp = ::onNavigateUp) + entry { screen -> + MainScreen( + initialTab = screen.initialTab, + onNavigateTo = ::navigate + ) } entry { screen -> AppDetailsScreen( packageName = screen.packageName, - onNavigateUp = ::onNavigateUp, - onNavigateToAppDetails = { packageName -> - backstack.add(Screen.AppDetails(packageName)) - } + onNavigateTo = ::navigate ) } entry { screen -> DevProfileScreen( developerId = screen.developerId, - onNavigateUp = ::onNavigateUp, - onNavigateToAppDetails = { packageName -> - backstack.add(Screen.AppDetails(packageName)) - } + onNavigateTo = ::navigate ) } entry { screen -> PermissionRationaleScreen( - onNavigateUp = ::onNavigateUp, requiredPermissions = screen.requiredPermissions ) } - entry { - DownloadsScreen( - onNavigateUp = ::onNavigateUp, - onNavigateToAppDetails = { packageName -> - backstack.add(Screen.AppDetails(packageName)) - } - ) - } - - entry { - AccountsScreen( - onNavigateUp = ::onNavigateUp, - onNavigateToSplash = { activity?.startActivity(splashIntent) } - ) - } - - entry { - AboutScreen(onNavigateUp = ::onNavigateUp) - } - - entry { - FavouriteScreen( - onNavigateUp = ::onNavigateUp, - onNavigateToAppDetails = { packageName -> - backstack.add(Screen.AppDetails(packageName)) - } - ) - } - - entry { - OnboardingScreen() - } - - entry { - SpoofScreen( - onNavigateUp = ::onNavigateUp, - onNavigateToSplash = { activity?.startActivity(splashIntent) } - ) - } - - entry { - DispenserScreen(onNavigateUp = ::onNavigateUp) - } - - entry { - InstallerScreen(onNavigateUp = ::onNavigateUp) - } - - entry { - InstalledScreen( - onNavigateUp = ::onNavigateUp, - onNavigateToAppDetails = { packageName -> - backstack.add(Screen.AppDetails(packageName)) - } - ) - } - entry { screen -> StreamBrowseScreen( streamCluster = screen.streamCluster, - onNavigateUp = ::onNavigateUp, - onNavigateToAppDetails = { packageName -> - backstack.add(Screen.AppDetails(packageName)) - } + onNavigateTo = ::navigate ) } + + entry { screen -> + ExpandedStreamBrowseScreen( + browseUrl = screen.browseUrl, + defaultTitle = screen.title, + onNavigateTo = ::navigate + ) + } + + entry { screen -> + CategoryBrowseScreen( + title = screen.title, + browseUrl = screen.browseUrl, + onNavigateTo = ::navigate + ) + } + + entry { + InstallationPreferenceScreen(onNavigateTo = ::navigate) + } + + entry( + metadata = metadata { + put(NavDisplay.TransitionKey) { + fadeIn() togetherWith + ExitTransition.KeepUntilTransitionsFinished + } + put(NavDisplay.PopTransitionKey) { + EnterTransition.None togetherWith + slideOutVertically(targetOffsetY = { it }) + } + put(NavDisplay.PredictivePopTransitionKey) { + EnterTransition.None togetherWith + slideOutVertically(targetOffsetY = { it }) + } + } + ) { SearchScreen() } + + entry { SplashScreen(onNavigateTo = ::navigate) } + entry { OnboardingScreen() } + entry { BlacklistScreen() } + entry { DownloadsScreen(onNavigateTo = ::navigate) } + entry { AccountsScreen(onNavigateTo = ::navigate) } + entry { GoogleLoginScreen(onNavigateTo = ::navigate) } + entry { AboutScreen() } + entry { FavouriteScreen(onNavigateTo = ::navigate) } + entry { SpoofScreen(onNavigateTo = ::navigate) } + entry { DispenserScreen() } + entry { InstallerScreen() } + entry { InstalledScreen(onNavigateTo = ::navigate) } + entry { SettingsScreen(onNavigateTo = ::navigate) } + entry { NetworkPreferenceScreen(onNavigateTo = ::navigate) } + entry { UIPreferenceScreen() } + entry { UpdatesPreferenceScreen() } } ) } diff --git a/app/src/main/java/com/aurora/store/compose/navigation/Screen.kt b/app/src/main/java/com/aurora/store/compose/navigation/Screen.kt index be2989737..c651ab0d3 100644 --- a/app/src/main/java/com/aurora/store/compose/navigation/Screen.kt +++ b/app/src/main/java/com/aurora/store/compose/navigation/Screen.kt @@ -44,6 +44,9 @@ sealed class Screen : NavKey, Parcelable { @Serializable data object Accounts : Screen() + @Serializable + data object GoogleLogin : Screen() + @Serializable data object About : Screen() @@ -67,4 +70,31 @@ sealed class Screen : NavKey, Parcelable { @Serializable data class StreamBrowse(val streamCluster: StreamCluster) : Screen() + + @Serializable + data class ExpandedStreamBrowse(val title: String, val browseUrl: String) : Screen() + + @Serializable + data class CategoryBrowse(val title: String, val browseUrl: String) : Screen() + + @Serializable + data object Settings : Screen() + + @Serializable + data object InstallationPreference : Screen() + + @Serializable + data object NetworkPreference : Screen() + + @Serializable + data object UIPreference : Screen() + + @Serializable + data object UpdatesPreference : Screen() + + @Serializable + data object Splash : Screen() + + @Serializable + data class Main(val initialTab: Int = 0) : Screen() } diff --git a/app/src/main/java/com/aurora/store/compose/theme/Theme.kt b/app/src/main/java/com/aurora/store/compose/theme/Theme.kt index 3f6365fda..1b5d8cbb7 100644 --- a/app/src/main/java/com/aurora/store/compose/theme/Theme.kt +++ b/app/src/main/java/com/aurora/store/compose/theme/Theme.kt @@ -6,6 +6,7 @@ package com.aurora.store.compose.theme +import android.content.SharedPreferences import android.os.Build import androidx.activity.compose.LocalActivity import androidx.compose.foundation.isSystemInDarkTheme @@ -16,7 +17,12 @@ import androidx.compose.material3.dynamicDarkColorScheme import androidx.compose.material3.dynamicLightColorScheme import androidx.compose.material3.lightColorScheme import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.SideEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalView import androidx.compose.ui.res.colorResource @@ -31,7 +37,19 @@ import com.aurora.store.util.Preferences fun AuroraTheme(content: @Composable () -> Unit) { val context = LocalContext.current - val themeStyle = Preferences.getInteger(context, Preferences.PREFERENCE_THEME_STYLE) + var themeStyle by remember { + mutableIntStateOf(Preferences.getInteger(context, Preferences.PREFERENCE_THEME_STYLE)) + } + DisposableEffect(Unit) { + val prefs = Preferences.getPrefs(context) + val listener = SharedPreferences.OnSharedPreferenceChangeListener { _, key -> + if (key == Preferences.PREFERENCE_THEME_STYLE) { + themeStyle = Preferences.getInteger(context, Preferences.PREFERENCE_THEME_STYLE) + } + } + prefs.registerOnSharedPreferenceChangeListener(listener) + onDispose { prefs.unregisterOnSharedPreferenceChangeListener(listener) } + } val isDynamicColorSupported = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S val lightScheme = if (isDynamicColorSupported) { @@ -69,11 +87,9 @@ fun AuroraTheme(content: @Composable () -> Unit) { val currentActivity = activity ?: return@SideEffect val window = currentActivity.window - // Transparent system bars window.statusBarColor = android.graphics.Color.TRANSPARENT window.navigationBarColor = android.graphics.Color.TRANSPARENT - // Control icon colors explicitly WindowCompat .getInsetsController(window, view) .apply {