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 {