From caafe2649e3a33a7411b20b0a89fdc52f90a58fb Mon Sep 17 00:00:00 2001 From: Roman Makeev <57789105+makeevrserg@users.noreply.github.com> Date: Wed, 11 Sep 2024 13:32:53 +0300 Subject: [PATCH] Remotecontrols/fix colors (#947) **Background** Remote controls screen has orange app bar, which are different on figma design. **Changes** - Fix app bar colors on remote control screens - Add ThemeStatusBarIconStyleProvider to share it in 4 places with same implementation - Remove snackbar animation and replace it with app bar texts **Test plan** - Open remote controls saved/remote screen - See app bar now non-orange colors and system bar colors are ok - See there's no snackbar, the status instead shown on app bar --- CHANGELOG.md | 1 + .../ChangelogScreenDecomposeComponentImpl.kt | 17 ++-- components/core/ui/decompose/build.gradle.kts | 2 + .../ui/decompose/AndroidWindowDecorator.kt | 2 +- .../internal/WindowDecoratorFactory.kt | 1 + .../ui/decompose/ScreenDecomposeComponent.kt | 2 +- .../internal/WindowDecoratorFactory.kt | 2 + .../StatusBarIconStyleProvider.kt | 2 +- .../ThemeStatusBarIconStyleProvider.kt | 23 +++++ .../internal/WindowDecoratorFactory.kt | 2 + .../KeyScreenViewDecomposeComponentImpl.kt | 16 +--- .../core/ui/layout/shared/SharedTopBar.kt | 59 +++++++++---- .../grid/remote/impl/build.gradle.kts | 1 + .../composable/components/RemoteGridTopBar.kt | 10 ++- .../RemoteGridScreenDecomposeComponentImpl.kt | 11 ++- .../grid/saved/impl/build.gradle.kts | 1 + .../local/composable/LocalGridComposable.kt | 65 ++++++++++---- .../components/ComposableSyncNotification.kt | 86 +++++++------------ .../composable/components/GridOptions.kt | 2 +- .../LocalGridScreenDecomposeComponentImpl.kt | 9 ++ 20 files changed, 193 insertions(+), 121 deletions(-) rename components/core/ui/decompose/src/commonMain/kotlin/com/flipperdevices/ui/decompose/{internal => statusbar}/StatusBarIconStyleProvider.kt (69%) create mode 100644 components/core/ui/decompose/src/commonMain/kotlin/com/flipperdevices/ui/decompose/statusbar/ThemeStatusBarIconStyleProvider.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a7b623f8b..7691d2ce06 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,7 @@ Attention: don't forget to add the flag for F-Droid before release - [FIX] Infinite dispatch after screen close on remote-control screens - [FIX] Bad bottom sheet animation on infrared setup screen - [FIX] Share infrared remote after rename +- [FIX] Fix app bar colors on remote controls - [CI] Fix merge-queue files diff - [CI] Add https://github.com/LionZXY/detekt-decompose-rule - [CI] Enabling detekt module for android and kmp modules diff --git a/components/changelog/impl/src/main/kotlin/com/flipperdevices/changelog/impl/api/ChangelogScreenDecomposeComponentImpl.kt b/components/changelog/impl/src/main/kotlin/com/flipperdevices/changelog/impl/api/ChangelogScreenDecomposeComponentImpl.kt index 94c4844f5f..d60c298009 100644 --- a/components/changelog/impl/src/main/kotlin/com/flipperdevices/changelog/impl/api/ChangelogScreenDecomposeComponentImpl.kt +++ b/components/changelog/impl/src/main/kotlin/com/flipperdevices/changelog/impl/api/ChangelogScreenDecomposeComponentImpl.kt @@ -8,14 +8,12 @@ import com.flipperdevices.changelog.api.ChangelogFormatterApi import com.flipperdevices.changelog.api.ChangelogScreenDecomposeComponent import com.flipperdevices.changelog.impl.composable.ChangelogScreenComposable import com.flipperdevices.core.di.AppGraph -import com.flipperdevices.core.preference.pb.SelectedTheme import com.flipperdevices.core.preference.pb.Settings import com.flipperdevices.ui.decompose.DecomposeOnBackParameter +import com.flipperdevices.ui.decompose.statusbar.ThemeStatusBarIconStyleProvider import com.flipperdevices.updater.model.UpdateRequest import dagger.assisted.Assisted import dagger.assisted.AssistedInject -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.runBlocking import me.gulya.anvil.assisted.ContributesAssistedFactory @ContributesAssistedFactory(AppGraph::class, ChangelogScreenDecomposeComponent.Factory::class) @@ -24,8 +22,11 @@ class ChangelogScreenDecomposeComponentImpl @AssistedInject constructor( @Assisted private val updateRequest: UpdateRequest, @Assisted private val onBack: DecomposeOnBackParameter, private val changelogFormatter: ChangelogFormatterApi, - private val dataStore: DataStore + dataStore: DataStore ) : ChangelogScreenDecomposeComponent(componentContext) { + + private val themeStatusBarIconStyleProvider = ThemeStatusBarIconStyleProvider(dataStore) + @Composable override fun Render() { val changelog = updateRequest.changelog @@ -43,12 +44,6 @@ class ChangelogScreenDecomposeComponentImpl @AssistedInject constructor( } override fun isStatusBarIconLight(systemIsDark: Boolean): Boolean { - val settings = runBlocking { dataStore.data.first() } - return when (settings.selected_theme) { - SelectedTheme.SYSTEM, - is SelectedTheme.Unrecognized -> systemIsDark - SelectedTheme.DARK -> true - SelectedTheme.LIGHT -> false - } + return themeStatusBarIconStyleProvider.isStatusBarIconLight(systemIsDark) } } diff --git a/components/core/ui/decompose/build.gradle.kts b/components/core/ui/decompose/build.gradle.kts index 65ebdfe082..c06366fa8f 100644 --- a/components/core/ui/decompose/build.gradle.kts +++ b/components/core/ui/decompose/build.gradle.kts @@ -8,6 +8,8 @@ android.namespace = "com.flipperdevices.ui.decompose" commonDependencies { implementation(projects.components.core.activityholder) + implementation(projects.components.core.preference) + implementation(libs.compose.ui) implementation(libs.compose.foundation) diff --git a/components/core/ui/decompose/src/androidMain/kotlin/com/flipperdevices/ui/decompose/AndroidWindowDecorator.kt b/components/core/ui/decompose/src/androidMain/kotlin/com/flipperdevices/ui/decompose/AndroidWindowDecorator.kt index 7febcaa1ad..dfbb129864 100644 --- a/components/core/ui/decompose/src/androidMain/kotlin/com/flipperdevices/ui/decompose/AndroidWindowDecorator.kt +++ b/components/core/ui/decompose/src/androidMain/kotlin/com/flipperdevices/ui/decompose/AndroidWindowDecorator.kt @@ -5,8 +5,8 @@ import android.os.Build import android.view.View import android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS import com.flipperdevices.core.activityholder.CurrentActivityHolder -import com.flipperdevices.ui.decompose.internal.StatusBarIconStyleProvider import com.flipperdevices.ui.decompose.internal.WindowDecorator +import com.flipperdevices.ui.decompose.statusbar.StatusBarIconStyleProvider internal class AndroidWindowDecorator( private val statusBarIconStyleProvider: StatusBarIconStyleProvider diff --git a/components/core/ui/decompose/src/androidMain/kotlin/com/flipperdevices/ui/decompose/internal/WindowDecoratorFactory.kt b/components/core/ui/decompose/src/androidMain/kotlin/com/flipperdevices/ui/decompose/internal/WindowDecoratorFactory.kt index a49b19eebe..fb18634ef2 100644 --- a/components/core/ui/decompose/src/androidMain/kotlin/com/flipperdevices/ui/decompose/internal/WindowDecoratorFactory.kt +++ b/components/core/ui/decompose/src/androidMain/kotlin/com/flipperdevices/ui/decompose/internal/WindowDecoratorFactory.kt @@ -1,6 +1,7 @@ package com.flipperdevices.ui.decompose.internal import com.flipperdevices.ui.decompose.AndroidWindowDecorator +import com.flipperdevices.ui.decompose.statusbar.StatusBarIconStyleProvider internal actual fun createWindowDecorator( statusBarIconStyleProvider: StatusBarIconStyleProvider diff --git a/components/core/ui/decompose/src/commonMain/kotlin/com/flipperdevices/ui/decompose/ScreenDecomposeComponent.kt b/components/core/ui/decompose/src/commonMain/kotlin/com/flipperdevices/ui/decompose/ScreenDecomposeComponent.kt index f1fa4e44df..174ef41242 100644 --- a/components/core/ui/decompose/src/commonMain/kotlin/com/flipperdevices/ui/decompose/ScreenDecomposeComponent.kt +++ b/components/core/ui/decompose/src/commonMain/kotlin/com/flipperdevices/ui/decompose/ScreenDecomposeComponent.kt @@ -2,9 +2,9 @@ package com.flipperdevices.ui.decompose import com.arkivanov.decompose.ComponentContext import com.arkivanov.essenty.lifecycle.Lifecycle -import com.flipperdevices.ui.decompose.internal.StatusBarIconStyleProvider import com.flipperdevices.ui.decompose.internal.WindowDecorator import com.flipperdevices.ui.decompose.internal.createWindowDecorator +import com.flipperdevices.ui.decompose.statusbar.StatusBarIconStyleProvider abstract class ScreenDecomposeComponent( componentContext: ComponentContext diff --git a/components/core/ui/decompose/src/commonMain/kotlin/com/flipperdevices/ui/decompose/internal/WindowDecoratorFactory.kt b/components/core/ui/decompose/src/commonMain/kotlin/com/flipperdevices/ui/decompose/internal/WindowDecoratorFactory.kt index 6f4cf2ba4a..84452b6e15 100644 --- a/components/core/ui/decompose/src/commonMain/kotlin/com/flipperdevices/ui/decompose/internal/WindowDecoratorFactory.kt +++ b/components/core/ui/decompose/src/commonMain/kotlin/com/flipperdevices/ui/decompose/internal/WindowDecoratorFactory.kt @@ -1,5 +1,7 @@ package com.flipperdevices.ui.decompose.internal +import com.flipperdevices.ui.decompose.statusbar.StatusBarIconStyleProvider + internal expect fun createWindowDecorator( statusBarIconStyleProvider: StatusBarIconStyleProvider ): WindowDecorator diff --git a/components/core/ui/decompose/src/commonMain/kotlin/com/flipperdevices/ui/decompose/internal/StatusBarIconStyleProvider.kt b/components/core/ui/decompose/src/commonMain/kotlin/com/flipperdevices/ui/decompose/statusbar/StatusBarIconStyleProvider.kt similarity index 69% rename from components/core/ui/decompose/src/commonMain/kotlin/com/flipperdevices/ui/decompose/internal/StatusBarIconStyleProvider.kt rename to components/core/ui/decompose/src/commonMain/kotlin/com/flipperdevices/ui/decompose/statusbar/StatusBarIconStyleProvider.kt index 4ab3910c25..d01216c606 100644 --- a/components/core/ui/decompose/src/commonMain/kotlin/com/flipperdevices/ui/decompose/internal/StatusBarIconStyleProvider.kt +++ b/components/core/ui/decompose/src/commonMain/kotlin/com/flipperdevices/ui/decompose/statusbar/StatusBarIconStyleProvider.kt @@ -1,4 +1,4 @@ -package com.flipperdevices.ui.decompose.internal +package com.flipperdevices.ui.decompose.statusbar internal interface StatusBarIconStyleProvider { fun isStatusBarIconLight(systemIsDark: Boolean): Boolean diff --git a/components/core/ui/decompose/src/commonMain/kotlin/com/flipperdevices/ui/decompose/statusbar/ThemeStatusBarIconStyleProvider.kt b/components/core/ui/decompose/src/commonMain/kotlin/com/flipperdevices/ui/decompose/statusbar/ThemeStatusBarIconStyleProvider.kt new file mode 100644 index 0000000000..ee8e5a7012 --- /dev/null +++ b/components/core/ui/decompose/src/commonMain/kotlin/com/flipperdevices/ui/decompose/statusbar/ThemeStatusBarIconStyleProvider.kt @@ -0,0 +1,23 @@ +package com.flipperdevices.ui.decompose.statusbar + +import androidx.datastore.core.DataStore +import com.flipperdevices.core.preference.pb.SelectedTheme +import com.flipperdevices.core.preference.pb.Settings +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.runBlocking + +class ThemeStatusBarIconStyleProvider( + private val dataStore: DataStore +) : StatusBarIconStyleProvider { + + override fun isStatusBarIconLight(systemIsDark: Boolean): Boolean { + val settings = runBlocking { dataStore.data.first() } + return when (settings.selected_theme) { + SelectedTheme.SYSTEM, + is SelectedTheme.Unrecognized -> systemIsDark + + SelectedTheme.DARK -> true + SelectedTheme.LIGHT -> false + } + } +} diff --git a/components/core/ui/decompose/src/desktopMain/kotlin/com/flipperdevices/ui/decompose/internal/WindowDecoratorFactory.kt b/components/core/ui/decompose/src/desktopMain/kotlin/com/flipperdevices/ui/decompose/internal/WindowDecoratorFactory.kt index 5e8e1b7425..61a87c5ddc 100644 --- a/components/core/ui/decompose/src/desktopMain/kotlin/com/flipperdevices/ui/decompose/internal/WindowDecoratorFactory.kt +++ b/components/core/ui/decompose/src/desktopMain/kotlin/com/flipperdevices/ui/decompose/internal/WindowDecoratorFactory.kt @@ -1,5 +1,7 @@ package com.flipperdevices.ui.decompose.internal +import com.flipperdevices.ui.decompose.statusbar.StatusBarIconStyleProvider + internal actual fun createWindowDecorator( statusBarIconStyleProvider: StatusBarIconStyleProvider ): WindowDecorator = DesktopWindowDecorator() diff --git a/components/keyscreen/impl/src/main/java/com/flipperdevices/keyscreen/impl/api/KeyScreenViewDecomposeComponentImpl.kt b/components/keyscreen/impl/src/main/java/com/flipperdevices/keyscreen/impl/api/KeyScreenViewDecomposeComponentImpl.kt index 3f0e7c5307..657f5376e5 100644 --- a/components/keyscreen/impl/src/main/java/com/flipperdevices/keyscreen/impl/api/KeyScreenViewDecomposeComponentImpl.kt +++ b/components/keyscreen/impl/src/main/java/com/flipperdevices/keyscreen/impl/api/KeyScreenViewDecomposeComponentImpl.kt @@ -14,7 +14,6 @@ import com.arkivanov.decompose.router.stack.pushToFront import com.arkivanov.essenty.backhandler.BackCallback import com.flipperdevices.bridge.dao.api.model.FlipperKeyPath import com.flipperdevices.bridge.synchronization.api.SynchronizationUiApi -import com.flipperdevices.core.preference.pb.SelectedTheme import com.flipperdevices.core.preference.pb.Settings import com.flipperdevices.core.ui.lifecycle.viewModelWithFactory import com.flipperdevices.core.ui.theme.LocalPallet @@ -25,13 +24,12 @@ import com.flipperdevices.keyscreen.impl.viewmodel.KeyScreenViewModel import com.flipperdevices.share.api.ShareBottomUIApi import com.flipperdevices.ui.decompose.DecomposeOnBackParameter import com.flipperdevices.ui.decompose.ScreenDecomposeComponent +import com.flipperdevices.ui.decompose.statusbar.ThemeStatusBarIconStyleProvider import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.update -import kotlinx.coroutines.runBlocking @Suppress("LongParameterList") class KeyScreenViewDecomposeComponentImpl @AssistedInject constructor( @@ -43,10 +41,11 @@ class KeyScreenViewDecomposeComponentImpl @AssistedInject constructor( private val shareBottomApi: ShareBottomUIApi, private val synchronizationUiApi: SynchronizationUiApi, private val keyEmulateApi: KeyEmulateApi, - private val dataStore: DataStore + dataStore: DataStore ) : ScreenDecomposeComponent(componentContext) { private val isBackPressHandledFlow = MutableStateFlow(false) private val backCallback = BackCallback(false) { isBackPressHandledFlow.update { true } } + private val themeStatusBarIconStyleProvider = ThemeStatusBarIconStyleProvider(dataStore) init { backHandler.register(backCallback) @@ -96,14 +95,7 @@ class KeyScreenViewDecomposeComponentImpl @AssistedInject constructor( } override fun isStatusBarIconLight(systemIsDark: Boolean): Boolean { - val settings = runBlocking { dataStore.data.first() } - return when (settings.selected_theme) { - SelectedTheme.SYSTEM, - is SelectedTheme.Unrecognized -> systemIsDark - - SelectedTheme.DARK -> true - SelectedTheme.LIGHT -> false - } + return themeStatusBarIconStyleProvider.isStatusBarIconLight(systemIsDark) } @AssistedFactory diff --git a/components/remote-controls/core-ui/src/main/kotlin/com/flipperdevices/ifrmvp/core/ui/layout/shared/SharedTopBar.kt b/components/remote-controls/core-ui/src/main/kotlin/com/flipperdevices/ifrmvp/core/ui/layout/shared/SharedTopBar.kt index c33c3f4347..e66dae844f 100644 --- a/components/remote-controls/core-ui/src/main/kotlin/com/flipperdevices/ifrmvp/core/ui/layout/shared/SharedTopBar.kt +++ b/components/remote-controls/core-ui/src/main/kotlin/com/flipperdevices/ifrmvp/core/ui/layout/shared/SharedTopBar.kt @@ -15,6 +15,7 @@ import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow @@ -29,14 +30,16 @@ import com.flipperdevices.core.ui.res.R as DesignSystem @Composable fun SharedTopBar( onBackClick: () -> Unit, + title: @Composable () -> Unit, + subtitle: @Composable () -> Unit, modifier: Modifier = Modifier, - title: String = "", - subtitle: String = "", + background: Color = LocalPalletV2.current.surface.navBar.body.accentBrand, + backIconTint: Color = LocalPalletV2.current.icon.blackAndWhite.blackOnColor, actions: @Composable BoxScope.() -> Unit = {} ) { Row( modifier = modifier - .background(LocalPalletV2.current.surface.navBar.body.accentBrand) + .background(background) .statusBarsPadding() .padding(horizontal = 14.dp, vertical = 16.dp) .fillMaxWidth(), @@ -53,7 +56,7 @@ fun SharedTopBar( .clickableRipple(bounded = false, onClick = onBackClick), painter = painterResource(DesignSystem.drawable.ic_back), contentDescription = null, - tint = LocalPalletV2.current.icon.blackAndWhite.blackOnColor + tint = backIconTint ) } ) @@ -63,29 +66,55 @@ fun SharedTopBar( .weight(weight = 2f, fill = false) .padding(horizontal = 8.dp) ) { + title.invoke() + + subtitle.invoke() + } + Box( + modifier = Modifier.weight(weight = 1f), + contentAlignment = Alignment.CenterEnd, + content = { + actions.invoke(this) + } + ) + } +} + +@Composable +fun SharedTopBar( + onBackClick: () -> Unit, + modifier: Modifier = Modifier, + background: Color = LocalPalletV2.current.surface.navBar.body.accentBrand, + backIconTint: Color = LocalPalletV2.current.icon.blackAndWhite.blackOnColor, + textColor: Color = LocalPalletV2.current.text.title.blackOnColor, + title: String = "", + subtitle: String = "", + actions: @Composable BoxScope.() -> Unit = {} +) { + SharedTopBar( + onBackClick = onBackClick, + actions = actions, + modifier = modifier, + background = background, + backIconTint = backIconTint, + title = { Text( text = title, - color = LocalPalletV2.current.text.title.blackOnColor, + color = textColor, style = LocalTypography.current.titleEB18, textAlign = TextAlign.Center, maxLines = 1, overflow = TextOverflow.Ellipsis ) - + }, + subtitle = { Text( text = subtitle, - color = LocalPalletV2.current.text.title.blackOnColor, + color = textColor, style = LocalTypography.current.subtitleM12 ) } - Box( - modifier = Modifier.weight(weight = 1f), - contentAlignment = Alignment.CenterEnd, - content = { - actions.invoke(this) - } - ) - } + ) } @Preview diff --git a/components/remote-controls/grid/remote/impl/build.gradle.kts b/components/remote-controls/grid/remote/impl/build.gradle.kts index 4dd9d37ebb..76b0a76ce6 100644 --- a/components/remote-controls/grid/remote/impl/build.gradle.kts +++ b/components/remote-controls/grid/remote/impl/build.gradle.kts @@ -16,6 +16,7 @@ dependencies { implementation(projects.components.core.ui.ktx) implementation(projects.components.core.ui.res) implementation(projects.components.core.ui.dialog) + implementation(projects.components.core.preference) implementation(projects.components.bridge.dao.api) implementation(projects.components.bridge.service.api) diff --git a/components/remote-controls/grid/remote/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/remote/composable/components/RemoteGridTopBar.kt b/components/remote-controls/grid/remote/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/remote/composable/components/RemoteGridTopBar.kt index 065185fa05..556137fa8f 100644 --- a/components/remote-controls/grid/remote/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/remote/composable/components/RemoteGridTopBar.kt +++ b/components/remote-controls/grid/remote/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/remote/composable/components/RemoteGridTopBar.kt @@ -1,11 +1,13 @@ package com.flipperdevices.remotecontrols.impl.grid.remote.composable.components import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.core.animateIntAsState import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.foundation.layout.Row import androidx.compose.material.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign @@ -27,13 +29,17 @@ internal fun RemoteGridTopBar( SharedTopBar( onBackClick = onBack, title = remoteName.orEmpty(), + background = LocalPalletV2.current.surface.navBar.body.main, + backIconTint = LocalPalletV2.current.icon.blackAndWhite.default, + textColor = LocalPalletV2.current.text.title.primary, subtitle = when { saveProgress == null -> { stringResource(R.string.remote_subtitle) } else -> { - stringResource(R.string.uploading_to_flipper).format("$saveProgress%") + val progress by animateIntAsState(saveProgress) + stringResource(R.string.uploading_to_flipper).format("$progress%") } }, actions = { @@ -45,7 +51,7 @@ internal fun RemoteGridTopBar( Row(modifier = Modifier) { Text( text = stringResource(R.string.save), - color = LocalPalletV2.current.text.title.blackOnColor, + color = LocalPalletV2.current.text.title.primary, style = LocalTypography.current.titleEB18, textAlign = TextAlign.Center, maxLines = 1, diff --git a/components/remote-controls/grid/remote/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/remote/presentation/decompose/internal/RemoteGridScreenDecomposeComponentImpl.kt b/components/remote-controls/grid/remote/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/remote/presentation/decompose/internal/RemoteGridScreenDecomposeComponentImpl.kt index 6561d27ffb..54ae6e96a1 100644 --- a/components/remote-controls/grid/remote/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/remote/presentation/decompose/internal/RemoteGridScreenDecomposeComponentImpl.kt +++ b/components/remote-controls/grid/remote/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/remote/presentation/decompose/internal/RemoteGridScreenDecomposeComponentImpl.kt @@ -1,9 +1,11 @@ package com.flipperdevices.remotecontrols.impl.grid.remote.presentation.decompose.internal import androidx.compose.runtime.Composable +import androidx.datastore.core.DataStore import com.arkivanov.decompose.ComponentContext import com.arkivanov.decompose.childContext import com.flipperdevices.core.di.AppGraph +import com.flipperdevices.core.preference.pb.Settings import com.flipperdevices.faphub.errors.api.FapHubComposableErrorsRenderer import com.flipperdevices.keyedit.api.NotSavedFlipperKey import com.flipperdevices.remotecontrols.api.FlipperDispatchDialogApi @@ -12,6 +14,7 @@ import com.flipperdevices.remotecontrols.grid.remote.api.RemoteGridScreenDecompo import com.flipperdevices.remotecontrols.impl.grid.remote.composable.RemoteGridComposable import com.flipperdevices.remotecontrols.impl.grid.remote.presentation.decompose.RemoteGridComponent import com.flipperdevices.ui.decompose.DecomposeOnBackParameter +import com.flipperdevices.ui.decompose.statusbar.ThemeStatusBarIconStyleProvider import dagger.assisted.Assisted import dagger.assisted.AssistedInject import me.gulya.anvil.assisted.ContributesAssistedFactory @@ -25,7 +28,8 @@ class RemoteGridScreenDecomposeComponentImpl @AssistedInject constructor( @Assisted onSaveKey: (NotSavedFlipperKey) -> Unit, remoteGridComponentFactory: RemoteGridComponent.Factory, flipperDispatchDialogApiFactory: FlipperDispatchDialogApi.Factory, - private val errorsRenderer: FapHubComposableErrorsRenderer + private val errorsRenderer: FapHubComposableErrorsRenderer, + dataStore: DataStore ) : RemoteGridScreenDecomposeComponent(componentContext) { private val gridComponent = remoteGridComponentFactory.invoke( componentContext = childContext("GridComponent"), @@ -34,6 +38,7 @@ class RemoteGridScreenDecomposeComponentImpl @AssistedInject constructor( onSaveKey = onSaveKey ) private val flipperDispatchDialogApi = flipperDispatchDialogApiFactory.invoke(onBack = onBack) + private val themeStatusBarIconStyleProvider = ThemeStatusBarIconStyleProvider(dataStore) @Composable override fun Render() { @@ -43,4 +48,8 @@ class RemoteGridScreenDecomposeComponentImpl @AssistedInject constructor( flipperDispatchDialogApi = flipperDispatchDialogApi ) } + + override fun isStatusBarIconLight(systemIsDark: Boolean): Boolean { + return themeStatusBarIconStyleProvider.isStatusBarIconLight(systemIsDark) + } } diff --git a/components/remote-controls/grid/saved/impl/build.gradle.kts b/components/remote-controls/grid/saved/impl/build.gradle.kts index 60fe151665..508db7c4ca 100644 --- a/components/remote-controls/grid/saved/impl/build.gradle.kts +++ b/components/remote-controls/grid/saved/impl/build.gradle.kts @@ -16,6 +16,7 @@ dependencies { implementation(projects.components.core.ui.ktx) implementation(projects.components.core.ui.res) implementation(projects.components.core.ui.dialog) + implementation(projects.components.core.preference) implementation(projects.components.bridge.dao.api) implementation(projects.components.bridge.service.api) diff --git a/components/remote-controls/grid/saved/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/local/composable/LocalGridComposable.kt b/components/remote-controls/grid/saved/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/local/composable/LocalGridComposable.kt index ed322d95b0..d6d04193dd 100644 --- a/components/remote-controls/grid/saved/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/local/composable/LocalGridComposable.kt +++ b/components/remote-controls/grid/saved/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/local/composable/LocalGridComposable.kt @@ -1,10 +1,11 @@ package com.flipperdevices.remotecontrols.impl.grid.local.composable -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.animation.Crossfade +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding import androidx.compose.material.Scaffold +import androidx.compose.material.Text import androidx.compose.material.rememberScaffoldState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -12,18 +13,19 @@ import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource -import androidx.compose.ui.unit.dp +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextOverflow import com.flipperdevices.core.ui.theme.LocalPalletV2 +import com.flipperdevices.core.ui.theme.LocalTypography import com.flipperdevices.ifrmvp.core.ui.layout.shared.SharedTopBar import com.flipperdevices.infrared.api.InfraredConnectionApi.InfraredEmulateState import com.flipperdevices.remotecontrols.api.FlipperDispatchDialogApi import com.flipperdevices.remotecontrols.grid.saved.impl.R import com.flipperdevices.remotecontrols.impl.grid.local.api.LocalGridScreenDecomposeComponent import com.flipperdevices.remotecontrols.impl.grid.local.composable.components.ComposableInfraredDropDown -import com.flipperdevices.remotecontrols.impl.grid.local.composable.components.ComposableSynchronizationNotification +import com.flipperdevices.remotecontrols.impl.grid.local.composable.components.ComposableNotification import com.flipperdevices.remotecontrols.impl.grid.local.composable.components.LocalGridComposableContent import com.flipperdevices.remotecontrols.impl.grid.local.presentation.decompose.LocalGridComponent @@ -52,8 +54,45 @@ fun LocalGridComposable( (model as? LocalGridComponent.Model.Loaded)?.let { loadedModel -> SharedTopBar( onBackClick = localGridComponent::pop, - title = loadedModel.keyPath.path.nameWithoutExtension, - subtitle = stringResource(R.string.remote_subtitle) + background = LocalPalletV2.current.surface.navBar.body.main, + backIconTint = LocalPalletV2.current.icon.blackAndWhite.default, + title = { + Text( + text = loadedModel.keyPath.path.nameWithoutExtension, + color = LocalPalletV2.current.text.title.primary, + style = LocalTypography.current.titleEB18, + textAlign = TextAlign.Center, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + }, + subtitle = { + Crossfade((model as? LocalGridComponent.Model.Loaded)?.connectionState) { connectionState -> + when (connectionState) { + InfraredEmulateState.ALL_GOOD -> { + Text( + text = stringResource(R.string.remote_subtitle), + color = LocalPalletV2.current.text.title.primary, + style = LocalTypography.current.subtitleM12, + modifier = Modifier.fillMaxWidth(), + textAlign = TextAlign.Center + ) + } + + InfraredEmulateState.NOT_CONNECTED, + InfraredEmulateState.CONNECTING, + InfraredEmulateState.SYNCING, + InfraredEmulateState.UPDATE_FLIPPER -> { + ComposableNotification( + state = connectionState, + modifier = Modifier + ) + } + + null -> Unit + } + } + } ) { ComposableInfraredDropDown( onRename = { @@ -86,18 +125,6 @@ fun LocalGridComposable( .padding(scaffoldPaddings) .navigationBarsPadding() ) - Box( - modifier = Modifier - .fillMaxSize() - .navigationBarsPadding() - .padding(14.dp), - contentAlignment = Alignment.BottomCenter - ) { - val state = (model as? LocalGridComponent.Model.Loaded) - ?.connectionState - ?: InfraredEmulateState.ALL_GOOD - ComposableSynchronizationNotification(state) - } } ) } diff --git a/components/remote-controls/grid/saved/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/local/composable/components/ComposableSyncNotification.kt b/components/remote-controls/grid/saved/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/local/composable/components/ComposableSyncNotification.kt index 11aa3ac463..16f6d8f748 100644 --- a/components/remote-controls/grid/saved/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/local/composable/components/ComposableSyncNotification.kt +++ b/components/remote-controls/grid/saved/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/local/composable/components/ComposableSyncNotification.kt @@ -1,35 +1,27 @@ package com.flipperdevices.remotecontrols.impl.grid.local.composable.components -import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.Crossfade +import androidx.compose.animation.animateColorAsState import androidx.compose.animation.core.LinearEasing import androidx.compose.animation.core.animateFloat import androidx.compose.animation.core.infiniteRepeatable import androidx.compose.animation.core.rememberInfiniteTransition import androidx.compose.animation.core.tween -import androidx.compose.animation.fadeIn -import androidx.compose.animation.fadeOut -import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.Icon -import androidx.compose.material.SnackbarDuration import androidx.compose.material.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.State import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.rotate +import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview @@ -39,7 +31,6 @@ import com.flipperdevices.core.ui.theme.LocalPalletV2 import com.flipperdevices.core.ui.theme.LocalTypography import com.flipperdevices.infrared.api.InfraredConnectionApi.InfraredEmulateState import com.flipperdevices.remotecontrols.grid.saved.impl.R -import kotlinx.coroutines.delay @Composable private fun notificationText(state: InfraredEmulateState): String { @@ -53,6 +44,21 @@ private fun notificationText(state: InfraredEmulateState): String { } } +@Composable +private fun animateNotificationColor(state: InfraredEmulateState): State { + return animateColorAsState( + targetValue = when (state) { + InfraredEmulateState.UPDATE_FLIPPER, + InfraredEmulateState.NOT_CONNECTED -> LocalPalletV2.current.action.danger.text.default + + InfraredEmulateState.CONNECTING, + InfraredEmulateState.SYNCING -> LocalPalletV2.current.action.blue.text.default + + InfraredEmulateState.ALL_GOOD -> Color.Unspecified + } + ) +} + @Composable private fun NotificationIcon(state: InfraredEmulateState) { when (state) { @@ -60,9 +66,8 @@ private fun NotificationIcon(state: InfraredEmulateState) { Icon( painter = painterResource(R.drawable.ic_not_connected), contentDescription = null, - modifier = Modifier - .size(24.dp), - tint = LocalPalletV2.current.illustration.blackAndWhite.black + modifier = Modifier.size(12.dp), + tint = LocalPalletV2.current.action.danger.text.default ) } @@ -84,7 +89,7 @@ private fun NotificationIcon(state: InfraredEmulateState) { painter = painterResource(R.drawable.ic_syncing), contentDescription = null, modifier = Modifier - .size(24.dp) + .size(12.dp) .rotate(angle), tint = LocalPalletV2.current.text.link.default ) @@ -95,8 +100,7 @@ private fun NotificationIcon(state: InfraredEmulateState) { Icon( painter = painterResource(R.drawable.ic_synced), contentDescription = null, - modifier = Modifier - .size(24.dp), + modifier = Modifier.size(12.dp), tint = LocalPalletV2.current.text.link.default ) } @@ -104,60 +108,28 @@ private fun NotificationIcon(state: InfraredEmulateState) { } @Composable -private fun ComposableNotification( +internal fun ComposableNotification( state: InfraredEmulateState, modifier: Modifier = Modifier, ) { Row( - modifier = modifier - .fillMaxWidth() - .clip(RoundedCornerShape(8.dp)) - .background(LocalPalletV2.current.surface.popUp.body.default) - .padding(14.dp), - horizontalArrangement = Arrangement.spacedBy(8.dp), + modifier = modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(8.dp, Alignment.CenterHorizontally), verticalAlignment = Alignment.CenterVertically ) { Crossfade(state) { state -> NotificationIcon(state) } Crossfade(state) { state -> + val color by animateNotificationColor(state) Text( - modifier = Modifier - .padding(top = 9.dp, bottom = 9.dp, end = 12.dp) - .weight(1f), + modifier = Modifier.weight(1f), text = notificationText(state), - style = LocalTypography.current.subtitleB12 + style = LocalTypography.current.subtitleM12, + color = color ) } } } -private const val VISIBILITY_DURATION = 2000L - -@Composable -fun ComposableSynchronizationNotification(state: InfraredEmulateState) { - var isVisible by remember(Unit) { mutableStateOf(false) } - LaunchedEffect(state, isVisible) { - when (state) { - InfraredEmulateState.NOT_CONNECTED -> isVisible = true - InfraredEmulateState.CONNECTING -> isVisible = true - InfraredEmulateState.SYNCING -> isVisible = true - - InfraredEmulateState.UPDATE_FLIPPER, - InfraredEmulateState.ALL_GOOD -> { - if (!isVisible) return@LaunchedEffect - SnackbarDuration.Short - delay(VISIBILITY_DURATION) - isVisible = false - } - } - } - AnimatedVisibility( - visible = isVisible, - enter = fadeIn(), - exit = fadeOut(), - content = { ComposableNotification(state) } - ) -} - @Preview @Composable private fun ComposableNotificationPreview() { diff --git a/components/remote-controls/grid/saved/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/local/composable/components/GridOptions.kt b/components/remote-controls/grid/saved/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/local/composable/components/GridOptions.kt index c2b9aa9b21..bd3aafd8ce 100644 --- a/components/remote-controls/grid/saved/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/local/composable/components/GridOptions.kt +++ b/components/remote-controls/grid/saved/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/local/composable/components/GridOptions.kt @@ -52,7 +52,7 @@ internal fun ComposableInfraredDropDown( val isDropDownEnabled = isConnected && !isEmulating val moreIconTint by animateColorAsState( if (isDropDownEnabled) { - LocalPalletV2.current.icon.blackAndWhite.blackOnColor + LocalPalletV2.current.icon.blackAndWhite.default } else { LocalPalletV2.current.action.neutral.icon.primary.disabled } diff --git a/components/remote-controls/grid/saved/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/local/presentation/decompose/internal/LocalGridScreenDecomposeComponentImpl.kt b/components/remote-controls/grid/saved/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/local/presentation/decompose/internal/LocalGridScreenDecomposeComponentImpl.kt index bd1769545e..f9c29c0a65 100644 --- a/components/remote-controls/grid/saved/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/local/presentation/decompose/internal/LocalGridScreenDecomposeComponentImpl.kt +++ b/components/remote-controls/grid/saved/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/local/presentation/decompose/internal/LocalGridScreenDecomposeComponentImpl.kt @@ -4,17 +4,20 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.datastore.core.DataStore import com.arkivanov.decompose.ComponentContext import com.arkivanov.decompose.childContext import com.arkivanov.essenty.backhandler.BackCallback import com.flipperdevices.bridge.dao.api.model.FlipperKeyPath import com.flipperdevices.core.di.AppGraph +import com.flipperdevices.core.preference.pb.Settings import com.flipperdevices.remotecontrols.api.FlipperDispatchDialogApi import com.flipperdevices.remotecontrols.impl.grid.local.api.LocalGridScreenDecomposeComponent import com.flipperdevices.remotecontrols.impl.grid.local.composable.LocalGridComposable import com.flipperdevices.remotecontrols.impl.grid.local.presentation.decompose.LocalGridComponent import com.flipperdevices.share.api.ShareBottomUIApi import com.flipperdevices.ui.decompose.DecomposeOnBackParameter +import com.flipperdevices.ui.decompose.statusbar.ThemeStatusBarIconStyleProvider import dagger.assisted.Assisted import dagger.assisted.AssistedInject import kotlinx.coroutines.flow.MutableStateFlow @@ -31,6 +34,7 @@ class LocalGridScreenDecomposeComponentImpl @AssistedInject constructor( localGridComponentFactory: LocalGridComponent.Factory, private val shareBottomUiApi: ShareBottomUIApi, flipperDispatchDialogApiFactory: FlipperDispatchDialogApi.Factory, + dataStore: DataStore ) : LocalGridScreenDecomposeComponent(componentContext) { private val localGridComponent = localGridComponentFactory.invoke( componentContext = childContext("GridComponent_local"), @@ -41,6 +45,7 @@ class LocalGridScreenDecomposeComponentImpl @AssistedInject constructor( private val isBackPressHandledFlow = MutableStateFlow(false) private val backCallback = BackCallback(false) { isBackPressHandledFlow.update { true } } + private val themeStatusBarIconStyleProvider = ThemeStatusBarIconStyleProvider(dataStore) init { backHandler.register(backCallback) @@ -71,4 +76,8 @@ class LocalGridScreenDecomposeComponentImpl @AssistedInject constructor( ) } } + + override fun isStatusBarIconLight(systemIsDark: Boolean): Boolean { + return themeStatusBarIconStyleProvider.isStatusBarIconLight(systemIsDark) + } }