Skip to content

Commit

Permalink
Remote controls design fixes (#922)
Browse files Browse the repository at this point in the history
**Background**

This PR introduces fixes for design part of RemoteControls

**Changes**

- Title/Subtitle on remote screen
- Connection API for infrareds
- Progress loading on remote screen

**Test plan**

- Open setup screen, find remote
- See the progress now is shown on remote screen
  • Loading branch information
makeevrserg authored Aug 21, 2024
1 parent 45b8202 commit fcce80f
Show file tree
Hide file tree
Showing 64 changed files with 687 additions and 328 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ Attention: don't forget to add the flag for F-Droid before release
- [FIX] Use kotlin 2.0.20-RC2 for bypass Options screen crash
- [FIX] Relocate remote-controls button to infrared remotes screen
- [FIX] Fap manifest caching
- [FIX] Remote controls design issues
- [CI] Fix merge-queue files diff
- [CI] Add https://github.com/LionZXY/detekt-decompose-rule
- [CI] Enabling detekt module for android and kmp modules
Expand Down
3 changes: 3 additions & 0 deletions components/infrared/api/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ android.namespace = "com.flipperdevices.infrared.api"
dependencies {
implementation(projects.components.core.ui.decompose)
implementation(projects.components.bridge.dao.api)
implementation(projects.components.bridge.service.api)

implementation(libs.kotlin.coroutines)

implementation(libs.decompose)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.flipperdevices.infrared.api

import com.flipperdevices.bridge.service.api.FlipperServiceApi
import kotlinx.coroutines.flow.Flow

interface InfraredConnectionApi {
fun getState(serviceApi: FlipperServiceApi): Flow<InfraredEmulateState>

enum class InfraredEmulateState {
NOT_CONNECTED, CONNECTING, SYNCING, UPDATE_FLIPPER, ALL_GOOD
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.flipperdevices.infrared.impl.api

import com.flipperdevices.bridge.api.manager.ktx.state.ConnectionState
import com.flipperdevices.bridge.api.manager.ktx.state.FlipperSupportedState
import com.flipperdevices.bridge.api.utils.Constants.API_SUPPORTED_INFRARED_EMULATE
import com.flipperdevices.bridge.service.api.FlipperServiceApi
import com.flipperdevices.bridge.synchronization.api.SynchronizationApi
import com.flipperdevices.bridge.synchronization.api.SynchronizationState
import com.flipperdevices.core.di.AppGraph
import com.flipperdevices.core.log.info
import com.flipperdevices.infrared.api.InfraredConnectionApi
import com.squareup.anvil.annotations.ContributesBinding
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import javax.inject.Inject

@ContributesBinding(AppGraph::class, InfraredConnectionApi::class)
class InfraredConnectionApiImpl @Inject constructor(
private val synchronizationApi: SynchronizationApi
) : InfraredConnectionApi {
override fun getState(
serviceApi: FlipperServiceApi
): Flow<InfraredConnectionApi.InfraredEmulateState> {
return combine(
serviceApi.flipperVersionApi.getVersionInformationFlow(),
serviceApi.connectionInformationApi.getConnectionStateFlow(),
synchronizationApi.getSynchronizationState()
) { versionInformation, connectionState, synchronizationState ->
val infraredEmulateState = when {
connectionState is ConnectionState.Disconnected -> {
InfraredConnectionApi.InfraredEmulateState.NOT_CONNECTED
}

connectionState !is ConnectionState.Ready ||
connectionState.supportedState != FlipperSupportedState.READY -> {
InfraredConnectionApi.InfraredEmulateState.CONNECTING
}

synchronizationState is SynchronizationState.InProgress -> {
InfraredConnectionApi.InfraredEmulateState.SYNCING
}

versionInformation == null || versionInformation < API_SUPPORTED_INFRARED_EMULATE -> {
InfraredConnectionApi.InfraredEmulateState.UPDATE_FLIPPER
}

else -> {
InfraredConnectionApi.InfraredEmulateState.ALL_GOOD
}
}
info { "#onServiceApiReady $infraredEmulateState" }
infraredEmulateState
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.arkivanov.decompose.ComponentContext
import com.flipperdevices.bridge.dao.api.model.FlipperKeyPath
import com.flipperdevices.infrared.api.InfraredConnectionApi.InfraredEmulateState
import com.flipperdevices.infrared.impl.R
import com.flipperdevices.infrared.impl.composable.components.ComposableIconText
import com.flipperdevices.infrared.impl.composable.components.ComposableInfraredAppBar
import com.flipperdevices.infrared.impl.viewmodel.InfraredEmulateState
import com.flipperdevices.infrared.impl.viewmodel.InfraredViewModel
import com.flipperdevices.keyemulate.api.KeyEmulateApi
import com.flipperdevices.keyemulate.api.KeyEmulateUiApi
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.flipperdevices.infrared.api.InfraredConnectionApi.InfraredEmulateState
import com.flipperdevices.infrared.impl.composable.components.ComposableFadedInfraredRemotes
import com.flipperdevices.infrared.impl.composable.components.ComposableInfraredRemotes
import com.flipperdevices.infrared.impl.viewmodel.InfraredEmulateState
import com.flipperdevices.keyemulate.model.EmulateConfig
import com.flipperdevices.keyscreen.model.KeyScreenState

Expand Down
Original file line number Diff line number Diff line change
@@ -1,32 +1,28 @@
package com.flipperdevices.infrared.impl.viewmodel

import com.flipperdevices.bridge.api.manager.ktx.state.ConnectionState
import com.flipperdevices.bridge.api.manager.ktx.state.FlipperSupportedState
import com.flipperdevices.bridge.api.utils.Constants.API_SUPPORTED_INFRARED_EMULATE
import com.flipperdevices.bridge.dao.api.model.FlipperKeyPath
import com.flipperdevices.bridge.service.api.FlipperServiceApi
import com.flipperdevices.bridge.service.api.provider.FlipperBleServiceConsumer
import com.flipperdevices.bridge.service.api.provider.FlipperServiceProvider
import com.flipperdevices.bridge.synchronization.api.SynchronizationApi
import com.flipperdevices.bridge.synchronization.api.SynchronizationState
import com.flipperdevices.core.log.LogTagProvider
import com.flipperdevices.core.log.info
import com.flipperdevices.core.ui.lifecycle.DecomposeViewModel
import com.flipperdevices.infrared.api.InfraredConnectionApi
import com.flipperdevices.infrared.api.InfraredConnectionApi.InfraredEmulateState
import com.flipperdevices.keyscreen.api.KeyStateHelperApi
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach

class InfraredViewModel @AssistedInject constructor(
@Assisted val keyPath: FlipperKeyPath, // For get value to bottom sheet
keyStateHelperApi: KeyStateHelperApi.Builder,
private val synchronizationApi: SynchronizationApi,
serviceProvider: FlipperServiceProvider
serviceProvider: FlipperServiceProvider,
private val infraredConnectionApi: InfraredConnectionApi
) : DecomposeViewModel(), FlipperBleServiceConsumer, LogTagProvider {
override val TAG: String = "InfraredViewModel"

Expand All @@ -45,31 +41,12 @@ class InfraredViewModel @AssistedInject constructor(
fun onRename(onEndAction: (FlipperKeyPath) -> Unit) = keyStateHelper.onOpenEdit(onEndAction)

fun onDelete(onEndAction: () -> Unit) = keyStateHelper.onDelete(onEndAction)

override fun onServiceApiReady(serviceApi: FlipperServiceApi) {
combine(
serviceApi.flipperVersionApi.getVersionInformationFlow(),
serviceApi.connectionInformationApi.getConnectionStateFlow(),
synchronizationApi.getSynchronizationState()
) { versionInformation, connectionState, synchronizationState ->
return@combine if (connectionState is ConnectionState.Disconnected) {
InfraredEmulateState.NOT_CONNECTED
} else if (connectionState !is ConnectionState.Ready ||
connectionState.supportedState != FlipperSupportedState.READY
) {
InfraredEmulateState.CONNECTING
} else if (synchronizationState is SynchronizationState.InProgress) {
InfraredEmulateState.SYNCING
} else if (versionInformation == null ||
versionInformation < API_SUPPORTED_INFRARED_EMULATE
) {
InfraredEmulateState.UPDATE_FLIPPER
} else {
InfraredEmulateState.ALL_GOOD
}
}.onEach {
info { "#onServiceApiReady $it" }
emulateStateFlow.emit(it)
}.launchIn(viewModelScope)
infraredConnectionApi.getState(serviceApi)
.onEach { info { "#onServiceApiReady $it" } }
.onEach { emulateStateFlow.emit(it) }
.launchIn(viewModelScope)
}

@AssistedFactory
Expand All @@ -79,7 +56,3 @@ class InfraredViewModel @AssistedInject constructor(
): InfraredViewModel
}
}

enum class InfraredEmulateState {
NOT_CONNECTED, CONNECTING, SYNCING, UPDATE_FLIPPER, ALL_GOOD
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ abstract class BrandsScreenDecomposeComponent(
componentContext: ComponentContext,
categoryId: Long,
onBackClick: () -> Unit,
onBrandClick: (brandId: Long) -> Unit
onBrandClick: (brandId: Long, brandName: String) -> Unit
): BrandsScreenDecomposeComponent
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ interface BrandsDecomposeComponent {
componentContext: ComponentContext,
categoryId: Long,
onBackClick: DecomposeOnBackParameter,
onBrandClick: (brandId: Long) -> Unit
onBrandClick: (brandId: Long, brandName: String) -> Unit
): BrandsDecomposeComponent
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import javax.inject.Provider
class BrandsDecomposeComponentImpl @AssistedInject constructor(
@Assisted componentContext: ComponentContext,
@Assisted private val onBackClick: DecomposeOnBackParameter,
@Assisted private val onBrandClick: (brandId: Long) -> Unit,
@Assisted private val onBrandClick: (brandId: Long, brandName: String) -> Unit,
@Assisted categoryId: Long,
createBrandsListViewModel: BrandsListViewModel.Factory,
createQueryViewModel: Provider<QueryViewModel>
Expand Down Expand Up @@ -82,7 +82,7 @@ class BrandsDecomposeComponentImpl @AssistedInject constructor(
}

override fun onBrandClick(brandModel: BrandModel) {
onBrandClick.invoke(brandModel.id)
onBrandClick.invoke(brandModel.id, brandModel.name)
}

override fun tryLoad() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class BrandsScreenDecomposeComponentImpl @AssistedInject constructor(
@Assisted componentContext: ComponentContext,
@Assisted categoryId: Long,
@Assisted onBackClick: () -> Unit,
@Assisted onBrandClick: (brandId: Long) -> Unit,
@Assisted onBrandClick: (brandId: Long, brandName: String) -> Unit,
brandsDecomposeComponentFactory: BrandsDecomposeComponent.Factory,
) : BrandsScreenDecomposeComponent(componentContext) {
private val brandsComponent = brandsDecomposeComponentFactory.createBrandsComponent(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ abstract class CategoriesScreenDecomposeComponent(
operator fun invoke(
componentContext: ComponentContext,
onBackClick: () -> Unit,
onCategoryClick: (categoryId: Long) -> Unit
onCategoryClick: (categoryId: Long, categoryName: String) -> Unit
): CategoriesScreenDecomposeComponent
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ interface DeviceCategoriesComponent {
fun invoke(
componentContext: ComponentContext,
onBackClick: DecomposeOnBackParameter,
onCategoryClick: (categoryId: Long) -> Unit
onCategoryClick: (categoryId: Long, categoryName: String) -> Unit
): DeviceCategoriesComponent
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import me.gulya.anvil.assisted.ContributesAssistedFactory
class CategoriesScreenDecomposeComponentImpl @AssistedInject constructor(
@Assisted componentContext: ComponentContext,
@Assisted onBackClick: () -> Unit,
@Assisted onCategoryClick: (categoryId: Long) -> Unit,
@Assisted onCategoryClick: (categoryId: Long, categoryName: String) -> Unit,
deviceCategoriesComponentFactory: DeviceCategoriesComponent.Factory,
) : CategoriesScreenDecomposeComponent(componentContext) {
private val deviceCategoriesComponent = deviceCategoriesComponentFactory.invoke(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import javax.inject.Provider
class DeviceCategoriesComponentImpl @AssistedInject constructor(
@Assisted componentContext: ComponentContext,
@Assisted private val onBackClick: DecomposeOnBackParameter,
@Assisted private val onCategoryClick: (categoryId: Long) -> Unit,
@Assisted private val onCategoryClick: (categoryId: Long, categoryName: String) -> Unit,
createDeviceCategoryListViewModel: Provider<DeviceCategoryListViewModel>,
) : DeviceCategoriesComponent,
ComponentContext by componentContext {
Expand All @@ -27,7 +27,10 @@ class DeviceCategoriesComponentImpl @AssistedInject constructor(
override val model = deviceCategoryListFeature.model

override fun onCategoryClick(category: DeviceCategory) {
onCategoryClick(category.id)
onCategoryClick(
category.id,
category.meta.manifest.singularDisplayName
)
}

override fun onBackClick() = onBackClick.invoke()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,14 @@ fun ButtonItemComposable(
emulatedKeyIdentifier: IfrKeyIdentifier?,
onKeyDataClick: (IfrKeyIdentifier) -> Unit,
isSyncing: Boolean,
isConnected: Boolean,
modifier: Modifier = Modifier
) {
when (buttonData) {
is IconButtonData -> {
ButtonPlaceholderComposition(
isSyncing = isSyncing,
isConnected = isConnected,
isEmulating = emulatedKeyIdentifier == buttonData.keyIdentifier,
content = {
SquareIconButton(
Expand All @@ -43,6 +45,7 @@ fun ButtonItemComposable(
is ChannelButtonData -> {
ButtonPlaceholderComposition(
isSyncing = isSyncing,
isConnected = isConnected,
isEmulating = buttonData.reduceKeyIdentifier == emulatedKeyIdentifier ||
buttonData.addKeyIdentifier == emulatedKeyIdentifier,
content = {
Expand All @@ -58,6 +61,7 @@ fun ButtonItemComposable(
is VolumeButtonData -> {
ButtonPlaceholderComposition(
isSyncing = isSyncing,
isConnected = isConnected,
isEmulating = buttonData.reduceKeyIdentifier == emulatedKeyIdentifier ||
buttonData.addKeyIdentifier == emulatedKeyIdentifier,
content = {
Expand All @@ -73,6 +77,7 @@ fun ButtonItemComposable(
is NavigationButtonData -> {
ButtonPlaceholderComposition(
isSyncing = isSyncing,
isConnected = isConnected,
isEmulating = listOf(
buttonData.okKeyIdentifier,
buttonData.upKeyIdentifier,
Expand All @@ -96,6 +101,7 @@ fun ButtonItemComposable(
is TextButtonData -> {
ButtonPlaceholderComposition(
isSyncing = isSyncing,
isConnected = isConnected,
isEmulating = emulatedKeyIdentifier == buttonData.keyIdentifier,
content = {
TextButton(
Expand All @@ -111,6 +117,7 @@ fun ButtonItemComposable(
is Base64ImageButtonData -> {
ButtonPlaceholderComposition(
isSyncing = isSyncing,
isConnected = isConnected,
isEmulating = emulatedKeyIdentifier == buttonData.keyIdentifier,
content = {
Base64ImageButton(
Expand All @@ -125,6 +132,7 @@ fun ButtonItemComposable(
UnknownButtonData -> {
ButtonPlaceholderComposition(
isSyncing = isSyncing,
isConnected = isConnected,
isEmulating = false,
content = {
UnknownButton(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import com.flipperdevices.core.ui.theme.LocalPalletV2
import com.flipperdevices.ifrmvp.core.ui.button.core.LocalButtonPlaceholder
import com.flipperdevices.ifrmvp.core.ui.button.core.SyncingBox
import com.flipperdevices.ifrmvp.core.ui.button.core.TextButton
import com.flipperdevices.ifrmvp.core.ui.layout.core.sf
Expand Down Expand Up @@ -47,7 +46,7 @@ fun DoubleButton(
text = lastText,
background = LocalPalletV2.current.surface.menu.body.dufault,
)
SyncingBox(LocalButtonPlaceholder.current.isSyncing)
SyncingBox()
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.rememberVectorPainter
import com.flipperdevices.core.ui.theme.LocalPalletV2
import com.flipperdevices.ifrmvp.core.ui.button.core.LocalButtonPlaceholder
import com.flipperdevices.ifrmvp.core.ui.button.core.NoConnectionBox
import com.flipperdevices.ifrmvp.core.ui.button.core.SyncingBox
import com.flipperdevices.ifrmvp.core.ui.layout.core.sf
import com.flipperdevices.ifrmvp.core.ui.layout.core.sfp
Expand Down Expand Up @@ -113,6 +113,7 @@ fun NavigationButton(
.clickable(onClick = onDownClick)
.align(Alignment.BottomCenter)
)
SyncingBox(isSyncing = LocalButtonPlaceholder.current.isSyncing)
SyncingBox()
NoConnectionBox()
}
}
Loading

0 comments on commit fcce80f

Please sign in to comment.