From e6f2aff45a79ade42a8c3b2190325312532172e7 Mon Sep 17 00:00:00 2001 From: Roman Makeev <57789105+makeevrserg@users.noreply.github.com> Date: Mon, 9 Sep 2024 16:58:10 +0300 Subject: [PATCH] Remotecontrols/dialogs (#942) **Background** On new remote-controls screen we don't have much dialogs to determine current flipper conditions. This PR adds new dialogs to improve the situation **Changes** - Add dialogs into remote-controls for NotSupported/Busy/NotConnected - Move remotecontrols into bottombar **Test plan** - Open some saved remote or new remote with flipper disconnected or busy - try press button and see dialog --- CHANGELOG.md | 1 + .../category/composable/ComposableCategory.kt | 10 +++-- components/archive/impl/build.gradle.kts | 2 + .../impl/api/ArchiveDecomposeComponentImpl.kt | 10 ++++- .../impl/model/ArchiveNavigationConfig.kt | 7 +++ .../flipperdevices/deeplink/model/Deeplink.kt | 3 ++ .../remote-controls/core-ui/build.gradle.kts | 1 + .../remote/composable/RemoteGridComposable.kt | 7 ++- .../components/RemoteGridComposableContent.kt | 19 +++----- .../decompose/RemoteGridComponent.kt | 5 ++- .../internal/RemoteGridComponentImpl.kt | 2 +- .../RemoteGridScreenDecomposeComponentImpl.kt | 10 ++++- .../mapping/GridComponentStateMapper.kt | 3 +- .../local/composable/LocalGridComposable.kt | 7 ++- .../components/LocalGridComposableContent.kt | 19 +++----- .../decompose/LocalGridComponent.kt | 5 ++- .../internal/LocalGridComponentImpl.kt | 5 ++- .../LocalGridScreenDecomposeComponentImpl.kt | 6 +++ .../remotecontrols/api/DispatchSignalApi.kt | 2 + .../api/FlipperDispatchDialogApi.kt | 31 +++++++++++++ .../setup/impl/build.gradle.kts | 3 ++ .../dialog/FlipperDispatchDialogApiImpl.kt | 38 ++++++++++++++++ .../ComposableFlipperNotConnectedDialog.kt | 24 +++++++++++ .../ComposableFlipperNotSupportedDialog.kt | 24 +++++++++++ .../SetupFlipperDialogComposable.kt | 43 +++++++++++++++++++ .../impl/setup/composable/SetupScreen.kt | 16 +++---- .../composable/components/ConfirmContent.kt | 4 +- .../presentation/decompose/SetupComponent.kt | 5 ++- .../decompose/internal/SetupComponentImpl.kt | 5 ++- .../SetupScreenDecomposeComponentImpl.kt | 8 +++- .../viewmodel/DispatchSignalViewModel.kt | 17 ++++++++ .../impl/src/main/res/values/strings.xml | 6 +++ .../rootscreen/model/RootScreenConfig.kt | 3 -- .../impl/api/RootDecomposeComponentImpl.kt | 7 --- 34 files changed, 287 insertions(+), 71 deletions(-) create mode 100644 components/remote-controls/setup/api/src/main/kotlin/com/flipperdevices/remotecontrols/api/FlipperDispatchDialogApi.kt create mode 100644 components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/api/dialog/FlipperDispatchDialogApiImpl.kt create mode 100644 components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/api/dialog/composable/ComposableFlipperNotConnectedDialog.kt create mode 100644 components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/api/dialog/composable/ComposableFlipperNotSupportedDialog.kt create mode 100644 components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/api/dialog/composable/SetupFlipperDialogComposable.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index 35aca8504f..513cb017f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ Attention: don't forget to add the flag for F-Droid before release - [Feature] Add How to Use dialog into remote-controls - [Feature] Skip infrared signals on setup screen - [Feature] Better user-ux when configuring remote control +- [Feature] Add flipper action dialogs into remote control and move it into bottombar - [Refactor] Load RemoteControls from flipper, emulating animation - [Refactor] Update to Kotlin 2.0 - [Refactor] Replace Ktorfit with Ktor requests in remote-controls diff --git a/components/archive/category/src/main/java/com/flipperdevices/archive/category/composable/ComposableCategory.kt b/components/archive/category/src/main/java/com/flipperdevices/archive/category/composable/ComposableCategory.kt index c8d4055718..38cc3607f7 100644 --- a/components/archive/category/src/main/java/com/flipperdevices/archive/category/composable/ComposableCategory.kt +++ b/components/archive/category/src/main/java/com/flipperdevices/archive/category/composable/ComposableCategory.kt @@ -29,21 +29,23 @@ import com.flipperdevices.core.ui.ktx.clickableRipple import com.flipperdevices.core.ui.theme.LocalPallet import com.flipperdevices.core.ui.theme.LocalPalletV2 import com.flipperdevices.core.ui.theme.LocalTypography +import com.flipperdevices.deeplink.model.Deeplink import com.flipperdevices.keyparser.api.model.FlipperKeyParsed -import com.flipperdevices.rootscreen.api.LocalRootNavigation -import com.flipperdevices.rootscreen.model.RootScreenConfig +import com.flipperdevices.rootscreen.api.LocalDeeplinkHandler import kotlinx.collections.immutable.ImmutableList @Composable fun AddRemoteEndBlock(modifier: Modifier = Modifier) { - val rootNavigation = LocalRootNavigation.current + val deeplinkHandler = LocalDeeplinkHandler.current Text( text = stringResource(R.string.add_remote), style = LocalTypography.current.buttonB14, color = LocalPalletV2.current.text.title.blackOnColor, modifier = modifier .padding(horizontal = 14.dp) - .clickableRipple { rootNavigation.push(RootScreenConfig.RemoteControls) } + .clickableRipple { + deeplinkHandler.handleDeeplink(Deeplink.BottomBar.ArchiveTab.RemoteControls) + } ) } diff --git a/components/archive/impl/build.gradle.kts b/components/archive/impl/build.gradle.kts index 42d70f1e0d..3c8bf8e6f8 100644 --- a/components/archive/impl/build.gradle.kts +++ b/components/archive/impl/build.gradle.kts @@ -26,6 +26,8 @@ dependencies { implementation(projects.components.bottombar.api) implementation(projects.components.rootscreen.api) + implementation(projects.components.remoteControls.main.api) + implementation(projects.components.bridge.dao.api) implementation(projects.components.bridge.synchronization.api) diff --git a/components/archive/impl/src/main/java/com/flipperdevices/archive/impl/api/ArchiveDecomposeComponentImpl.kt b/components/archive/impl/src/main/java/com/flipperdevices/archive/impl/api/ArchiveDecomposeComponentImpl.kt index 92732fc7b8..cefa4c08fb 100644 --- a/components/archive/impl/src/main/java/com/flipperdevices/archive/impl/api/ArchiveDecomposeComponentImpl.kt +++ b/components/archive/impl/src/main/java/com/flipperdevices/archive/impl/api/ArchiveDecomposeComponentImpl.kt @@ -13,6 +13,7 @@ import com.flipperdevices.archive.impl.model.toArchiveNavigationStack import com.flipperdevices.bottombar.handlers.ResetTabDecomposeHandler import com.flipperdevices.core.di.AppGraph import com.flipperdevices.deeplink.model.Deeplink +import com.flipperdevices.remotecontrols.api.RemoteControlsScreenDecomposeComponent import com.flipperdevices.ui.decompose.DecomposeComponent import com.flipperdevices.ui.decompose.DecomposeOnBackParameter import com.flipperdevices.ui.decompose.findComponentByConfig @@ -22,6 +23,7 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedInject import me.gulya.anvil.assisted.ContributesAssistedFactory +@Suppress("LongParameterList") @ContributesAssistedFactory(AppGraph::class, ArchiveDecomposeComponent.Factory::class) class ArchiveDecomposeComponentImpl @AssistedInject constructor( @Assisted componentContext: ComponentContext, @@ -29,7 +31,8 @@ class ArchiveDecomposeComponentImpl @AssistedInject constructor( @Assisted private val onBack: DecomposeOnBackParameter, private val openCategoryFactory: CategoryDecomposeComponent.Factory, private val searchFactory: SearchDecomposeComponent.Factory, - private val archiveScreenFactory: ArchiveScreenDecomposeComponentImpl.Factory + private val archiveScreenFactory: ArchiveScreenDecomposeComponentImpl.Factory, + private val remoteControlsComponentFactory: RemoteControlsScreenDecomposeComponent.Factory, ) : ArchiveDecomposeComponent(), ComponentContext by componentContext, ResetTabDecomposeHandler { @@ -64,6 +67,11 @@ class ArchiveDecomposeComponentImpl @AssistedInject constructor( onItemSelected = null, onBack = { navigation.popOr(onBack::invoke) } ) + + is ArchiveNavigationConfig.RemoteControls -> remoteControlsComponentFactory( + componentContext = componentContext, + onBack = { navigation.popOr(onBack::invoke) } + ) } override fun handleDeeplink(deeplink: Deeplink.BottomBar.ArchiveTab) { diff --git a/components/archive/impl/src/main/java/com/flipperdevices/archive/impl/model/ArchiveNavigationConfig.kt b/components/archive/impl/src/main/java/com/flipperdevices/archive/impl/model/ArchiveNavigationConfig.kt index 66eb4ef016..9e35ce11c9 100644 --- a/components/archive/impl/src/main/java/com/flipperdevices/archive/impl/model/ArchiveNavigationConfig.kt +++ b/components/archive/impl/src/main/java/com/flipperdevices/archive/impl/model/ArchiveNavigationConfig.kt @@ -17,6 +17,9 @@ sealed class ArchiveNavigationConfig { @Serializable data object OpenSearch : ArchiveNavigationConfig() + + @Serializable + data object RemoteControls : ArchiveNavigationConfig() } fun Deeplink.BottomBar.ArchiveTab?.toArchiveNavigationStack(): List { @@ -36,6 +39,10 @@ fun Deeplink.BottomBar.ArchiveTab?.toArchiveNavigationStack(): List { } + + Deeplink.BottomBar.ArchiveTab.RemoteControls -> { + stack.add(ArchiveNavigationConfig.RemoteControls) + } } return stack } diff --git a/components/deeplink/api/src/main/java/com/flipperdevices/deeplink/model/Deeplink.kt b/components/deeplink/api/src/main/java/com/flipperdevices/deeplink/model/Deeplink.kt index faa4e02cd8..4c8a93710a 100644 --- a/components/deeplink/api/src/main/java/com/flipperdevices/deeplink/model/Deeplink.kt +++ b/components/deeplink/api/src/main/java/com/flipperdevices/deeplink/model/Deeplink.kt @@ -51,6 +51,9 @@ sealed interface Deeplink { @Serializable sealed interface ArchiveTab : BottomBar { + @Serializable + data object RemoteControls : ArchiveTab + @Serializable sealed interface ArchiveCategory : ArchiveTab { val category: FlipperKeyType? diff --git a/components/remote-controls/core-ui/build.gradle.kts b/components/remote-controls/core-ui/build.gradle.kts index fca6ec02ba..3836db6efc 100644 --- a/components/remote-controls/core-ui/build.gradle.kts +++ b/components/remote-controls/core-ui/build.gradle.kts @@ -10,6 +10,7 @@ dependencies { implementation(projects.components.core.ui.theme) implementation(projects.components.core.ui.ktx) implementation(projects.components.core.ui.res) + implementation(projects.components.core.ui.dialog) implementation(projects.components.remoteControls.apiBackend) implementation(projects.components.remoteControls.coreModel) diff --git a/components/remote-controls/grid/remote/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/remote/composable/RemoteGridComposable.kt b/components/remote-controls/grid/remote/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/remote/composable/RemoteGridComposable.kt index 52664fe0ba..0b58e998d4 100644 --- a/components/remote-controls/grid/remote/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/remote/composable/RemoteGridComposable.kt +++ b/components/remote-controls/grid/remote/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/remote/composable/RemoteGridComposable.kt @@ -11,6 +11,7 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import com.flipperdevices.core.ui.theme.LocalPalletV2 +import com.flipperdevices.remotecontrols.api.FlipperDispatchDialogApi import com.flipperdevices.remotecontrols.impl.grid.remote.composable.components.RemoteGridComposableContent import com.flipperdevices.remotecontrols.impl.grid.remote.composable.components.RemoteGridTopBar import com.flipperdevices.remotecontrols.impl.grid.remote.presentation.decompose.RemoteGridComponent @@ -18,6 +19,7 @@ import com.flipperdevices.remotecontrols.impl.grid.remote.presentation.decompose @Composable fun RemoteGridComposable( remoteGridComponent: RemoteGridComponent, + flipperDispatchDialogApi: FlipperDispatchDialogApi, modifier: Modifier = Modifier ) { val coroutineScope = rememberCoroutineScope() @@ -43,8 +45,11 @@ fun RemoteGridComposable( content = { scaffoldPaddings -> RemoteGridComposableContent( remoteGridComponent = remoteGridComponent, + flipperDispatchDialogApi = flipperDispatchDialogApi, model = model, - modifier = Modifier.padding(scaffoldPaddings).navigationBarsPadding() + modifier = Modifier + .padding(scaffoldPaddings) + .navigationBarsPadding() ) } ) diff --git a/components/remote-controls/grid/remote/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/remote/composable/components/RemoteGridComposableContent.kt b/components/remote-controls/grid/remote/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/remote/composable/components/RemoteGridComposableContent.kt index 665a8f4592..dcf8607878 100644 --- a/components/remote-controls/grid/remote/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/remote/composable/components/RemoteGridComposableContent.kt +++ b/components/remote-controls/grid/remote/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/remote/composable/components/RemoteGridComposableContent.kt @@ -15,25 +15,23 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp -import com.flipperdevices.core.ui.dialog.composable.busy.ComposableFlipperBusy import com.flipperdevices.core.ui.theme.LocalPallet import com.flipperdevices.core.ui.theme.LocalPalletV2 import com.flipperdevices.core.ui.theme.LocalTypography import com.flipperdevices.ifrmvp.core.ui.layout.shared.ErrorComposable import com.flipperdevices.ifrmvp.core.ui.layout.shared.GridPagesContent +import com.flipperdevices.remotecontrols.api.FlipperDispatchDialogApi import com.flipperdevices.remotecontrols.grid.remote.impl.R import com.flipperdevices.remotecontrols.impl.grid.remote.composable.util.contentKey import com.flipperdevices.remotecontrols.impl.grid.remote.presentation.decompose.RemoteGridComponent -import com.flipperdevices.rootscreen.api.LocalRootNavigation -import com.flipperdevices.rootscreen.model.RootScreenConfig @Composable internal fun RemoteGridComposableContent( remoteGridComponent: RemoteGridComponent, + flipperDispatchDialogApi: FlipperDispatchDialogApi, model: RemoteGridComponent.Model, modifier: Modifier = Modifier ) { - val rootNavigation = LocalRootNavigation.current AnimatedContent( targetState = model, modifier = modifier, @@ -49,15 +47,10 @@ internal fun RemoteGridComposableContent( } is RemoteGridComponent.Model.Loaded -> { - if (animatedModel.isFlipperBusy) { - ComposableFlipperBusy( - onDismiss = remoteGridComponent::dismissBusyDialog, - goToRemote = { - remoteGridComponent.dismissBusyDialog() - rootNavigation.push(RootScreenConfig.ScreenStreaming) - } - ) - } + flipperDispatchDialogApi.Render( + dialogType = animatedModel.flipperDialog, + onDismiss = remoteGridComponent::dismissDialog, + ) GridPagesContent( pagesLayout = animatedModel.pagesLayout, onButtonClick = { _, keyIdentifier -> diff --git a/components/remote-controls/grid/remote/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/remote/presentation/decompose/RemoteGridComponent.kt b/components/remote-controls/grid/remote/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/remote/presentation/decompose/RemoteGridComponent.kt index 080ca003e5..61480cbe2e 100644 --- a/components/remote-controls/grid/remote/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/remote/presentation/decompose/RemoteGridComponent.kt +++ b/components/remote-controls/grid/remote/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/remote/presentation/decompose/RemoteGridComponent.kt @@ -6,6 +6,7 @@ import com.flipperdevices.ifrmvp.model.PagesLayout import com.flipperdevices.infrared.api.InfraredConnectionApi.InfraredEmulateState import com.flipperdevices.infrared.editor.core.model.InfraredRemote import com.flipperdevices.keyedit.api.NotSavedFlipperKey +import com.flipperdevices.remotecontrols.api.FlipperDispatchDialogApi import com.flipperdevices.remotecontrols.api.SaveTempSignalApi import com.flipperdevices.remotecontrols.api.model.ServerRemoteControlParam import com.flipperdevices.ui.decompose.DecomposeOnBackParameter @@ -23,7 +24,7 @@ interface RemoteGridComponent { fun pop() - fun dismissBusyDialog() + fun dismissDialog() fun save() @@ -35,7 +36,7 @@ interface RemoteGridComponent { data class Loaded( val pagesLayout: PagesLayout, val remotes: ImmutableList, - val isFlipperBusy: Boolean = false, + val flipperDialog: FlipperDispatchDialogApi.DialogType? = null, val emulatedKey: IfrKeyIdentifier? = null, val saveState: SaveTempSignalApi.State, val connectionState: InfraredEmulateState diff --git a/components/remote-controls/grid/remote/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/remote/presentation/decompose/internal/RemoteGridComponentImpl.kt b/components/remote-controls/grid/remote/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/remote/presentation/decompose/internal/RemoteGridComponentImpl.kt index 0d256cfa85..9fa384427e 100644 --- a/components/remote-controls/grid/remote/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/remote/presentation/decompose/internal/RemoteGridComponentImpl.kt +++ b/components/remote-controls/grid/remote/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/remote/presentation/decompose/internal/RemoteGridComponentImpl.kt @@ -97,7 +97,7 @@ class RemoteGridComponentImpl @AssistedInject constructor( } ).stateIn(coroutineScope, SharingStarted.Eagerly, RemoteGridComponent.Model.Loading()) - override fun dismissBusyDialog() { + override fun dismissDialog() { dispatchSignalApi.dismissBusyDialog() } 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 5d5848671f..4ff53ea06d 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 @@ -5,6 +5,7 @@ import com.arkivanov.decompose.ComponentContext import com.arkivanov.decompose.childContext import com.flipperdevices.core.di.AppGraph import com.flipperdevices.keyedit.api.NotSavedFlipperKey +import com.flipperdevices.remotecontrols.api.FlipperDispatchDialogApi import com.flipperdevices.remotecontrols.api.model.ServerRemoteControlParam import com.flipperdevices.remotecontrols.grid.remote.api.RemoteGridScreenDecomposeComponent import com.flipperdevices.remotecontrols.impl.grid.remote.composable.RemoteGridComposable @@ -20,7 +21,8 @@ class RemoteGridScreenDecomposeComponentImpl @AssistedInject constructor( @Assisted param: ServerRemoteControlParam, @Assisted onBack: DecomposeOnBackParameter, @Assisted onSaveKey: (NotSavedFlipperKey) -> Unit, - remoteGridComponentFactory: RemoteGridComponent.Factory + remoteGridComponentFactory: RemoteGridComponent.Factory, + flipperDispatchDialogApiFactory: FlipperDispatchDialogApi.Factory, ) : RemoteGridScreenDecomposeComponent(componentContext) { private val gridComponent = remoteGridComponentFactory.invoke( componentContext = childContext("GridComponent"), @@ -28,9 +30,13 @@ class RemoteGridScreenDecomposeComponentImpl @AssistedInject constructor( onBack = onBack, onSaveKey = onSaveKey ) + private val flipperDispatchDialogApi = flipperDispatchDialogApiFactory.invoke(onBack = onBack) @Composable override fun Render() { - RemoteGridComposable(gridComponent) + RemoteGridComposable( + remoteGridComponent = gridComponent, + flipperDispatchDialogApi = flipperDispatchDialogApi + ) } } diff --git a/components/remote-controls/grid/remote/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/remote/presentation/mapping/GridComponentStateMapper.kt b/components/remote-controls/grid/remote/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/remote/presentation/mapping/GridComponentStateMapper.kt index 53e6c2d5f2..4c9fffcd6b 100644 --- a/components/remote-controls/grid/remote/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/remote/presentation/mapping/GridComponentStateMapper.kt +++ b/components/remote-controls/grid/remote/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/remote/presentation/mapping/GridComponentStateMapper.kt @@ -2,6 +2,7 @@ package com.flipperdevices.remotecontrols.impl.grid.remote.presentation.mapping import com.flipperdevices.infrared.api.InfraredConnectionApi import com.flipperdevices.remotecontrols.api.DispatchSignalApi +import com.flipperdevices.remotecontrols.api.FlipperDispatchDialogApi.Companion.toDialogType import com.flipperdevices.remotecontrols.api.SaveTempSignalApi import com.flipperdevices.remotecontrols.impl.grid.remote.presentation.decompose.RemoteGridComponent import com.flipperdevices.remotecontrols.impl.grid.remote.presentation.viewmodel.RemoteGridViewModel @@ -23,7 +24,7 @@ internal object GridComponentStateMapper { RemoteGridComponent.Model.Loaded( pagesLayout = gridState.pagesLayout, remotes = gridState.remotes, - isFlipperBusy = dispatchState is DispatchSignalApi.State.FlipperIsBusy, + flipperDialog = dispatchState.toDialogType(), emulatedKey = (dispatchState as? DispatchSignalApi.State.Emulating)?.ifrKeyIdentifier, saveState = saveState, connectionState = connectionState 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 35eff52526..ed322d95b0 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 @@ -19,6 +19,7 @@ import androidx.compose.ui.unit.dp import com.flipperdevices.core.ui.theme.LocalPalletV2 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 @@ -30,6 +31,7 @@ import com.flipperdevices.remotecontrols.impl.grid.local.presentation.decompose. @Suppress("LongMethod") fun LocalGridComposable( localGridComponent: LocalGridComponent, + flipperDispatchDialogApi: FlipperDispatchDialogApi, onCallback: (LocalGridScreenDecomposeComponent.Callback) -> Unit, onShare: () -> Unit, modifier: Modifier = Modifier @@ -78,8 +80,11 @@ fun LocalGridComposable( content = { scaffoldPaddings -> LocalGridComposableContent( localGridComponent = localGridComponent, + flipperDispatchDialogApi = flipperDispatchDialogApi, model = model, - modifier = Modifier.padding(scaffoldPaddings).navigationBarsPadding() + modifier = Modifier + .padding(scaffoldPaddings) + .navigationBarsPadding() ) Box( modifier = Modifier diff --git a/components/remote-controls/grid/saved/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/local/composable/components/LocalGridComposableContent.kt b/components/remote-controls/grid/saved/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/local/composable/components/LocalGridComposableContent.kt index 1fa2b152e4..9eaa98ef49 100644 --- a/components/remote-controls/grid/saved/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/local/composable/components/LocalGridComposableContent.kt +++ b/components/remote-controls/grid/saved/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/local/composable/components/LocalGridComposableContent.kt @@ -15,24 +15,22 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp -import com.flipperdevices.core.ui.dialog.composable.busy.ComposableFlipperBusy import com.flipperdevices.core.ui.theme.LocalPallet import com.flipperdevices.core.ui.theme.LocalPalletV2 import com.flipperdevices.core.ui.theme.LocalTypography import com.flipperdevices.ifrmvp.core.ui.layout.shared.GridPagesContent +import com.flipperdevices.remotecontrols.api.FlipperDispatchDialogApi import com.flipperdevices.remotecontrols.grid.saved.impl.R import com.flipperdevices.remotecontrols.impl.grid.local.composable.util.contentKey import com.flipperdevices.remotecontrols.impl.grid.local.presentation.decompose.LocalGridComponent -import com.flipperdevices.rootscreen.api.LocalRootNavigation -import com.flipperdevices.rootscreen.model.RootScreenConfig @Composable internal fun LocalGridComposableContent( localGridComponent: LocalGridComponent, + flipperDispatchDialogApi: FlipperDispatchDialogApi, model: LocalGridComponent.Model, modifier: Modifier = Modifier ) { - val rootNavigation = LocalRootNavigation.current AnimatedContent( targetState = model, modifier = modifier, @@ -44,15 +42,10 @@ internal fun LocalGridComposableContent( LocalGridComponent.Model.Error -> Unit is LocalGridComponent.Model.Loaded -> { - if (animatedModel.isFlipperBusy) { - ComposableFlipperBusy( - onDismiss = localGridComponent::dismissBusyDialog, - goToRemote = { - localGridComponent.dismissBusyDialog() - rootNavigation.push(RootScreenConfig.ScreenStreaming) - } - ) - } + flipperDispatchDialogApi.Render( + dialogType = animatedModel.flipperDialog, + onDismiss = localGridComponent::dismissDialog + ) GridPagesContent( pagesLayout = animatedModel.pagesLayout, onButtonClick = { _, keyIdentifier -> diff --git a/components/remote-controls/grid/saved/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/local/presentation/decompose/LocalGridComponent.kt b/components/remote-controls/grid/saved/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/local/presentation/decompose/LocalGridComponent.kt index 21baa506d5..a369193286 100644 --- a/components/remote-controls/grid/saved/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/local/presentation/decompose/LocalGridComponent.kt +++ b/components/remote-controls/grid/saved/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/local/presentation/decompose/LocalGridComponent.kt @@ -6,6 +6,7 @@ import com.flipperdevices.ifrmvp.model.IfrKeyIdentifier import com.flipperdevices.ifrmvp.model.PagesLayout import com.flipperdevices.infrared.api.InfraredConnectionApi.InfraredEmulateState import com.flipperdevices.infrared.editor.core.model.InfraredRemote +import com.flipperdevices.remotecontrols.api.FlipperDispatchDialogApi import com.flipperdevices.ui.decompose.DecomposeOnBackParameter import kotlinx.collections.immutable.ImmutableList import kotlinx.coroutines.CoroutineScope @@ -17,14 +18,14 @@ interface LocalGridComponent { fun onRename(onEndAction: (FlipperKeyPath) -> Unit) fun onDelete(onEndAction: () -> Unit) fun pop() - fun dismissBusyDialog() + fun dismissDialog() sealed interface Model { data object Loading : Model data class Loaded( val pagesLayout: PagesLayout, val remotes: ImmutableList, - val isFlipperBusy: Boolean, + val flipperDialog: FlipperDispatchDialogApi.DialogType? = null, val emulatedKey: IfrKeyIdentifier?, val connectionState: InfraredEmulateState, val keyPath: FlipperKeyPath diff --git a/components/remote-controls/grid/saved/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/local/presentation/decompose/internal/LocalGridComponentImpl.kt b/components/remote-controls/grid/saved/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/local/presentation/decompose/internal/LocalGridComponentImpl.kt index 69f6d965ec..260488b08a 100644 --- a/components/remote-controls/grid/saved/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/local/presentation/decompose/internal/LocalGridComponentImpl.kt +++ b/components/remote-controls/grid/saved/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/grid/local/presentation/decompose/internal/LocalGridComponentImpl.kt @@ -6,6 +6,7 @@ import com.flipperdevices.bridge.dao.api.model.FlipperKeyPath import com.flipperdevices.core.di.AppGraph import com.flipperdevices.ifrmvp.model.IfrKeyIdentifier import com.flipperdevices.remotecontrols.api.DispatchSignalApi +import com.flipperdevices.remotecontrols.api.FlipperDispatchDialogApi.Companion.toDialogType import com.flipperdevices.remotecontrols.impl.grid.local.presentation.decompose.LocalGridComponent import com.flipperdevices.remotecontrols.impl.grid.local.presentation.viewmodel.ConnectionViewModel import com.flipperdevices.remotecontrols.impl.grid.local.presentation.viewmodel.LocalGridViewModel @@ -51,7 +52,7 @@ class LocalGridComponentImpl @AssistedInject constructor( is LocalGridViewModel.State.Loaded -> LocalGridComponent.Model.Loaded( pagesLayout = gridState.pagesLayout, remotes = gridState.remotes, - isFlipperBusy = dispatchState is DispatchSignalApi.State.FlipperIsBusy, + flipperDialog = dispatchState.toDialogType(), emulatedKey = (dispatchState as? DispatchSignalApi.State.Emulating)?.ifrKeyIdentifier, connectionState = connectionState, keyPath = gridState.keyPath @@ -80,7 +81,7 @@ class LocalGridComponentImpl @AssistedInject constructor( override fun pop() = onBack.invoke() - override fun dismissBusyDialog() { + override fun dismissDialog() { dispatchSignalApi.dismissBusyDialog() } } 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 31a2f3f236..f45b3d1d32 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 @@ -9,6 +9,7 @@ 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.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 @@ -20,6 +21,7 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.update import me.gulya.anvil.assisted.ContributesAssistedFactory +@Suppress("LongParameterList") @ContributesAssistedFactory(AppGraph::class, LocalGridScreenDecomposeComponent.Factory::class) class LocalGridScreenDecomposeComponentImpl @AssistedInject constructor( @Assisted componentContext: ComponentContext, @@ -28,12 +30,15 @@ class LocalGridScreenDecomposeComponentImpl @AssistedInject constructor( @Assisted private val onCallback: (Callback) -> Unit, localGridComponentFactory: LocalGridComponent.Factory, private val shareBottomUiApi: ShareBottomUIApi, + flipperDispatchDialogApiFactory: FlipperDispatchDialogApi.Factory, ) : LocalGridScreenDecomposeComponent(componentContext) { private val localGridComponent = localGridComponentFactory.invoke( componentContext = childContext("GridComponent_local"), keyPath = keyPath, onBack = onBack, ) + private val flipperDispatchDialogApi = flipperDispatchDialogApiFactory.invoke(onBack = onBack) + private val isBackPressHandledFlow = MutableStateFlow(false) private val backCallback = BackCallback(false) { isBackPressHandledFlow.update { true } } @@ -60,6 +65,7 @@ class LocalGridScreenDecomposeComponentImpl @AssistedInject constructor( ) { onShare -> LocalGridComposable( localGridComponent = localGridComponent, + flipperDispatchDialogApi = flipperDispatchDialogApi, onCallback = onCallback, onShare = onShare ) diff --git a/components/remote-controls/setup/api/src/main/kotlin/com/flipperdevices/remotecontrols/api/DispatchSignalApi.kt b/components/remote-controls/setup/api/src/main/kotlin/com/flipperdevices/remotecontrols/api/DispatchSignalApi.kt index 8b5d61b0d8..1de248ae29 100644 --- a/components/remote-controls/setup/api/src/main/kotlin/com/flipperdevices/remotecontrols/api/DispatchSignalApi.kt +++ b/components/remote-controls/setup/api/src/main/kotlin/com/flipperdevices/remotecontrols/api/DispatchSignalApi.kt @@ -36,6 +36,8 @@ interface DispatchSignalApi : InstanceKeeper.Instance { sealed interface State { data object Pending : State data object FlipperIsBusy : State + data object FlipperNotConnected : State + data object FlipperNotSupported : State data class Emulating(val ifrKeyIdentifier: IfrKeyIdentifier) : State data object Error : State } diff --git a/components/remote-controls/setup/api/src/main/kotlin/com/flipperdevices/remotecontrols/api/FlipperDispatchDialogApi.kt b/components/remote-controls/setup/api/src/main/kotlin/com/flipperdevices/remotecontrols/api/FlipperDispatchDialogApi.kt new file mode 100644 index 0000000000..e33d641e43 --- /dev/null +++ b/components/remote-controls/setup/api/src/main/kotlin/com/flipperdevices/remotecontrols/api/FlipperDispatchDialogApi.kt @@ -0,0 +1,31 @@ +package com.flipperdevices.remotecontrols.api + +import androidx.compose.runtime.Composable +import com.flipperdevices.ui.decompose.DecomposeOnBackParameter + +interface FlipperDispatchDialogApi { + enum class DialogType { + FLIPPER_IS_BUSY, FLIPPER_NOT_CONNECTED, FLIPPER_NOT_SUPPORTED + } + + @Composable + fun Render( + dialogType: FlipperDispatchDialogApi.DialogType?, + onDismiss: () -> Unit + ) + + fun interface Factory { + operator fun invoke( + onBack: DecomposeOnBackParameter + ): FlipperDispatchDialogApi + } + + companion object { + fun DispatchSignalApi.State.toDialogType() = when (this) { + DispatchSignalApi.State.FlipperIsBusy -> DialogType.FLIPPER_IS_BUSY + DispatchSignalApi.State.FlipperNotConnected -> DialogType.FLIPPER_NOT_CONNECTED + DispatchSignalApi.State.FlipperNotSupported -> DialogType.FLIPPER_NOT_SUPPORTED + else -> null + } + } +} diff --git a/components/remote-controls/setup/impl/build.gradle.kts b/components/remote-controls/setup/impl/build.gradle.kts index 4541b056fa..4b10137492 100644 --- a/components/remote-controls/setup/impl/build.gradle.kts +++ b/components/remote-controls/setup/impl/build.gradle.kts @@ -25,6 +25,9 @@ dependencies { implementation(projects.components.infrared.utils) implementation(projects.components.infrared.api) + implementation(projects.components.faphub.target.api) + implementation(projects.components.deeplink.api) + implementation(projects.components.remoteControls.apiBackend) implementation(projects.components.remoteControls.coreModel) implementation(projects.components.remoteControls.coreUi) diff --git a/components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/api/dialog/FlipperDispatchDialogApiImpl.kt b/components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/api/dialog/FlipperDispatchDialogApiImpl.kt new file mode 100644 index 0000000000..048170e7cc --- /dev/null +++ b/components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/api/dialog/FlipperDispatchDialogApiImpl.kt @@ -0,0 +1,38 @@ +package com.flipperdevices.remotecontrols.impl.setup.api.dialog + +import androidx.compose.runtime.Composable +import com.flipperdevices.core.di.AppGraph +import com.flipperdevices.deeplink.model.Deeplink +import com.flipperdevices.deeplink.model.DeeplinkBottomBarTab +import com.flipperdevices.remotecontrols.api.FlipperDispatchDialogApi +import com.flipperdevices.remotecontrols.impl.setup.api.dialog.composable.SetupFlipperDialogComposable +import com.flipperdevices.rootscreen.api.LocalDeeplinkHandler +import com.flipperdevices.ui.decompose.DecomposeOnBackParameter +import dagger.assisted.Assisted +import dagger.assisted.AssistedInject +import me.gulya.anvil.assisted.ContributesAssistedFactory + +@ContributesAssistedFactory(AppGraph::class, FlipperDispatchDialogApi.Factory::class) +class FlipperDispatchDialogApiImpl @AssistedInject constructor( + @Assisted private val onBack: DecomposeOnBackParameter +) : FlipperDispatchDialogApi { + @Composable + override fun Render( + dialogType: FlipperDispatchDialogApi.DialogType?, + onDismiss: () -> Unit + ) { + val deeplinkHandler = LocalDeeplinkHandler.current + SetupFlipperDialogComposable( + flipperDialog = dialogType, + onDismiss = onDismiss, + openDeviceTab = { + deeplinkHandler.handleDeeplink( + Deeplink.BottomBar.OpenTab( + DeeplinkBottomBarTab.DEVICE + ) + ) + onBack.invoke() + } + ) + } +} diff --git a/components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/api/dialog/composable/ComposableFlipperNotConnectedDialog.kt b/components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/api/dialog/composable/ComposableFlipperNotConnectedDialog.kt new file mode 100644 index 0000000000..b70c0c46cf --- /dev/null +++ b/components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/api/dialog/composable/ComposableFlipperNotConnectedDialog.kt @@ -0,0 +1,24 @@ +package com.flipperdevices.remotecontrols.impl.setup.api.dialog.composable + +import androidx.compose.runtime.Composable +import com.flipperdevices.core.ui.dialog.composable.FlipperDialog +import com.flipperdevices.remotecontrols.setup.impl.R +import com.flipperdevices.core.ui.res.R as DesignSystem + +@Composable +fun ComposableFlipperNotConnectedDialog( + onDismiss: () -> Unit, + onOpenDeviceTab: () -> Unit +) { + FlipperDialog( + titleId = R.string.remotecontrols_dialog_flipper_not_connected_title, + textId = R.string.remotecontrols_dialog_flipper_not_connected_desc, + buttonTextId = R.string.remotecontrols_dialog_flipper_not_connected_btn, + onClickButton = { + onOpenDeviceTab() + onDismiss() + }, + imageId = DesignSystem.drawable.ic_flipper_upload_failed, + onDismissRequest = onDismiss + ) +} diff --git a/components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/api/dialog/composable/ComposableFlipperNotSupportedDialog.kt b/components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/api/dialog/composable/ComposableFlipperNotSupportedDialog.kt new file mode 100644 index 0000000000..03e55ed97f --- /dev/null +++ b/components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/api/dialog/composable/ComposableFlipperNotSupportedDialog.kt @@ -0,0 +1,24 @@ +package com.flipperdevices.remotecontrols.impl.setup.api.dialog.composable + +import androidx.compose.runtime.Composable +import com.flipperdevices.core.ui.dialog.composable.FlipperDialog +import com.flipperdevices.remotecontrols.setup.impl.R +import com.flipperdevices.core.ui.res.R as DesignSystem + +@Composable +fun ComposableFlipperNotSupportedDialog( + onDismiss: () -> Unit, + onOpenDeviceTab: () -> Unit +) { + FlipperDialog( + titleId = R.string.remotecontrols_dialog_not_supported_flipper_title, + textId = R.string.remotecontrols_dialog_not_supported_flipper_desc, + buttonTextId = R.string.remotecontrols_dialog_not_supported_flipper_btn, + onDismissRequest = onDismiss, + onClickButton = { + onOpenDeviceTab() + onDismiss() + }, + imageId = DesignSystem.drawable.ic_firmware_flipper_deprecated + ) +} diff --git a/components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/api/dialog/composable/SetupFlipperDialogComposable.kt b/components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/api/dialog/composable/SetupFlipperDialogComposable.kt new file mode 100644 index 0000000000..02be87b6c3 --- /dev/null +++ b/components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/api/dialog/composable/SetupFlipperDialogComposable.kt @@ -0,0 +1,43 @@ +package com.flipperdevices.remotecontrols.impl.setup.api.dialog.composable + +import androidx.compose.runtime.Composable +import com.flipperdevices.core.ui.dialog.composable.busy.ComposableFlipperBusy +import com.flipperdevices.remotecontrols.api.FlipperDispatchDialogApi.DialogType +import com.flipperdevices.rootscreen.api.LocalRootNavigation +import com.flipperdevices.rootscreen.model.RootScreenConfig + +@Composable +fun SetupFlipperDialogComposable( + flipperDialog: DialogType?, + onDismiss: () -> Unit, + openDeviceTab: () -> Unit +) { + val rootNavigation = LocalRootNavigation.current + when (flipperDialog) { + DialogType.FLIPPER_IS_BUSY -> { + ComposableFlipperBusy( + onDismiss = onDismiss, + goToRemote = { + onDismiss.invoke() + rootNavigation.push(RootScreenConfig.ScreenStreaming) + } + ) + } + + DialogType.FLIPPER_NOT_CONNECTED -> { + ComposableFlipperNotConnectedDialog( + onDismiss = onDismiss, + onOpenDeviceTab = openDeviceTab + ) + } + + DialogType.FLIPPER_NOT_SUPPORTED -> { + ComposableFlipperNotSupportedDialog( + onDismiss = onDismiss, + onOpenDeviceTab = openDeviceTab + ) + } + + null -> Unit + } +} diff --git a/components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/composable/SetupScreen.kt b/components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/composable/SetupScreen.kt index a76fe9771b..352f10c39d 100644 --- a/components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/composable/SetupScreen.kt +++ b/components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/composable/SetupScreen.kt @@ -15,10 +15,10 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource -import com.flipperdevices.core.ui.dialog.composable.busy.ComposableFlipperBusy import com.flipperdevices.core.ui.theme.LocalPalletV2 import com.flipperdevices.ifrmvp.core.ui.layout.shared.ErrorComposable import com.flipperdevices.ifrmvp.core.ui.layout.shared.SharedTopBar +import com.flipperdevices.remotecontrols.api.FlipperDispatchDialogApi import com.flipperdevices.remotecontrols.impl.setup.composable.components.AnimatedConfirmContent import com.flipperdevices.remotecontrols.impl.setup.composable.components.LoadedContent import com.flipperdevices.remotecontrols.impl.setup.composable.components.SetupLoadingContent @@ -40,6 +40,7 @@ private val SetupComponent.Model.key: Any @Composable fun SetupScreen( setupComponent: SetupComponent, + flipperDispatchDialogApi: FlipperDispatchDialogApi, modifier: Modifier = Modifier ) { val rootNavigation = LocalRootNavigation.current @@ -81,15 +82,10 @@ fun SetupScreen( } is SetupComponent.Model.Loaded -> { - if (model.isFlipperBusy) { - ComposableFlipperBusy( - onDismiss = setupComponent::dismissBusyDialog, - goToRemote = { - setupComponent.dismissBusyDialog() - rootNavigation.push(RootScreenConfig.ScreenStreaming) - } - ) - } + flipperDispatchDialogApi.Render( + dialogType = model.flipperDialog, + onDismiss = setupComponent::dismissDialog + ) LoadedContent( model = model, modifier = Modifier.padding(scaffoldPaddings), diff --git a/components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/composable/components/ConfirmContent.kt b/components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/composable/components/ConfirmContent.kt index c4a9a12328..2fdd5d85d3 100644 --- a/components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/composable/components/ConfirmContent.kt +++ b/components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/composable/components/ConfirmContent.kt @@ -16,7 +16,6 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.CircleShape @@ -134,8 +133,7 @@ fun AnimatedConfirmContent( exit = slideOutVertically(targetOffsetY = { it / 2 }) + fadeOut(), modifier = Modifier .fillMaxWidth() - .align(Alignment.BottomCenter) - .navigationBarsPadding(), + .align(Alignment.BottomCenter), ) { val contentState = when (this.transition.targetState) { EnterExitState.Visible -> transition.targetState diff --git a/components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/presentation/decompose/SetupComponent.kt b/components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/presentation/decompose/SetupComponent.kt index 01b0503fda..e5903509a2 100644 --- a/components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/presentation/decompose/SetupComponent.kt +++ b/components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/presentation/decompose/SetupComponent.kt @@ -6,6 +6,7 @@ import com.flipperdevices.ifrmvp.backend.model.SignalResponse import com.flipperdevices.ifrmvp.backend.model.SignalResponseModel import com.flipperdevices.ifrmvp.model.IfrKeyIdentifier import com.flipperdevices.infrared.api.InfraredConnectionApi.InfraredEmulateState +import com.flipperdevices.remotecontrols.api.FlipperDispatchDialogApi import com.flipperdevices.remotecontrols.api.SetupScreenDecomposeComponent import com.flipperdevices.ui.decompose.DecomposeOnBackParameter import kotlinx.coroutines.CoroutineScope @@ -26,7 +27,7 @@ interface SetupComponent { fun onSkipClicked() fun dispatchSignal() - fun dismissBusyDialog() + fun dismissDialog() fun tryLoad() @@ -36,7 +37,7 @@ interface SetupComponent { data class Loading(val progress: Float) : Model data class Loaded( val response: SignalResponseModel, - val isFlipperBusy: Boolean = false, + val flipperDialog: FlipperDispatchDialogApi.DialogType? = null, val emulatedKeyIdentifier: IfrKeyIdentifier?, val connectionState: InfraredEmulateState ) : Model { diff --git a/components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/presentation/decompose/internal/SetupComponentImpl.kt b/components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/presentation/decompose/internal/SetupComponentImpl.kt index fa375b59dc..1ab37962f4 100644 --- a/components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/presentation/decompose/internal/SetupComponentImpl.kt +++ b/components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/presentation/decompose/internal/SetupComponentImpl.kt @@ -14,6 +14,7 @@ import com.flipperdevices.ifrmvp.model.IfrKeyIdentifier import com.flipperdevices.ifrmvp.model.buttondata.SingleKeyButtonData import com.flipperdevices.keyemulate.model.EmulateConfig import com.flipperdevices.remotecontrols.api.DispatchSignalApi +import com.flipperdevices.remotecontrols.api.FlipperDispatchDialogApi.Companion.toDialogType import com.flipperdevices.remotecontrols.api.SaveTempSignalApi import com.flipperdevices.remotecontrols.api.SetupScreenDecomposeComponent import com.flipperdevices.remotecontrols.impl.setup.presentation.decompose.SetupComponent @@ -110,7 +111,7 @@ class SetupComponentImpl @AssistedInject constructor( SaveTempSignalApi.State.Uploaded, SaveTempSignalApi.State.Pending -> SetupComponent.Model.Loaded( response = signalState.response, - isFlipperBusy = dispatchState is DispatchSignalApi.State.FlipperIsBusy, + flipperDialog = dispatchState.toDialogType(), emulatedKeyIdentifier = emulatingState?.ifrKeyIdentifier, connectionState = connectionState ) @@ -129,7 +130,7 @@ class SetupComponentImpl @AssistedInject constructor( .filterIsInstance() .mapNotNull { it.response.ifrFileModel } - override fun dismissBusyDialog() { + override fun dismissDialog() { dispatchSignalApi.dismissBusyDialog() } diff --git a/components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/presentation/decompose/internal/SetupScreenDecomposeComponentImpl.kt b/components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/presentation/decompose/internal/SetupScreenDecomposeComponentImpl.kt index 9ac4b68efb..847f148dbc 100644 --- a/components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/presentation/decompose/internal/SetupScreenDecomposeComponentImpl.kt +++ b/components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/presentation/decompose/internal/SetupScreenDecomposeComponentImpl.kt @@ -4,6 +4,7 @@ import androidx.compose.runtime.Composable import com.arkivanov.decompose.ComponentContext import com.arkivanov.decompose.childContext import com.flipperdevices.core.di.AppGraph +import com.flipperdevices.remotecontrols.api.FlipperDispatchDialogApi import com.flipperdevices.remotecontrols.api.SetupScreenDecomposeComponent import com.flipperdevices.remotecontrols.impl.setup.composable.SetupScreen import com.flipperdevices.remotecontrols.impl.setup.presentation.decompose.SetupComponent @@ -18,6 +19,7 @@ class SetupScreenDecomposeComponentImpl @AssistedInject constructor( @Assisted onBack: () -> Unit, @Assisted onIrFileReady: (id: Long) -> Unit, setupComponentFactory: SetupComponent.Factory, + flipperDispatchDialogApiFactory: FlipperDispatchDialogApi.Factory, ) : SetupScreenDecomposeComponent(componentContext) { private val setupComponent = setupComponentFactory.createSetupComponent( componentContext = childContext("SetupComponent"), @@ -25,9 +27,13 @@ class SetupScreenDecomposeComponentImpl @AssistedInject constructor( onBack = onBack, onIrFileReady = onIrFileReady ) + private val flipperDispatchDialogApi = flipperDispatchDialogApiFactory.invoke(onBack = onBack) @Composable override fun Render() { - SetupScreen(setupComponent = setupComponent) + SetupScreen( + setupComponent = setupComponent, + flipperDispatchDialogApi = flipperDispatchDialogApi + ) } } diff --git a/components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/presentation/viewmodel/DispatchSignalViewModel.kt b/components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/presentation/viewmodel/DispatchSignalViewModel.kt index fdb33ceaaf..87ec61c020 100644 --- a/components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/presentation/viewmodel/DispatchSignalViewModel.kt +++ b/components/remote-controls/setup/impl/src/main/kotlin/com/flipperdevices/remotecontrols/impl/setup/presentation/viewmodel/DispatchSignalViewModel.kt @@ -10,6 +10,8 @@ import com.flipperdevices.core.log.LogTagProvider import com.flipperdevices.core.log.error import com.flipperdevices.core.log.info import com.flipperdevices.core.ui.lifecycle.DecomposeViewModel +import com.flipperdevices.faphub.target.api.FlipperTargetProviderApi +import com.flipperdevices.faphub.target.model.FlipperTarget import com.flipperdevices.ifrmvp.model.IfrKeyIdentifier import com.flipperdevices.infrared.editor.core.model.InfraredRemote import com.flipperdevices.keyemulate.api.EmulateHelper @@ -27,6 +29,7 @@ import kotlinx.coroutines.cancelAndJoin import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import javax.inject.Inject @@ -35,6 +38,7 @@ class DispatchSignalViewModel @Inject constructor( private val emulateHelper: EmulateHelper, private val serviceProvider: FlipperServiceProvider, private val closeEmulateAppTaskHolder: CloseEmulateAppTaskHolder, + private val flipperTargetProviderApi: FlipperTargetProviderApi, ) : DecomposeViewModel(), FlipperBleServiceConsumer, LogTagProvider, @@ -59,6 +63,19 @@ class DispatchSignalViewModel @Inject constructor( ffPath: FlipperFilePath, onDispatched: () -> Unit ) { + when (flipperTargetProviderApi.getFlipperTarget().value) { + FlipperTarget.NotConnected -> { + _state.update { DispatchSignalApi.State.FlipperNotConnected } + return + } + + FlipperTarget.Unsupported -> { + _state.update { DispatchSignalApi.State.FlipperNotSupported } + return + } + + else -> Unit + } val i = remotes.indexOfFirst { remote -> when (identifier) { is IfrKeyIdentifier.Name -> { diff --git a/components/remote-controls/setup/impl/src/main/res/values/strings.xml b/components/remote-controls/setup/impl/src/main/res/values/strings.xml index 3d8c825873..97bf260fb7 100644 --- a/components/remote-controls/setup/impl/src/main/res/values/strings.xml +++ b/components/remote-controls/setup/impl/src/main/res/values/strings.xml @@ -8,4 +8,10 @@ Point Flipper Zero at the %s and tap the button Set Up Remote Add remote + Flipper Not Connected + Connect your Flipper Zero to add remote + Go to Connection + Flipper Firmware Not Supported + This feature requires the latest Flipper firmware version + Go to Firmware Update \ No newline at end of file diff --git a/components/rootscreen/api/src/main/kotlin/com/flipperdevices/rootscreen/model/RootScreenConfig.kt b/components/rootscreen/api/src/main/kotlin/com/flipperdevices/rootscreen/model/RootScreenConfig.kt index 5e37d4c005..6f2956b173 100644 --- a/components/rootscreen/api/src/main/kotlin/com/flipperdevices/rootscreen/model/RootScreenConfig.kt +++ b/components/rootscreen/api/src/main/kotlin/com/flipperdevices/rootscreen/model/RootScreenConfig.kt @@ -35,9 +35,6 @@ sealed class RootScreenConfig { @Serializable data class Changelog(val updateRequest: UpdateRequest) : RootScreenConfig() - @Serializable - data object RemoteControls : RootScreenConfig() - @Serializable data class ServerRemoteControl( val infraredFileId: Long, diff --git a/components/rootscreen/impl/src/main/kotlin/com/flipperdevices/rootscreen/impl/api/RootDecomposeComponentImpl.kt b/components/rootscreen/impl/src/main/kotlin/com/flipperdevices/rootscreen/impl/api/RootDecomposeComponentImpl.kt index 8282859f28..1b32d7f894 100644 --- a/components/rootscreen/impl/src/main/kotlin/com/flipperdevices/rootscreen/impl/api/RootDecomposeComponentImpl.kt +++ b/components/rootscreen/impl/src/main/kotlin/com/flipperdevices/rootscreen/impl/api/RootDecomposeComponentImpl.kt @@ -23,7 +23,6 @@ import com.flipperdevices.firstpair.api.FirstPairApi import com.flipperdevices.firstpair.api.FirstPairDecomposeComponent import com.flipperdevices.keyscreen.api.KeyScreenDecomposeComponent import com.flipperdevices.remotecontrols.api.ConfigureGridDecomposeComponent -import com.flipperdevices.remotecontrols.api.RemoteControlsScreenDecomposeComponent import com.flipperdevices.remotecontrols.api.model.ServerRemoteControlParam import com.flipperdevices.rootscreen.api.RootDecomposeComponent import com.flipperdevices.rootscreen.impl.deeplink.RootDeeplinkHandler @@ -59,7 +58,6 @@ class RootDecomposeComponentImpl @AssistedInject constructor( private val keyScreenFactory: KeyScreenDecomposeComponent.Factory, private val screenshotsPreviewFactory: ScreenshotsPreviewDecomposeComponent.Factory, private val changelogScreenDecomposeFactory: ChangelogScreenDecomposeComponent.Factory, - private val remoteControlsComponentFactory: RemoteControlsScreenDecomposeComponent.Factory, private val serverRemoteControlFactory: ConfigureGridDecomposeComponent.Factory ) : RootDecomposeComponent, ComponentContext by componentContext { private val scope = coroutineScope(FlipperDispatchers.workStealingDispatcher) @@ -134,11 +132,6 @@ class RootDecomposeComponentImpl @AssistedInject constructor( onBack = this::internalOnBack ) - is RootScreenConfig.RemoteControls -> remoteControlsComponentFactory( - componentContext = componentContext, - onBack = this::internalOnBack - ) - is RootScreenConfig.ServerRemoteControl -> serverRemoteControlFactory( componentContext = componentContext, param = ServerRemoteControlParam(