diff --git a/androidApp/build.gradle.kts b/androidApp/build.gradle.kts index b12d5b2af..c49468c82 100644 --- a/androidApp/build.gradle.kts +++ b/androidApp/build.gradle.kts @@ -94,9 +94,11 @@ dependencies { implementation(libs.voyager.tab) implementation(projects.shared) + implementation(projects.core.appearance) implementation(projects.core.di) implementation(projects.core.utils) implementation(projects.core.navigation) + implementation(projects.core.persistence) implementation(projects.core.resources) kover(projects.shared) diff --git a/androidApp/src/main/java/com/livefast/eattrash/raccoonforlemmy/android/MainActivity.kt b/androidApp/src/main/java/com/livefast/eattrash/raccoonforlemmy/android/MainActivity.kt index 877c4db13..5dcb0fe05 100644 --- a/androidApp/src/main/java/com/livefast/eattrash/raccoonforlemmy/android/MainActivity.kt +++ b/androidApp/src/main/java/com/livefast/eattrash/raccoonforlemmy/android/MainActivity.kt @@ -10,12 +10,19 @@ import androidx.activity.enableEdgeToEdge import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import androidx.lifecycle.lifecycleScope import com.livefast.eattrash.raccoonforlemmy.MainView +import com.livefast.eattrash.raccoonforlemmy.core.appearance.data.toUiBarTheme +import com.livefast.eattrash.raccoonforlemmy.core.appearance.data.toUiTheme +import com.livefast.eattrash.raccoonforlemmy.core.appearance.di.getBarColorProvider +import com.livefast.eattrash.raccoonforlemmy.core.appearance.theme.SolidBarColorWorkaround import com.livefast.eattrash.raccoonforlemmy.core.navigation.ComposeEvent import com.livefast.eattrash.raccoonforlemmy.core.navigation.TabNavigationSection import com.livefast.eattrash.raccoonforlemmy.core.navigation.di.getNavigationCoordinator +import com.livefast.eattrash.raccoonforlemmy.core.persistence.di.getAccountRepository +import com.livefast.eattrash.raccoonforlemmy.core.persistence.di.getSettingsRepository import com.livefast.eattrash.raccoonforlemmy.feature.home.ui.HomeTab import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.runBlocking class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { @@ -79,6 +86,11 @@ class MainActivity : ComponentActivity() { handleIntent(intent) } + override fun onResume() { + super.onResume() + applyWorkaroundForSolidStatusBar() + } + private fun handleIntent(intent: Intent?) = intent?.apply { when (action) { @@ -110,4 +122,20 @@ class MainActivity : ComponentActivity() { val navigationCoordinator = getNavigationCoordinator() navigationCoordinator.submitComposeEvent(event) } + + private fun applyWorkaroundForSolidStatusBar() { + val barColorProvider = getBarColorProvider() + val settingsRepository = getSettingsRepository() + val accountRepository = getAccountRepository() + val settings = + runBlocking { + val accountId = accountRepository.getActive()?.id + settingsRepository.getSettings(accountId) + } + (barColorProvider as? SolidBarColorWorkaround)?.apply( + activity = this@MainActivity, + theme = settings.theme.toUiTheme(), + barTheme = settings.systemBarTheme.toUiBarTheme(), + ) + } } diff --git a/core/appearance/src/androidMain/kotlin/com/livefast/eattrash/raccoonforlemmy/core/appearance/theme/DefaultBarColorProvider.kt b/core/appearance/src/androidMain/kotlin/com/livefast/eattrash/raccoonforlemmy/core/appearance/theme/DefaultBarColorProvider.kt index ec570e39d..6025ccfdb 100644 --- a/core/appearance/src/androidMain/kotlin/com/livefast/eattrash/raccoonforlemmy/core/appearance/theme/DefaultBarColorProvider.kt +++ b/core/appearance/src/androidMain/kotlin/com/livefast/eattrash/raccoonforlemmy/core/appearance/theme/DefaultBarColorProvider.kt @@ -2,6 +2,7 @@ package com.livefast.eattrash.raccoonforlemmy.core.appearance.theme import android.app.Activity import android.os.Build +import android.view.WindowInsets import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -12,9 +13,14 @@ import androidx.core.view.WindowCompat import com.livefast.eattrash.raccoonforlemmy.core.appearance.data.UiBarTheme import com.livefast.eattrash.raccoonforlemmy.core.appearance.data.UiTheme -internal class DefaultBarColorProvider : BarColorProvider { - override val isBarThemeSupported: Boolean - get() = Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM +internal class DefaultBarColorProvider : + BarColorProvider, + SolidBarColorWorkaround { + override val isBarThemeSupported = true + override val isOpaqueThemeSupported: Boolean + get() { + return Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM + } @Composable override fun setBarColorAccordingToTheme( @@ -25,24 +31,8 @@ internal class DefaultBarColorProvider : BarColorProvider { val isSystemInDarkTheme = isSystemInDarkTheme() LaunchedEffect(theme, barTheme) { (view.context as? Activity)?.window?.apply { - val baseColor = - when (theme) { - UiTheme.Light -> Color.White - UiTheme.Black, UiTheme.Dark -> Color.Black - UiTheme.Default -> - if (isSystemInDarkTheme) { - Color.Black - } else { - Color.White - } - else -> Color.Black - } - val barColor = - when (barTheme) { - UiBarTheme.Opaque -> baseColor.copy(alpha = 0.25f) - UiBarTheme.Transparent -> baseColor.copy(alpha = 0.01f) - else -> baseColor - }.toArgb() + val baseColor = theme.getBaseColor(isSystemInDarkTheme) + val barColor = barTheme.getBarColor(baseColor).toArgb() if (isBarThemeSupported) { statusBarColor = barColor navigationBarColor = barColor @@ -50,7 +40,7 @@ internal class DefaultBarColorProvider : BarColorProvider { WindowCompat.setDecorFitsSystemWindows(this, false) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) { + if (isBarThemeSupported) { isStatusBarContrastEnforced = true } isNavigationBarContrastEnforced = true @@ -71,4 +61,50 @@ internal class DefaultBarColorProvider : BarColorProvider { } } } + + override fun apply( + activity: Activity, + theme: UiTheme, + barTheme: UiBarTheme, + ) { + if (barTheme == UiBarTheme.Transparent) { + return + } + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) { + return + } + + val decorView = activity.window.decorView + val isSystemInDarkTheme = activity.resources.configuration.isNightModeActive + val baseColor = theme.getBaseColor(isSystemInDarkTheme) + val barColor = barTheme.getBarColor(baseColor).toArgb() + + decorView.setOnApplyWindowInsetsListener { view, insets -> + val systemBarInsets = insets.getInsets(WindowInsets.Type.systemBars()) + view.setBackgroundColor(barColor) + view.setPadding(0, systemBarInsets.top, 0, systemBarInsets.bottom) + insets + } + } } + +private fun UiTheme.getBaseColor(isSystemInDarkTheme: Boolean): Color = + when (this) { + UiTheme.Light -> Color.White + UiTheme.Black, UiTheme.Dark -> Color.Black + UiTheme.Default -> + if (isSystemInDarkTheme) { + Color.Black + } else { + Color.White + } + + else -> Color.Black + } + +private fun UiBarTheme.getBarColor(baseColor: Color): Color = + when (this) { + UiBarTheme.Opaque -> baseColor.copy(alpha = 0.25f) + UiBarTheme.Transparent -> baseColor.copy(alpha = 0.01f) + else -> baseColor + } diff --git a/core/appearance/src/androidMain/kotlin/com/livefast/eattrash/raccoonforlemmy/core/appearance/theme/SolidBarColorWorkaround.kt b/core/appearance/src/androidMain/kotlin/com/livefast/eattrash/raccoonforlemmy/core/appearance/theme/SolidBarColorWorkaround.kt new file mode 100644 index 000000000..11eb8d4ea --- /dev/null +++ b/core/appearance/src/androidMain/kotlin/com/livefast/eattrash/raccoonforlemmy/core/appearance/theme/SolidBarColorWorkaround.kt @@ -0,0 +1,13 @@ +package com.livefast.eattrash.raccoonforlemmy.core.appearance.theme + +import android.app.Activity +import com.livefast.eattrash.raccoonforlemmy.core.appearance.data.UiBarTheme +import com.livefast.eattrash.raccoonforlemmy.core.appearance.data.UiTheme + +interface SolidBarColorWorkaround { + fun apply( + activity: Activity, + theme: UiTheme, + barTheme: UiBarTheme, + ) +} diff --git a/core/appearance/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/core/appearance/theme/BarColorProvider.kt b/core/appearance/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/core/appearance/theme/BarColorProvider.kt index 68827ef89..e40523f0f 100644 --- a/core/appearance/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/core/appearance/theme/BarColorProvider.kt +++ b/core/appearance/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/core/appearance/theme/BarColorProvider.kt @@ -6,6 +6,7 @@ import com.livefast.eattrash.raccoonforlemmy.core.appearance.data.UiTheme interface BarColorProvider { val isBarThemeSupported: Boolean + val isOpaqueThemeSupported: Boolean @Composable fun setBarColorAccordingToTheme( diff --git a/core/appearance/src/iosMain/kotlin/com/livefast/eattrash/raccoonforlemmy/core/appearance/theme/DefaultBarColorProvider.kt b/core/appearance/src/iosMain/kotlin/com/livefast/eattrash/raccoonforlemmy/core/appearance/theme/DefaultBarColorProvider.kt index 8b6ff98cc..2d9f3fb99 100644 --- a/core/appearance/src/iosMain/kotlin/com/livefast/eattrash/raccoonforlemmy/core/appearance/theme/DefaultBarColorProvider.kt +++ b/core/appearance/src/iosMain/kotlin/com/livefast/eattrash/raccoonforlemmy/core/appearance/theme/DefaultBarColorProvider.kt @@ -11,6 +11,7 @@ import platform.UIKit.setStatusBarStyle internal class DefaultBarColorProvider : BarColorProvider { override val isBarThemeSupported = false + override val isOpaqueThemeSupported = false @Composable override fun setBarColorAccordingToTheme( diff --git a/feature/settings/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/feature/settings/advanced/AdvancedSettingsMviModel.kt b/feature/settings/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/feature/settings/advanced/AdvancedSettingsMviModel.kt index 73aeef2b4..c1e62e405 100644 --- a/feature/settings/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/feature/settings/advanced/AdvancedSettingsMviModel.kt +++ b/feature/settings/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/feature/settings/advanced/AdvancedSettingsMviModel.kt @@ -131,6 +131,7 @@ interface AdvancedSettingsMviModel : val enableAlternateMarkdownRendering: Boolean = false, val restrictLocalUserSearch: Boolean = false, val isBarThemeSupported: Boolean = false, + val isBarOpaqueThemeSupported: Boolean = false, val markAsReadOnInteraction: Boolean = true, ) diff --git a/feature/settings/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/feature/settings/advanced/AdvancedSettingsScreen.kt b/feature/settings/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/feature/settings/advanced/AdvancedSettingsScreen.kt index 444300d9b..6302606eb 100644 --- a/feature/settings/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/feature/settings/advanced/AdvancedSettingsScreen.kt +++ b/feature/settings/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/feature/settings/advanced/AdvancedSettingsScreen.kt @@ -665,11 +665,13 @@ class AdvancedSettingsScreen : Screen { if (barThemeBottomSheetOpened) { val values = - listOf( - UiBarTheme.Transparent, - UiBarTheme.Opaque, - UiBarTheme.Solid, - ) + buildList { + this += UiBarTheme.Transparent + if (uiState.isBarOpaqueThemeSupported) { + this += UiBarTheme.Opaque + } + this += UiBarTheme.Solid + } CustomModalBottomSheet( title = LocalStrings.current.settingsBarTheme, items = diff --git a/feature/settings/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/feature/settings/advanced/AdvancedSettingsViewModel.kt b/feature/settings/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/feature/settings/advanced/AdvancedSettingsViewModel.kt index c528dad98..cfcd26adb 100644 --- a/feature/settings/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/feature/settings/advanced/AdvancedSettingsViewModel.kt +++ b/feature/settings/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/feature/settings/advanced/AdvancedSettingsViewModel.kt @@ -167,6 +167,7 @@ class AdvancedSettingsViewModel( enableAlternateMarkdownRendering = settings.enableAlternateMarkdownRendering, restrictLocalUserSearch = settings.restrictLocalUserSearch, isBarThemeSupported = barColorProvider.isBarThemeSupported, + isBarOpaqueThemeSupported = barColorProvider.isOpaqueThemeSupported, markAsReadOnInteraction = settings.markAsReadOnInteraction, ) }