diff --git a/CHANGELOG.md b/CHANGELOG.md index 67803f49e1..ae3fcbcdd2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - [Refactor] Migrate file manager to decompose - [Refactor] Migrate options screen to decompose +- [Refactor] Migrate device screen to decompose - [Feature] New report bug # 1.6.7 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 c949a17659..0edd3dd089 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 @@ -1,12 +1,14 @@ package com.flipperdevices.deeplink.model import android.os.Parcelable +import androidx.compose.runtime.Immutable import com.flipperdevices.bridge.dao.api.model.FlipperFilePath import com.flipperdevices.bridge.dao.api.model.FlipperKeyPath import kotlinx.parcelize.Parcelize import kotlinx.serialization.Serializable @Serializable +@Immutable sealed class Deeplink : Parcelable { @Parcelize @Serializable diff --git a/components/info/api/build.gradle.kts b/components/info/api/build.gradle.kts index ed7c152c21..28accef624 100644 --- a/components/info/api/build.gradle.kts +++ b/components/info/api/build.gradle.kts @@ -8,4 +8,7 @@ dependencies { implementation(projects.components.deeplink.api) implementation(projects.components.core.ui.navigation) + implementation(projects.components.core.ui.decompose) + + implementation(libs.decompose) } diff --git a/components/info/api/src/main/java/com/flipperdevices/info/api/screen/DeviceScreenDecomposeComponent.kt b/components/info/api/src/main/java/com/flipperdevices/info/api/screen/DeviceScreenDecomposeComponent.kt new file mode 100644 index 0000000000..8dd5da1e49 --- /dev/null +++ b/components/info/api/src/main/java/com/flipperdevices/info/api/screen/DeviceScreenDecomposeComponent.kt @@ -0,0 +1,12 @@ +package com.flipperdevices.info.api.screen + +import com.arkivanov.decompose.ComponentContext +import com.flipperdevices.ui.decompose.DecomposeComponent + +interface DeviceScreenDecomposeComponent : DecomposeComponent { + fun interface Factory { + operator fun invoke( + componentContext: ComponentContext, + ): DeviceScreenDecomposeComponent + } +} diff --git a/components/info/api/src/main/java/com/flipperdevices/info/api/screen/InfoFeatureEntry.kt b/components/info/api/src/main/java/com/flipperdevices/info/api/screen/InfoFeatureEntry.kt index 75e87a0126..c0c1a10165 100644 --- a/components/info/api/src/main/java/com/flipperdevices/info/api/screen/InfoFeatureEntry.kt +++ b/components/info/api/src/main/java/com/flipperdevices/info/api/screen/InfoFeatureEntry.kt @@ -2,13 +2,8 @@ package com.flipperdevices.info.api.screen import com.flipperdevices.core.ui.navigation.AggregateFeatureEntry import com.flipperdevices.core.ui.navigation.FeatureScreenRootRoute -import com.flipperdevices.deeplink.model.Deeplink interface InfoFeatureEntry : AggregateFeatureEntry { override val ROUTE: FeatureScreenRootRoute get() = FeatureScreenRootRoute.DEVICE_INFO - - fun fullInfo(): String - - fun getWebUpdateByDeeplink(deeplink: Deeplink): String } diff --git a/components/info/impl/build.gradle.kts b/components/info/impl/build.gradle.kts index bf2c5a45bc..cc1f455ebd 100644 --- a/components/info/impl/build.gradle.kts +++ b/components/info/impl/build.gradle.kts @@ -18,6 +18,7 @@ dependencies { implementation(projects.components.core.ui.res) implementation(projects.components.core.ui.ktx) implementation(projects.components.core.ui.navigation) + implementation(projects.components.core.ui.decompose) implementation(projects.components.core.ui.lifecycle) implementation(projects.components.core.ui.theme) implementation(projects.components.core.ui.dialog) @@ -46,15 +47,12 @@ dependencies { implementation(libs.appcompat) - implementation(libs.tangle.viewmodel.compose) - implementation(libs.tangle.viewmodel.api) - anvil(libs.tangle.viewmodel.compiler) - implementation(libs.compose.ui) implementation(libs.compose.material) implementation(libs.compose.tooling) implementation(libs.compose.foundation) implementation(libs.compose.navigation) + implementation(libs.bundles.decompose) implementation(libs.kotlin.serialization.json) implementation(libs.kotlin.coroutines) diff --git a/components/info/impl/src/main/java/com/flipperdevices/info/impl/api/DeviceScreenDecomposeComponentImpl.kt b/components/info/impl/src/main/java/com/flipperdevices/info/impl/api/DeviceScreenDecomposeComponentImpl.kt new file mode 100644 index 0000000000..15e190f316 --- /dev/null +++ b/components/info/impl/src/main/java/com/flipperdevices/info/impl/api/DeviceScreenDecomposeComponentImpl.kt @@ -0,0 +1,76 @@ +package com.flipperdevices.info.impl.api + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import com.arkivanov.decompose.ComponentContext +import com.arkivanov.decompose.extensions.compose.stack.Children +import com.arkivanov.decompose.extensions.compose.subscribeAsState +import com.arkivanov.decompose.router.stack.ChildStack +import com.arkivanov.decompose.router.stack.StackNavigation +import com.arkivanov.decompose.router.stack.childStack +import com.arkivanov.decompose.router.stack.pop +import com.arkivanov.decompose.value.Value +import com.flipperdevices.core.di.AppGraph +import com.flipperdevices.info.api.screen.DeviceScreenDecomposeComponent +import com.flipperdevices.info.impl.model.DeviceScreenNavigationConfig +import com.flipperdevices.settings.api.SettingsDecomposeComponent +import com.flipperdevices.ui.decompose.DecomposeComponent +import com.squareup.anvil.annotations.ContributesBinding +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject + +class DeviceScreenDecomposeComponentImpl @AssistedInject constructor( + @Assisted componentContext: ComponentContext, + private val settingsFactory: SettingsDecomposeComponent.Factory, + private val updateFactory: UpdateScreenDecomposeComponent.Factory, + private val fullInfoDecomposeComponentFactory: FullInfoDecomposeComponent.Factory +) : DeviceScreenDecomposeComponent, ComponentContext by componentContext { + private val navigation = StackNavigation() + + val stack: Value> = childStack( + source = navigation, + serializer = DeviceScreenNavigationConfig.serializer(), + initialConfiguration = DeviceScreenNavigationConfig.Update(), + handleBackButton = true, + childFactory = ::child, + ) + + private fun child( + config: DeviceScreenNavigationConfig, + componentContext: ComponentContext + ): DecomposeComponent = when (config) { + is DeviceScreenNavigationConfig.Update -> updateFactory( + componentContext, + config.deeplink, + navigation + ) + + DeviceScreenNavigationConfig.FullInfo -> fullInfoDecomposeComponentFactory( + componentContext = componentContext, + onBack = navigation::pop + ) + + DeviceScreenNavigationConfig.Options -> settingsFactory(componentContext) + } + + @Composable + @Suppress("NonSkippableComposable") + override fun Render() { + val childStack by stack.subscribeAsState() + + Children( + stack = childStack, + ) { + it.instance.Render() + } + } + + @AssistedFactory + @ContributesBinding(AppGraph::class, DeviceScreenDecomposeComponent.Factory::class) + interface Factory : DeviceScreenDecomposeComponent.Factory { + override operator fun invoke( + componentContext: ComponentContext, + ): DeviceScreenDecomposeComponentImpl + } +} diff --git a/components/info/impl/src/main/java/com/flipperdevices/info/impl/api/FullInfoDecomposeComponent.kt b/components/info/impl/src/main/java/com/flipperdevices/info/impl/api/FullInfoDecomposeComponent.kt new file mode 100644 index 0000000000..571d32fbfd --- /dev/null +++ b/components/info/impl/src/main/java/com/flipperdevices/info/impl/api/FullInfoDecomposeComponent.kt @@ -0,0 +1,53 @@ +package com.flipperdevices.info.impl.api + +import androidx.compose.runtime.Composable +import com.arkivanov.decompose.ComponentContext +import com.flipperdevices.core.ui.ktx.viewModelWithFactory +import com.flipperdevices.info.impl.compose.screens.ComposableFullDeviceInfoScreen +import com.flipperdevices.info.impl.viewmodel.DeviceStatusViewModel +import com.flipperdevices.info.impl.viewmodel.deviceinfo.BasicInfoViewModel +import com.flipperdevices.info.impl.viewmodel.deviceinfo.FullInfoViewModel +import com.flipperdevices.info.impl.viewmodel.deviceinfo.ShareFullInfoFileViewModel +import com.flipperdevices.ui.decompose.DecomposeComponent +import com.flipperdevices.ui.decompose.DecomposeOnBackParameter +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import javax.inject.Provider + +class FullInfoDecomposeComponent @AssistedInject constructor( + @Assisted componentContext: ComponentContext, + @Assisted private val onBack: DecomposeOnBackParameter, + private val shareFullInfoViewModelProvider: Provider, + private val basicInfoViewModelProvider: Provider, + private val fullInfoViewModelProvider: Provider, + private val deviceStatusViewModelProvider: Provider +) : DecomposeComponent, ComponentContext by componentContext { + @Composable + @Suppress("NonSkippableComposable") + override fun Render() { + ComposableFullDeviceInfoScreen( + onBack = onBack::invoke, + shareViewModel = viewModelWithFactory(key = null) { + shareFullInfoViewModelProvider.get() + }, + basicInfoViewModel = viewModelWithFactory(key = null) { + basicInfoViewModelProvider.get() + }, + fullInfoViewModel = viewModelWithFactory(key = null) { + fullInfoViewModelProvider.get() + }, + deviceStatusViewModel = viewModelWithFactory(key = null) { + deviceStatusViewModelProvider.get() + } + ) + } + + @AssistedFactory + fun interface Factory { + operator fun invoke( + componentContext: ComponentContext, + onBack: DecomposeOnBackParameter + ): FullInfoDecomposeComponent + } +} diff --git a/components/info/impl/src/main/java/com/flipperdevices/info/impl/api/InfoDeeplinkHandler.kt b/components/info/impl/src/main/java/com/flipperdevices/info/impl/api/InfoDeeplinkHandler.kt index ea0ba62f9b..8b95362a5b 100644 --- a/components/info/impl/src/main/java/com/flipperdevices/info/impl/api/InfoDeeplinkHandler.kt +++ b/components/info/impl/src/main/java/com/flipperdevices/info/impl/api/InfoDeeplinkHandler.kt @@ -1,20 +1,17 @@ package com.flipperdevices.info.impl.api import android.content.Intent -import androidx.core.net.toUri import androidx.navigation.NavController import com.flipperdevices.bottombar.api.BottomNavigationHandleDeeplink import com.flipperdevices.core.di.AppGraph import com.flipperdevices.deeplink.api.DeepLinkHandler import com.flipperdevices.deeplink.api.DispatcherPriority import com.flipperdevices.deeplink.model.Deeplink -import com.flipperdevices.info.api.screen.InfoFeatureEntry import com.squareup.anvil.annotations.ContributesMultibinding import javax.inject.Inject @ContributesMultibinding(AppGraph::class, DeepLinkHandler::class) class InfoDeeplinkHandler @Inject constructor( - private val infoFeatureEntry: InfoFeatureEntry, private val bottomHandleDeeplink: BottomNavigationHandleDeeplink ) : DeepLinkHandler { override fun isSupportLink(link: Deeplink): DispatcherPriority? { @@ -27,7 +24,7 @@ class InfoDeeplinkHandler @Inject constructor( override fun processLink(navController: NavController, link: Deeplink) { val intent = Intent().apply { - data = infoFeatureEntry.getWebUpdateByDeeplink(link).toUri() + // data = infoFeatureEntry.getWebUpdateByDeeplink(link).toUri() } bottomHandleDeeplink.handleDeepLink(intent) } diff --git a/components/info/impl/src/main/java/com/flipperdevices/info/impl/api/InfoFeatureEntryImpl.kt b/components/info/impl/src/main/java/com/flipperdevices/info/impl/api/InfoFeatureEntryImpl.kt index 0787cb95a2..084ff8cfa1 100644 --- a/components/info/impl/src/main/java/com/flipperdevices/info/impl/api/InfoFeatureEntryImpl.kt +++ b/components/info/impl/src/main/java/com/flipperdevices/info/impl/api/InfoFeatureEntryImpl.kt @@ -1,80 +1,41 @@ package com.flipperdevices.info.impl.api -import android.net.Uri +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember import androidx.navigation.NavGraphBuilder import androidx.navigation.NavHostController import androidx.navigation.compose.composable -import androidx.navigation.navArgument -import androidx.navigation.navDeepLink import androidx.navigation.navigation +import com.arkivanov.decompose.extensions.compose.stack.Children +import com.arkivanov.decompose.extensions.compose.subscribeAsState import com.flipperdevices.core.di.AppGraph import com.flipperdevices.core.ui.navigation.AggregateFeatureEntry -import com.flipperdevices.core.ui.navigation.LocalGlobalNavigationNavStack -import com.flipperdevices.deeplink.model.Deeplink -import com.flipperdevices.deeplink.model.DeeplinkConstants -import com.flipperdevices.deeplink.model.DeeplinkNavType +import com.flipperdevices.info.api.screen.DeviceScreenDecomposeComponent import com.flipperdevices.info.api.screen.InfoFeatureEntry -import com.flipperdevices.info.impl.compose.screens.ComposableDeviceInfoScreen -import com.flipperdevices.info.impl.compose.screens.ComposableFullDeviceInfoScreen -import com.flipperdevices.settings.api.SettingsFeatureEntry -import com.flipperdevices.updater.api.UpdaterCardApi -import com.flipperdevices.updater.api.UpdaterFeatureEntry +import com.flipperdevices.ui.decompose.rememberComponentContext import com.squareup.anvil.annotations.ContributesBinding import com.squareup.anvil.annotations.ContributesMultibinding -import kotlinx.serialization.encodeToString -import kotlinx.serialization.json.Json import javax.inject.Inject -private const val DEEPLINK_KEY = DeeplinkConstants.KEY -private const val DEEPLINK_SCHEME = DeeplinkConstants.SCHEMA -private const val DEEPLINK_WEB_UPDATER_URL = "${DEEPLINK_SCHEME}web_updater={$DEEPLINK_KEY}" - @ContributesBinding(AppGraph::class, InfoFeatureEntry::class) @ContributesMultibinding(AppGraph::class, AggregateFeatureEntry::class) class InfoFeatureEntryImpl @Inject constructor( - private val updaterCardApi: UpdaterCardApi, - private val settingFeatureEntry: SettingsFeatureEntry, - private val updaterFeatureEntry: UpdaterFeatureEntry + private val deviceScreenDecomposeComponentFactory: DeviceScreenDecomposeComponent.Factory ) : InfoFeatureEntry { - - private val infoRoute = "@${ROUTE.name}/$DEEPLINK_KEY={$DEEPLINK_KEY}" - - override fun fullInfo(): String = "@${ROUTE.name}full" - override fun getWebUpdateByDeeplink(deeplink: Deeplink): String { - val deeplinkStr = Uri.encode(Json.encodeToString(deeplink)) - return "${DEEPLINK_SCHEME}web_updater=$deeplinkStr" - } - - private val arguments = listOf( - navArgument(DeeplinkConstants.KEY) { - nullable = true - type = DeeplinkNavType() - } - ) - - private val deeplinkArguments = listOf( - navDeepLink { uriPattern = DEEPLINK_WEB_UPDATER_URL } - ) - override fun NavGraphBuilder.navigation(navController: NavHostController) { - navigation(startDestination = infoRoute, route = ROUTE.name) { - composable( - route = infoRoute, - arguments = arguments, - deepLinks = deeplinkArguments - ) { - val globalNavController = LocalGlobalNavigationNavStack.current - ComposableDeviceInfoScreen( - updaterCardApi, - onOpenFullDeviceInfo = { navController.navigate(fullInfo()) }, - onOpenOptions = { navController.navigate(settingFeatureEntry.ROUTE.name) }, - onStartUpdateRequest = { - globalNavController.navigate(updaterFeatureEntry.getUpdaterScreen(it)) - } - ) - } - composable("@${ROUTE.name}full") { - ComposableFullDeviceInfoScreen(navController) + navigation(startDestination = "@${ROUTE.name}", route = ROUTE.name) { + composable("@${ROUTE.name}") { + val componentContext = rememberComponentContext() + val fileManagerComponent = remember(componentContext) { + deviceScreenDecomposeComponentFactory(componentContext) as DeviceScreenDecomposeComponentImpl + } + val childStack by fileManagerComponent.stack.subscribeAsState() + + Children( + stack = childStack + ) { + it.instance.Render() + } } } } diff --git a/components/info/impl/src/main/java/com/flipperdevices/info/impl/api/UpdateScreenDecomposeComponent.kt b/components/info/impl/src/main/java/com/flipperdevices/info/impl/api/UpdateScreenDecomposeComponent.kt new file mode 100644 index 0000000000..abfda403fa --- /dev/null +++ b/components/info/impl/src/main/java/com/flipperdevices/info/impl/api/UpdateScreenDecomposeComponent.kt @@ -0,0 +1,101 @@ +package com.flipperdevices.info.impl.api + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import com.arkivanov.decompose.ComponentContext +import com.arkivanov.decompose.router.stack.StackNavigator +import com.arkivanov.decompose.router.stack.push +import com.flipperdevices.core.ui.ktx.viewModelWithFactory +import com.flipperdevices.core.ui.navigation.LocalGlobalNavigationNavStack +import com.flipperdevices.deeplink.model.Deeplink +import com.flipperdevices.info.impl.compose.screens.ComposableDeviceInfoScreen +import com.flipperdevices.info.impl.model.DeviceScreenNavigationConfig +import com.flipperdevices.info.impl.viewmodel.AlarmViewModel +import com.flipperdevices.info.impl.viewmodel.ConnectViewModel +import com.flipperdevices.info.impl.viewmodel.DeviceStatusViewModel +import com.flipperdevices.info.impl.viewmodel.FirmwareUpdateViewModel +import com.flipperdevices.info.impl.viewmodel.FlipperColorViewModel +import com.flipperdevices.info.impl.viewmodel.deviceinfo.BasicInfoViewModel +import com.flipperdevices.ui.decompose.DecomposeComponent +import com.flipperdevices.updater.api.UpdaterCardApi +import com.flipperdevices.updater.api.UpdaterFeatureEntry +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import javax.inject.Provider + +@Suppress("LongParameterList") +class UpdateScreenDecomposeComponent @AssistedInject constructor( + @Assisted componentContext: ComponentContext, + @Assisted private val deeplink: Deeplink?, + @Assisted private val navigator: StackNavigator, + private val updaterCardApi: UpdaterCardApi, + private val updaterFeatureEntry: UpdaterFeatureEntry, + private val deviceStatusViewModelProvider: Provider, + private val connectViewModelProvider: Provider, + private val flipperColorProvider: Provider, + private val firmwareUpdateViewModelProvider: Provider, + private val alarmViewModelProvider: Provider, + private val basicInfoViewModelProvider: Provider +) : DecomposeComponent, ComponentContext by componentContext { + + @Suppress("NonSkippableComposable") + @Composable + override fun Render() { + val globalNavController = LocalGlobalNavigationNavStack.current + val deviceStatusViewModel = viewModelWithFactory(key = null) { + deviceStatusViewModelProvider.get() + } + val deviceStatus by deviceStatusViewModel.getState().collectAsState() + val updateState by deviceStatusViewModel.getUpdateState().collectAsState() + val connectViewModel = viewModelWithFactory(key = null) { + connectViewModelProvider.get() + } + val flipperColorViewModel = viewModelWithFactory(key = null) { + flipperColorProvider.get() + } + val flipperColor by flipperColorViewModel.getFlipperColor().collectAsState() + val firmwareUpdateViewModel = viewModelWithFactory(key = null) { + firmwareUpdateViewModelProvider.get() + } + val supportState by firmwareUpdateViewModel.getState().collectAsState() + val alarmViewModel = viewModelWithFactory(key = null) { + alarmViewModelProvider.get() + } + val basicInfoViewModel = viewModelWithFactory(key = null) { + basicInfoViewModelProvider.get() + } + val basicInfo by basicInfoViewModel.getDeviceInfo().collectAsState() + + ComposableDeviceInfoScreen( + updaterCardApi = updaterCardApi, + onOpenFullDeviceInfo = { + navigator.push(DeviceScreenNavigationConfig.FullInfo) + }, + onOpenOptions = { + navigator.push(DeviceScreenNavigationConfig.Options) + }, + onStartUpdateRequest = { + globalNavController.navigate(updaterFeatureEntry.getUpdaterScreen(it)) + }, + deeplink = deeplink, + deviceStatus = deviceStatus, + connectViewModel = connectViewModel, + hardwareColor = flipperColor, + supportedState = supportState, + updateState = updateState, + alarmOnFlipper = alarmViewModel::alarmOnFlipper, + deviceInfo = basicInfo + ) + } + + @AssistedFactory + fun interface Factory { + operator fun invoke( + componentContext: ComponentContext, + deeplink: Deeplink?, + navigator: StackNavigator + ): UpdateScreenDecomposeComponent + } +} diff --git a/components/info/impl/src/main/java/com/flipperdevices/info/impl/compose/bar/ComposableDeviceBar.kt b/components/info/impl/src/main/java/com/flipperdevices/info/impl/compose/bar/ComposableDeviceBar.kt index e614e42997..50ef6f9f5e 100644 --- a/components/info/impl/src/main/java/com/flipperdevices/info/impl/compose/bar/ComposableDeviceBar.kt +++ b/components/info/impl/src/main/java/com/flipperdevices/info/impl/compose/bar/ComposableDeviceBar.kt @@ -11,13 +11,13 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.material.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import com.flipperdevices.core.preference.pb.HardwareColor import com.flipperdevices.core.ui.flippermockup.ComposableFlipperMockup import com.flipperdevices.core.ui.flippermockup.ComposableFlipperMockupImage import com.flipperdevices.core.ui.ktx.SetUpStatusBarColor @@ -25,21 +25,20 @@ import com.flipperdevices.core.ui.theme.LocalPallet import com.flipperdevices.core.ui.theme.LocalTypography import com.flipperdevices.info.impl.R import com.flipperdevices.info.impl.model.DeviceStatus -import com.flipperdevices.info.impl.viewmodel.DeviceStatusViewModel -import com.flipperdevices.info.impl.viewmodel.FlipperColorViewModel -import tangle.viewmodel.compose.tangleViewModel import kotlin.math.roundToInt const val FLOAT_TO_PERCENT_QUALIFIER = 100 @Composable -fun ComposableDeviceBar(deviceStatusViewModel: DeviceStatusViewModel = tangleViewModel()) { - val deviceStatus by deviceStatusViewModel.getState().collectAsState() - DeviceBar(deviceStatus) +fun ComposableDeviceBar(deviceStatus: DeviceStatus, hardwareColor: HardwareColor) { + DeviceBar(deviceStatus, hardwareColor) } @Composable -private fun DeviceBar(deviceStatus: DeviceStatus) { +private fun DeviceBar( + deviceStatus: DeviceStatus, + hardwareColor: HardwareColor +) { Row( modifier = Modifier .fillMaxWidth() @@ -48,7 +47,10 @@ private fun DeviceBar(deviceStatus: DeviceStatus) { verticalAlignment = Alignment.CenterVertically ) { SetUpStatusBarColor(LocalPallet.current.accent, darkIcon = true) - FlipperImage(deviceStatus) + FlipperImage( + deviceStatus = deviceStatus, + flipperColor = hardwareColor + ) FlipperInformation(deviceStatus) } } @@ -69,10 +71,8 @@ private fun FlipperInformation(deviceStatus: DeviceStatus) { @Composable private fun FlipperImage( deviceStatus: DeviceStatus, - flipperColorViewModel: FlipperColorViewModel = tangleViewModel() + flipperColor: HardwareColor, ) { - val flipperColor by flipperColorViewModel.getFlipperColor().collectAsState() - val isActive = when (deviceStatus) { DeviceStatus.NoDevice -> false is DeviceStatus.Connected -> true @@ -161,7 +161,7 @@ private fun ComposableFlipperDeviceBarInformationPreview() { verticalArrangement = Arrangement.spacedBy(12.dp) ) { deviceStatus.forEach { - DeviceBar(it) + DeviceBar(it, HardwareColor.BLACK) } } } diff --git a/components/info/impl/src/main/java/com/flipperdevices/info/impl/compose/elements/ComposableConnectedDeviceActionCard.kt b/components/info/impl/src/main/java/com/flipperdevices/info/impl/compose/elements/ComposableConnectedDeviceActionCard.kt index 08547c26ab..b17b6ce410 100644 --- a/components/info/impl/src/main/java/com/flipperdevices/info/impl/compose/elements/ComposableConnectedDeviceActionCard.kt +++ b/components/info/impl/src/main/java/com/flipperdevices/info/impl/compose/elements/ComposableConnectedDeviceActionCard.kt @@ -1,50 +1,50 @@ package com.flipperdevices.info.impl.compose.elements import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import com.flipperdevices.bridge.api.manager.ktx.state.FlipperSupportedState import com.flipperdevices.core.ui.theme.LocalPallet import com.flipperdevices.info.impl.R import com.flipperdevices.info.impl.model.DeviceStatus -import com.flipperdevices.info.impl.viewmodel.AlarmViewModel -import com.flipperdevices.info.impl.viewmodel.ConnectViewModel -import com.flipperdevices.info.impl.viewmodel.DeviceStatusViewModel -import com.flipperdevices.info.impl.viewmodel.FirmwareUpdateViewModel import com.flipperdevices.info.shared.ButtonElementRow import com.flipperdevices.info.shared.ComposableInfoDivider import com.flipperdevices.info.shared.InfoElementCard -import tangle.viewmodel.compose.tangleViewModel import com.flipperdevices.core.ui.res.R as DesignSystem @Composable fun ComposableConnectedDeviceActionCard( + deviceStatus: DeviceStatus, + supportedState: FlipperSupportedState, + requestSynchronize: () -> Unit, + alarmOnFlipper: () -> Unit, modifier: Modifier = Modifier, - deviceStatusViewModel: DeviceStatusViewModel = tangleViewModel(), - firmwareUpdateViewModel: FirmwareUpdateViewModel = tangleViewModel() ) { - val deviceState by deviceStatusViewModel.getState().collectAsState() - val firmwareUpdateStatus by firmwareUpdateViewModel.getState().collectAsState() - if (deviceState is DeviceStatus.NoDevice) { + if (deviceStatus is DeviceStatus.NoDevice) { return } - val enabled = deviceState is DeviceStatus.Connected && - firmwareUpdateStatus == FlipperSupportedState.READY + val enabled = deviceStatus is DeviceStatus.Connected && + supportedState == FlipperSupportedState.READY InfoElementCard(modifier = modifier) { - ComposableSynchronize(enabled = enabled) + ComposableSynchronize( + enabled = enabled, + requestSynchronize = requestSynchronize + ) ComposableInfoDivider() - ComposableAlarmElement(enabled = enabled) + ComposableAlarmElement( + enabled = enabled, + alarmOnFlipper = alarmOnFlipper + ) } } @Composable private fun ComposableSynchronize( + requestSynchronize: () -> Unit, enabled: Boolean, - modifier: Modifier = Modifier, - connectViewModel: ConnectViewModel = tangleViewModel() + modifier: Modifier = Modifier ) { val color = if (enabled) { LocalPallet.current.accentSecond @@ -58,7 +58,7 @@ private fun ComposableSynchronize( iconId = DesignSystem.drawable.ic_syncing, color = color, onClick = if (enabled) { - connectViewModel::requestSynchronize + requestSynchronize } else { null } @@ -68,8 +68,8 @@ private fun ComposableSynchronize( @Composable private fun ComposableAlarmElement( enabled: Boolean, - modifier: Modifier = Modifier, - alarmViewModel: AlarmViewModel = tangleViewModel() + alarmOnFlipper: () -> Unit, + modifier: Modifier = Modifier ) { val colorId = if (enabled) { LocalPallet.current.accentSecond @@ -83,7 +83,7 @@ private fun ComposableAlarmElement( iconId = R.drawable.ic_ring, color = colorId, onClick = if (enabled) { - alarmViewModel::alarmOnFlipper + alarmOnFlipper } else { null } diff --git a/components/info/impl/src/main/java/com/flipperdevices/info/impl/compose/elements/ComposableFirmwareUnsupported.kt b/components/info/impl/src/main/java/com/flipperdevices/info/impl/compose/elements/ComposableFirmwareUnsupported.kt index d42d30f084..3c1326d779 100644 --- a/components/info/impl/src/main/java/com/flipperdevices/info/impl/compose/elements/ComposableFirmwareUnsupported.kt +++ b/components/info/impl/src/main/java/com/flipperdevices/info/impl/compose/elements/ComposableFirmwareUnsupported.kt @@ -9,7 +9,6 @@ import androidx.compose.foundation.layout.padding import androidx.compose.material.MaterialTheme import androidx.compose.material.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -22,18 +21,15 @@ import com.flipperdevices.core.markdown.ClickableUrlText import com.flipperdevices.core.ui.theme.LocalPallet import com.flipperdevices.core.ui.theme.LocalTypography import com.flipperdevices.info.impl.R -import com.flipperdevices.info.impl.viewmodel.FirmwareUpdateViewModel import com.flipperdevices.info.shared.InfoElementCard -import tangle.viewmodel.compose.tangleViewModel import com.flipperdevices.core.ui.res.R as DesignSystem @Composable fun ComposableFirmwareUpdate( - modifier: Modifier = Modifier, - firmwareViewModel: FirmwareUpdateViewModel = tangleViewModel() + supportedState: FlipperSupportedState, + modifier: Modifier = Modifier ) { - val updateStatus by firmwareViewModel.getState().collectAsState() - when (updateStatus) { + when (supportedState) { FlipperSupportedState.DEPRECATED_FLIPPER -> InfoElementCard( modifier = modifier, titleId = R.string.info_firmware_update_title diff --git a/components/info/impl/src/main/java/com/flipperdevices/info/impl/compose/elements/ComposableInfoCardContent.kt b/components/info/impl/src/main/java/com/flipperdevices/info/impl/compose/elements/ComposableInfoCardContent.kt index eccc17954d..01be704279 100644 --- a/components/info/impl/src/main/java/com/flipperdevices/info/impl/compose/elements/ComposableInfoCardContent.kt +++ b/components/info/impl/src/main/java/com/flipperdevices/info/impl/compose/elements/ComposableInfoCardContent.kt @@ -1,7 +1,6 @@ package com.flipperdevices.info.impl.compose.elements import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext @@ -24,24 +23,18 @@ import com.flipperdevices.info.impl.compose.info.ComposableFirmwareBuildDate import com.flipperdevices.info.impl.compose.info.ComposableFirmwareVersion import com.flipperdevices.info.impl.model.DeviceStatus import com.flipperdevices.info.impl.model.FlipperBasicInfo -import com.flipperdevices.info.impl.viewmodel.DeviceStatusViewModel -import com.flipperdevices.info.impl.viewmodel.deviceinfo.BasicInfoViewModel import com.flipperdevices.info.shared.ComposableDeviceInfoRowWithText import com.flipperdevices.info.shared.ComposableInfoDivider import com.flipperdevices.info.shared.InfoElementCard import com.flipperdevices.updater.model.FirmwareChannel import com.flipperdevices.updater.model.FirmwareVersion -import tangle.viewmodel.compose.tangleViewModel @Composable fun ComposableInfoCardContent( isUnsupported: Boolean, - basicInfoViewModel: BasicInfoViewModel = tangleViewModel(), - deviceStatusViewModel: DeviceStatusViewModel = tangleViewModel() + deviceStatus: DeviceStatus, + deviceInfo: FlipperBasicInfo ) { - val deviceInfo by basicInfoViewModel.getDeviceInfo().collectAsState() - val deviceStatus by deviceStatusViewModel.getState().collectAsState() - ComposableInfoCardContentInternal( isUnsupported = isUnsupported, flipperBasicInfo = deviceInfo, diff --git a/components/info/impl/src/main/java/com/flipperdevices/info/impl/compose/elements/ComposablePairDeviceActionCard.kt b/components/info/impl/src/main/java/com/flipperdevices/info/impl/compose/elements/ComposablePairDeviceActionCard.kt index 73d4b45b81..2328b57c1d 100644 --- a/components/info/impl/src/main/java/com/flipperdevices/info/impl/compose/elements/ComposablePairDeviceActionCard.kt +++ b/components/info/impl/src/main/java/com/flipperdevices/info/impl/compose/elements/ComposablePairDeviceActionCard.kt @@ -1,7 +1,6 @@ package com.flipperdevices.info.impl.compose.elements import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -13,32 +12,30 @@ import com.flipperdevices.info.impl.R import com.flipperdevices.info.impl.compose.dialogs.ComposableForgotDialog import com.flipperdevices.info.impl.model.DeviceStatus import com.flipperdevices.info.impl.viewmodel.ConnectViewModel -import com.flipperdevices.info.impl.viewmodel.DeviceStatusViewModel import com.flipperdevices.info.shared.ButtonElementRow import com.flipperdevices.info.shared.ComposableInfoDivider import com.flipperdevices.info.shared.InfoElementCard -import tangle.viewmodel.compose.tangleViewModel import com.flipperdevices.core.ui.res.R as DesignSystem @Composable fun ComposablePairDeviceActionCard( + connectViewModel: ConnectViewModel, + deviceStatus: DeviceStatus, modifier: Modifier = Modifier, - connectViewModel: ConnectViewModel = tangleViewModel(), - deviceStatusViewModel: DeviceStatusViewModel = tangleViewModel() ) { val navController = LocalGlobalNavigationNavStack.current - val deviceState by deviceStatusViewModel.getState().collectAsState() - val localDeviceState = deviceState InfoElementCard(modifier = modifier) { - when (localDeviceState) { + when (deviceStatus) { is DeviceStatus.Connected -> ComposableDisconnectElement(onDisconnect = connectViewModel::onDisconnect) + DeviceStatus.NoDevice -> ComposableFirstConnectElement( onGoToConnectScreen = { connectViewModel.goToConnectScreen(navController) } ) - is DeviceStatus.NoDeviceInformation -> if (localDeviceState.connectInProgress) { + + is DeviceStatus.NoDeviceInformation -> if (deviceStatus.connectInProgress) { ComposableDisconnectElement(onDisconnect = connectViewModel::onDisconnect) } else { ComposableConnectElement( @@ -47,7 +44,7 @@ fun ComposablePairDeviceActionCard( } } - if (localDeviceState is DeviceStatus.NoDevice) { + if (deviceStatus is DeviceStatus.NoDevice) { return@InfoElementCard } @@ -62,7 +59,7 @@ fun ComposablePairDeviceActionCard( ) if (isForgotDialogOpen) { ComposableForgotDialog( - flipperName = deviceState.getFlipperName(), + flipperName = deviceStatus.getFlipperName(), onCancel = { isForgotDialogOpen = false }, onForget = connectViewModel::forgetFlipper ) diff --git a/components/info/impl/src/main/java/com/flipperdevices/info/impl/compose/info/ComposableInfoCard.kt b/components/info/impl/src/main/java/com/flipperdevices/info/impl/compose/info/ComposableInfoCard.kt index f76341aa60..bdc1b3fa08 100644 --- a/components/info/impl/src/main/java/com/flipperdevices/info/impl/compose/info/ComposableInfoCard.kt +++ b/components/info/impl/src/main/java/com/flipperdevices/info/impl/compose/info/ComposableInfoCard.kt @@ -11,8 +11,8 @@ import androidx.compose.material.CircularProgressIndicator import androidx.compose.material.Icon import androidx.compose.material.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource @@ -27,31 +27,34 @@ import com.flipperdevices.core.ui.theme.LocalTypography import com.flipperdevices.info.impl.R import com.flipperdevices.info.impl.compose.elements.ComposableInfoCardContent import com.flipperdevices.info.impl.model.DeviceStatus -import com.flipperdevices.info.impl.viewmodel.DeviceStatusViewModel -import com.flipperdevices.info.impl.viewmodel.FirmwareUpdateViewModel +import com.flipperdevices.info.impl.model.FlipperBasicInfo import com.flipperdevices.info.shared.InfoElementCard import com.flipperdevices.updater.model.FlipperUpdateState -import tangle.viewmodel.compose.tangleViewModel import com.flipperdevices.core.ui.res.R as DesignSystem @Composable fun ComposableInfoCard( + deviceStatus: DeviceStatus, + updateState: FlipperUpdateState, + firmwareUpdateState: FlipperSupportedState, + deviceInfo: FlipperBasicInfo, onOpenFullDeviceInfo: () -> Unit, modifier: Modifier = Modifier, - deviceStatusViewModel: DeviceStatusViewModel = tangleViewModel(), - firmwareUpdateViewModel: FirmwareUpdateViewModel = tangleViewModel() ) { - val deviceStatus by deviceStatusViewModel.getState().collectAsState() - val updateStatus by deviceStatusViewModel.getUpdateState().collectAsState() - val firmwareUpdateStatus by firmwareUpdateViewModel.getState().collectAsState() - val isUnsupported = firmwareUpdateStatus != FlipperSupportedState.READY + val isUnsupported = remember(firmwareUpdateState) { + firmwareUpdateState != FlipperSupportedState.READY + } InfoElementCard(modifier, isSelectionArea = true, titleId = R.string.info_device_info_title) { - if (updateStatus is FlipperUpdateState.Updating) { + if (updateState is FlipperUpdateState.Updating) { ComposableWaitingFlipper() return@InfoElementCard } - ComposableInfoCardContent(isUnsupported) + ComposableInfoCardContent( + isUnsupported = isUnsupported, + deviceStatus = deviceStatus, + deviceInfo = deviceInfo + ) if (deviceStatus is DeviceStatus.Connected && !isUnsupported) { ComposableFullInfoButton(onOpenFullDeviceInfo) } diff --git a/components/info/impl/src/main/java/com/flipperdevices/info/impl/compose/screens/ComposableDeviceInfoScreen.kt b/components/info/impl/src/main/java/com/flipperdevices/info/impl/compose/screens/ComposableDeviceInfoScreen.kt index fda6b2111e..0dce284221 100644 --- a/components/info/impl/src/main/java/com/flipperdevices/info/impl/compose/screens/ComposableDeviceInfoScreen.kt +++ b/components/info/impl/src/main/java/com/flipperdevices/info/impl/compose/screens/ComposableDeviceInfoScreen.kt @@ -12,21 +12,36 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp +import com.flipperdevices.bridge.api.manager.ktx.state.FlipperSupportedState +import com.flipperdevices.core.preference.pb.HardwareColor import com.flipperdevices.core.ui.ktx.elements.SwipeRefresh +import com.flipperdevices.deeplink.model.Deeplink import com.flipperdevices.info.impl.compose.bar.ComposableDeviceBar import com.flipperdevices.info.impl.compose.elements.ComposableConnectedDeviceActionCard import com.flipperdevices.info.impl.compose.elements.ComposableFirmwareUpdate import com.flipperdevices.info.impl.compose.elements.ComposableOptionsCard import com.flipperdevices.info.impl.compose.elements.ComposablePairDeviceActionCard import com.flipperdevices.info.impl.compose.info.ComposableInfoCard +import com.flipperdevices.info.impl.model.DeviceStatus +import com.flipperdevices.info.impl.model.FlipperBasicInfo +import com.flipperdevices.info.impl.viewmodel.ConnectViewModel import com.flipperdevices.updater.api.UpdaterCardApi +import com.flipperdevices.updater.model.FlipperUpdateState import com.flipperdevices.updater.model.UpdateRequest @Composable fun ComposableDeviceInfoScreen( updaterCardApi: UpdaterCardApi, + deeplink: Deeplink?, + deviceStatus: DeviceStatus, + connectViewModel: ConnectViewModel, + hardwareColor: HardwareColor, + supportedState: FlipperSupportedState, + updateState: FlipperUpdateState, + deviceInfo: FlipperBasicInfo, onOpenFullDeviceInfo: () -> Unit, onOpenOptions: () -> Unit, + alarmOnFlipper: () -> Unit, onStartUpdateRequest: (UpdateRequest) -> Unit, modifier: Modifier = Modifier ) { @@ -41,25 +56,43 @@ fun ComposableDeviceInfoScreen( .fillMaxSize() .verticalScroll(rememberScrollState()) ) { - ComposableDeviceBar() + ComposableDeviceBar(deviceStatus, hardwareColor) updaterCardApi.ComposableUpdaterCard( modifier = Modifier.padding(top = 14.dp), onStartUpdateRequest = onStartUpdateRequest, requestRefresh = refreshRequested, - onRefreshRequestExecuted = { refreshRequested = false } + onRefreshRequestExecuted = { refreshRequested = false }, + deeplink = deeplink + ) + ComposableFirmwareUpdate( + modifier = Modifier.padding(top = 14.dp), + supportedState = supportedState ) - ComposableFirmwareUpdate(modifier = Modifier.padding(top = 14.dp)) ComposableInfoCard( modifier = Modifier.padding(top = 14.dp), - onOpenFullDeviceInfo = onOpenFullDeviceInfo + onOpenFullDeviceInfo = onOpenFullDeviceInfo, + deviceStatus = deviceStatus, + updateState = updateState, + firmwareUpdateState = supportedState, + deviceInfo = deviceInfo ) ComposableOptionsCard( modifier = Modifier .padding(top = 14.dp), onOpenOptions = onOpenOptions ) - ComposableConnectedDeviceActionCard(modifier = Modifier.padding(top = 14.dp)) - ComposablePairDeviceActionCard(modifier = Modifier.padding(top = 14.dp, bottom = 14.dp)) + ComposableConnectedDeviceActionCard( + modifier = Modifier.padding(top = 14.dp), + deviceStatus = deviceStatus, + supportedState = supportedState, + requestSynchronize = connectViewModel::requestSynchronize, + alarmOnFlipper = alarmOnFlipper + ) + ComposablePairDeviceActionCard( + modifier = Modifier.padding(top = 14.dp, bottom = 14.dp), + deviceStatus = deviceStatus, + connectViewModel = connectViewModel + ) } } } diff --git a/components/info/impl/src/main/java/com/flipperdevices/info/impl/compose/screens/ComposableFullDeviceInfoScreen.kt b/components/info/impl/src/main/java/com/flipperdevices/info/impl/compose/screens/ComposableFullDeviceInfoScreen.kt index 85337a2317..872a27ca66 100644 --- a/components/info/impl/src/main/java/com/flipperdevices/info/impl/compose/screens/ComposableFullDeviceInfoScreen.kt +++ b/components/info/impl/src/main/java/com/flipperdevices/info/impl/compose/screens/ComposableFullDeviceInfoScreen.kt @@ -10,8 +10,6 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp -import androidx.lifecycle.viewmodel.compose.viewModel -import androidx.navigation.NavHostController import com.flipperdevices.bridge.rpcinfo.model.FlipperInformationStatus import com.flipperdevices.bridge.rpcinfo.model.FlipperRpcInformation import com.flipperdevices.bridge.rpcinfo.model.dataOrNull @@ -25,17 +23,16 @@ import com.flipperdevices.info.impl.viewmodel.DeviceStatusViewModel import com.flipperdevices.info.impl.viewmodel.deviceinfo.BasicInfoViewModel import com.flipperdevices.info.impl.viewmodel.deviceinfo.FullInfoViewModel import com.flipperdevices.info.impl.viewmodel.deviceinfo.ShareFullInfoFileViewModel -import tangle.viewmodel.compose.tangleViewModel import com.flipperdevices.core.ui.res.R as DesignSystem @Composable fun ComposableFullDeviceInfoScreen( - navController: NavHostController, + onBack: () -> Unit, + shareViewModel: ShareFullInfoFileViewModel, + basicInfoViewModel: BasicInfoViewModel, + fullInfoViewModel: FullInfoViewModel, + deviceStatusViewModel: DeviceStatusViewModel, modifier: Modifier = Modifier, - shareViewModel: ShareFullInfoFileViewModel = viewModel(), - basicInfoViewModel: BasicInfoViewModel = tangleViewModel(), - fullInfoViewModel: FullInfoViewModel = tangleViewModel(), - deviceStatusViewModel: DeviceStatusViewModel = tangleViewModel() ) { val deviceStatus by deviceStatusViewModel.getState().collectAsState() val localDeviceStatus = deviceStatus @@ -49,12 +46,13 @@ fun ComposableFullDeviceInfoScreen( basicInfo.storageInfo.externalStorageStatus !is FlipperInformationStatus.Ready || basicInfo.storageInfo.internalStorageStatus !is FlipperInformationStatus.Ready || basicInfo.firmwareVersion !is FlipperInformationStatus.Ready + else -> false } Column(modifier = modifier) { ComposableFullDeviceInfoScreenBar( - onBack = navController::popBackStack, + onBack = onBack, onShare = { shareViewModel.shareDeviceInfo(flipperRpcInformation.dataOrNull(), basicInfo) }, diff --git a/components/info/impl/src/main/java/com/flipperdevices/info/impl/model/DeviceScreenNavigationConfig.kt b/components/info/impl/src/main/java/com/flipperdevices/info/impl/model/DeviceScreenNavigationConfig.kt new file mode 100644 index 0000000000..3ce672d582 --- /dev/null +++ b/components/info/impl/src/main/java/com/flipperdevices/info/impl/model/DeviceScreenNavigationConfig.kt @@ -0,0 +1,16 @@ +package com.flipperdevices.info.impl.model + +import com.flipperdevices.deeplink.model.Deeplink +import kotlinx.serialization.Serializable + +@Serializable +sealed class DeviceScreenNavigationConfig { + @Serializable + data class Update(val deeplink: Deeplink? = null) : DeviceScreenNavigationConfig() + + @Serializable + data object FullInfo : DeviceScreenNavigationConfig() + + @Serializable + data object Options : DeviceScreenNavigationConfig() +} diff --git a/components/info/impl/src/main/java/com/flipperdevices/info/impl/viewmodel/AlarmViewModel.kt b/components/info/impl/src/main/java/com/flipperdevices/info/impl/viewmodel/AlarmViewModel.kt index 6d5aad0ed1..f3ebb92b12 100644 --- a/components/info/impl/src/main/java/com/flipperdevices/info/impl/viewmodel/AlarmViewModel.kt +++ b/components/info/impl/src/main/java/com/flipperdevices/info/impl/viewmodel/AlarmViewModel.kt @@ -8,20 +8,20 @@ import com.flipperdevices.core.ui.lifecycle.LifecycleViewModel import com.flipperdevices.protobuf.main import com.flipperdevices.protobuf.system.playAudiovisualAlertRequest import kotlinx.coroutines.launch -import tangle.viewmodel.VMInject +import javax.inject.Inject -class AlarmViewModel @VMInject constructor( +class AlarmViewModel @Inject constructor( private val serviceProvider: FlipperServiceProvider ) : LifecycleViewModel() { fun alarmOnFlipper() { serviceProvider.provideServiceApi(this) { serviceApi -> viewModelScope.launch { - alarmOnFlipper(serviceApi) + alarmOnFlipperInternal(serviceApi) } } } - private suspend fun alarmOnFlipper(serviceApi: FlipperServiceApi) { + private suspend fun alarmOnFlipperInternal(serviceApi: FlipperServiceApi) { serviceApi.requestApi.requestWithoutAnswer( main { systemPlayAudiovisualAlertRequest = playAudiovisualAlertRequest { } diff --git a/components/info/impl/src/main/java/com/flipperdevices/info/impl/viewmodel/ConnectViewModel.kt b/components/info/impl/src/main/java/com/flipperdevices/info/impl/viewmodel/ConnectViewModel.kt index afacb32c49..720e622cf8 100644 --- a/components/info/impl/src/main/java/com/flipperdevices/info/impl/viewmodel/ConnectViewModel.kt +++ b/components/info/impl/src/main/java/com/flipperdevices/info/impl/viewmodel/ConnectViewModel.kt @@ -12,10 +12,10 @@ import com.flipperdevices.info.impl.model.ConnectRequestState import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch -import tangle.viewmodel.VMInject import java.util.concurrent.atomic.AtomicBoolean +import javax.inject.Inject -class ConnectViewModel @VMInject constructor( +class ConnectViewModel @Inject constructor( private val serviceProvider: FlipperServiceProvider, private val synchronizationApi: SynchronizationApi, private val dataStoreFirstPair: DataStore, diff --git a/components/info/impl/src/main/java/com/flipperdevices/info/impl/viewmodel/DeviceStatusViewModel.kt b/components/info/impl/src/main/java/com/flipperdevices/info/impl/viewmodel/DeviceStatusViewModel.kt index 1b09bd13be..88eb0d58de 100644 --- a/components/info/impl/src/main/java/com/flipperdevices/info/impl/viewmodel/DeviceStatusViewModel.kt +++ b/components/info/impl/src/main/java/com/flipperdevices/info/impl/viewmodel/DeviceStatusViewModel.kt @@ -16,9 +16,9 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach -import tangle.viewmodel.VMInject +import javax.inject.Inject -class DeviceStatusViewModel @VMInject constructor( +class DeviceStatusViewModel @Inject constructor( serviceProvider: FlipperServiceProvider, private val dataStorePair: DataStore, private val updateStateApi: UpdateStateApi diff --git a/components/info/impl/src/main/java/com/flipperdevices/info/impl/viewmodel/FirmwareUpdateViewModel.kt b/components/info/impl/src/main/java/com/flipperdevices/info/impl/viewmodel/FirmwareUpdateViewModel.kt index 8a128d94fd..b171cdce02 100644 --- a/components/info/impl/src/main/java/com/flipperdevices/info/impl/viewmodel/FirmwareUpdateViewModel.kt +++ b/components/info/impl/src/main/java/com/flipperdevices/info/impl/viewmodel/FirmwareUpdateViewModel.kt @@ -13,9 +13,9 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach -import tangle.viewmodel.VMInject +import javax.inject.Inject -class FirmwareUpdateViewModel @VMInject constructor( +class FirmwareUpdateViewModel @Inject constructor( serviceProvider: FlipperServiceProvider ) : LifecycleViewModel(), FlipperBleServiceConsumer, LogTagProvider { override val TAG = "FirmwareUpdateViewModel" diff --git a/components/info/impl/src/main/java/com/flipperdevices/info/impl/viewmodel/FlipperColorViewModel.kt b/components/info/impl/src/main/java/com/flipperdevices/info/impl/viewmodel/FlipperColorViewModel.kt index c8ea0f03d6..dd32aa397f 100644 --- a/components/info/impl/src/main/java/com/flipperdevices/info/impl/viewmodel/FlipperColorViewModel.kt +++ b/components/info/impl/src/main/java/com/flipperdevices/info/impl/viewmodel/FlipperColorViewModel.kt @@ -18,11 +18,11 @@ import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch -import tangle.viewmodel.VMInject +import javax.inject.Inject private const val RPC_KEY_HARDWARE_COLOR = "hardware.color" -class FlipperColorViewModel @VMInject constructor( +class FlipperColorViewModel @Inject constructor( private val settings: DataStore ) : ViewModel(), FlipperBleServiceConsumer { private val colorFlipperState = MutableStateFlow(HardwareColor.UNRECOGNIZED) diff --git a/components/info/impl/src/main/java/com/flipperdevices/info/impl/viewmodel/deviceinfo/BasicInfoViewModel.kt b/components/info/impl/src/main/java/com/flipperdevices/info/impl/viewmodel/deviceinfo/BasicInfoViewModel.kt index 2c7a10fe89..de098747ed 100644 --- a/components/info/impl/src/main/java/com/flipperdevices/info/impl/viewmodel/deviceinfo/BasicInfoViewModel.kt +++ b/components/info/impl/src/main/java/com/flipperdevices/info/impl/viewmodel/deviceinfo/BasicInfoViewModel.kt @@ -22,9 +22,9 @@ import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import kotlinx.coroutines.plus -import tangle.viewmodel.VMInject +import javax.inject.Inject -class BasicInfoViewModel @VMInject constructor( +class BasicInfoViewModel @Inject constructor( serviceProvider: FlipperServiceProvider, private val flipperVersionProviderApi: FlipperVersionProviderApi, private val flipperStorageInformationApi: FlipperStorageInformationApi diff --git a/components/info/impl/src/main/java/com/flipperdevices/info/impl/viewmodel/deviceinfo/FullInfoViewModel.kt b/components/info/impl/src/main/java/com/flipperdevices/info/impl/viewmodel/deviceinfo/FullInfoViewModel.kt index 203580631d..9492858459 100644 --- a/components/info/impl/src/main/java/com/flipperdevices/info/impl/viewmodel/deviceinfo/FullInfoViewModel.kt +++ b/components/info/impl/src/main/java/com/flipperdevices/info/impl/viewmodel/deviceinfo/FullInfoViewModel.kt @@ -23,9 +23,9 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import kotlinx.coroutines.plus -import tangle.viewmodel.VMInject +import javax.inject.Inject -class FullInfoViewModel @VMInject constructor( +class FullInfoViewModel @Inject constructor( private val serviceProvider: FlipperServiceProvider, private val flipperRpcInformationApi: FlipperRpcInformationApi, private val firmwareVersionBuilderApi: FirmwareVersionBuilderApi, diff --git a/components/info/impl/src/main/java/com/flipperdevices/info/impl/viewmodel/deviceinfo/ShareFullInfoFileViewModel.kt b/components/info/impl/src/main/java/com/flipperdevices/info/impl/viewmodel/deviceinfo/ShareFullInfoFileViewModel.kt index c7cf4ff4d0..138f202de4 100644 --- a/components/info/impl/src/main/java/com/flipperdevices/info/impl/viewmodel/deviceinfo/ShareFullInfoFileViewModel.kt +++ b/components/info/impl/src/main/java/com/flipperdevices/info/impl/viewmodel/deviceinfo/ShareFullInfoFileViewModel.kt @@ -16,8 +16,9 @@ import com.flipperdevices.info.impl.model.FlipperBasicInfo import java.io.File import java.time.LocalDateTime import java.time.format.DateTimeFormatter +import javax.inject.Inject -class ShareFullInfoFileViewModel( +class ShareFullInfoFileViewModel @Inject constructor( application: Application, ) : AndroidViewModel(application) { fun shareDeviceInfo( diff --git a/components/settings/api/build.gradle.kts b/components/settings/api/build.gradle.kts index 8fd3508c2c..b1ad66acbf 100644 --- a/components/settings/api/build.gradle.kts +++ b/components/settings/api/build.gradle.kts @@ -5,5 +5,6 @@ plugins { android.namespace = "com.flipperdevices.settings.api" dependencies { - implementation(projects.components.core.ui.navigation) + implementation(projects.components.core.ui.decompose) + implementation(libs.decompose) } diff --git a/components/settings/api/src/main/java/com/flipperdevices/settings/api/SettingsDecomposeComponent.kt b/components/settings/api/src/main/java/com/flipperdevices/settings/api/SettingsDecomposeComponent.kt new file mode 100644 index 0000000000..e03a802f2c --- /dev/null +++ b/components/settings/api/src/main/java/com/flipperdevices/settings/api/SettingsDecomposeComponent.kt @@ -0,0 +1,12 @@ +package com.flipperdevices.settings.api + +import com.arkivanov.decompose.ComponentContext +import com.flipperdevices.ui.decompose.DecomposeComponent + +interface SettingsDecomposeComponent : DecomposeComponent { + fun interface Factory { + operator fun invoke( + componentContext: ComponentContext, + ): SettingsDecomposeComponent + } +} diff --git a/components/settings/api/src/main/java/com/flipperdevices/settings/api/SettingsFeatureEntry.kt b/components/settings/api/src/main/java/com/flipperdevices/settings/api/SettingsFeatureEntry.kt deleted file mode 100644 index a0179b20c4..0000000000 --- a/components/settings/api/src/main/java/com/flipperdevices/settings/api/SettingsFeatureEntry.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.flipperdevices.settings.api - -import com.flipperdevices.core.ui.navigation.AggregateFeatureEntry -import com.flipperdevices.core.ui.navigation.FeatureScreenRootRoute - -interface SettingsFeatureEntry : AggregateFeatureEntry { - override val ROUTE: FeatureScreenRootRoute - get() = FeatureScreenRootRoute.OPTIONS -} diff --git a/components/settings/impl/build.gradle.kts b/components/settings/impl/build.gradle.kts index b8b692d205..508f5748af 100644 --- a/components/settings/impl/build.gradle.kts +++ b/components/settings/impl/build.gradle.kts @@ -24,7 +24,6 @@ dependencies { implementation(projects.components.core.ui.decompose) implementation(projects.components.core.ui.theme) - implementation(projects.components.core.ui.navigation) implementation(projects.components.debug.api) implementation(projects.components.firstpair.api) @@ -50,7 +49,6 @@ dependencies { implementation(libs.compose.tooling) implementation(libs.compose.foundation) implementation(libs.compose.material) - implementation(libs.compose.navigation) implementation(libs.bundles.decompose) implementation(libs.lifecycle.compose) diff --git a/components/settings/impl/src/main/java/com/flipperdevices/settings/impl/api/SettingsDecomposeComponentImpl.kt b/components/settings/impl/src/main/java/com/flipperdevices/settings/impl/api/SettingsDecomposeComponentImpl.kt index 22f66e71c4..a44aca1957 100644 --- a/components/settings/impl/src/main/java/com/flipperdevices/settings/impl/api/SettingsDecomposeComponentImpl.kt +++ b/components/settings/impl/src/main/java/com/flipperdevices/settings/impl/api/SettingsDecomposeComponentImpl.kt @@ -1,6 +1,10 @@ package com.flipperdevices.settings.impl.api +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue import com.arkivanov.decompose.ComponentContext +import com.arkivanov.decompose.extensions.compose.stack.Children +import com.arkivanov.decompose.extensions.compose.subscribeAsState import com.arkivanov.decompose.router.stack.ChildStack import com.arkivanov.decompose.router.stack.StackNavigation import com.arkivanov.decompose.router.stack.childStack @@ -9,6 +13,7 @@ import com.arkivanov.decompose.value.Value import com.flipperdevices.core.di.AppGraph import com.flipperdevices.debug.api.StressTestDecomposeComponent import com.flipperdevices.filemanager.api.navigation.FileManagerDecomposeComponent +import com.flipperdevices.settings.api.SettingsDecomposeComponent import com.flipperdevices.settings.impl.model.SettingsNavigationConfig import com.flipperdevices.shake2report.api.Shake2ReportDecomposeComponent import com.flipperdevices.ui.decompose.DecomposeComponent @@ -18,16 +23,6 @@ import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import javax.inject.Provider -interface SettingsDecomposeComponent { - val stack: Value> - - fun interface Factory { - operator fun invoke( - componentContext: ComponentContext, - ): SettingsDecomposeComponent - } -} - class SettingsDecomposeComponentImpl @AssistedInject constructor( @Assisted componentContext: ComponentContext, private val fileManagerComponentFactory: FileManagerDecomposeComponent.Factory, @@ -37,7 +32,7 @@ class SettingsDecomposeComponentImpl @AssistedInject constructor( ) : SettingsDecomposeComponent, ComponentContext by componentContext { private val navigation = StackNavigation() - override val stack: Value> = childStack( + private val stack: Value> = childStack( source = navigation, serializer = SettingsNavigationConfig.serializer(), initialConfiguration = SettingsNavigationConfig.Main, @@ -62,4 +57,16 @@ class SettingsDecomposeComponentImpl @AssistedInject constructor( componentContext: ComponentContext, ): SettingsDecomposeComponentImpl } + + @Composable + @Suppress("NonSkippableComposable") + override fun Render() { + val childStack by stack.subscribeAsState() + + Children( + stack = childStack, + ) { + it.instance.Render() + } + } } diff --git a/components/settings/impl/src/main/java/com/flipperdevices/settings/impl/api/SettingsFeatureEntryImpl.kt b/components/settings/impl/src/main/java/com/flipperdevices/settings/impl/api/SettingsFeatureEntryImpl.kt deleted file mode 100644 index 335ca735d5..0000000000 --- a/components/settings/impl/src/main/java/com/flipperdevices/settings/impl/api/SettingsFeatureEntryImpl.kt +++ /dev/null @@ -1,43 +0,0 @@ -package com.flipperdevices.settings.impl.api - -import androidx.compose.runtime.getValue -import androidx.compose.runtime.remember -import androidx.navigation.NavGraphBuilder -import androidx.navigation.NavHostController -import androidx.navigation.compose.composable -import androidx.navigation.navigation -import com.arkivanov.decompose.extensions.compose.stack.Children -import com.arkivanov.decompose.extensions.compose.subscribeAsState -import com.flipperdevices.core.di.AppGraph -import com.flipperdevices.core.ui.navigation.AggregateFeatureEntry -import com.flipperdevices.settings.api.SettingsFeatureEntry -import com.flipperdevices.ui.decompose.rememberComponentContext -import com.squareup.anvil.annotations.ContributesBinding -import com.squareup.anvil.annotations.ContributesMultibinding -import javax.inject.Inject - -@ContributesBinding(AppGraph::class, SettingsFeatureEntry::class) -@ContributesMultibinding(AppGraph::class, AggregateFeatureEntry::class) -class SettingsFeatureEntryImpl @Inject constructor( - private val settingsFactory: SettingsDecomposeComponent.Factory -) : SettingsFeatureEntry { - private fun start() = "@${ROUTE.name}" - - override fun NavGraphBuilder.navigation(navController: NavHostController) { - navigation(startDestination = start(), route = ROUTE.name) { - composable("@${ROUTE.name}") { - val componentContext = rememberComponentContext() - val fileManagerComponent = remember(componentContext) { - settingsFactory(componentContext) - } - val childStack by fileManagerComponent.stack.subscribeAsState() - - Children( - stack = childStack - ) { - it.instance.Render() - } - } - } - } -} diff --git a/components/updater/api/src/main/java/com/flipperdevices/updater/api/UpdaterCardApi.kt b/components/updater/api/src/main/java/com/flipperdevices/updater/api/UpdaterCardApi.kt index 764526a1e7..6789a32509 100644 --- a/components/updater/api/src/main/java/com/flipperdevices/updater/api/UpdaterCardApi.kt +++ b/components/updater/api/src/main/java/com/flipperdevices/updater/api/UpdaterCardApi.kt @@ -2,6 +2,7 @@ package com.flipperdevices.updater.api import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import com.flipperdevices.deeplink.model.Deeplink import com.flipperdevices.updater.model.UpdateRequest interface UpdaterCardApi { @@ -9,6 +10,7 @@ interface UpdaterCardApi { @Composable fun ComposableUpdaterCard( modifier: Modifier, + deeplink: Deeplink?, onStartUpdateRequest: (UpdateRequest) -> Unit, requestRefresh: Boolean, onRefreshRequestExecuted: () -> Unit diff --git a/components/updater/api/src/main/java/com/flipperdevices/updater/model/FlipperUpdateState.kt b/components/updater/api/src/main/java/com/flipperdevices/updater/model/FlipperUpdateState.kt index 35212767e7..c378c884cc 100644 --- a/components/updater/api/src/main/java/com/flipperdevices/updater/model/FlipperUpdateState.kt +++ b/components/updater/api/src/main/java/com/flipperdevices/updater/model/FlipperUpdateState.kt @@ -1,10 +1,13 @@ package com.flipperdevices.updater.model +import androidx.compose.runtime.Immutable + +@Immutable sealed class FlipperUpdateState { - object NotConnected : FlipperUpdateState() - object ConnectingInProgress : FlipperUpdateState() - object Updating : FlipperUpdateState() - class Complete(val version: FirmwareVersion?) : FlipperUpdateState() - class Failed(val version: FirmwareVersion?) : FlipperUpdateState() - object Ready : FlipperUpdateState() + data object NotConnected : FlipperUpdateState() + data object ConnectingInProgress : FlipperUpdateState() + data object Updating : FlipperUpdateState() + data class Complete(val version: FirmwareVersion?) : FlipperUpdateState() + data class Failed(val version: FirmwareVersion?) : FlipperUpdateState() + data object Ready : FlipperUpdateState() } diff --git a/components/updater/api/src/main/java/com/flipperdevices/updater/model/UpdateCardState.kt b/components/updater/api/src/main/java/com/flipperdevices/updater/model/UpdateCardState.kt index b94d234a77..78d63e6a7c 100644 --- a/components/updater/api/src/main/java/com/flipperdevices/updater/model/UpdateCardState.kt +++ b/components/updater/api/src/main/java/com/flipperdevices/updater/model/UpdateCardState.kt @@ -1,7 +1,10 @@ package com.flipperdevices.updater.model +import androidx.compose.runtime.Immutable + +@Immutable sealed class UpdateCardState { - object InProgress : UpdateCardState() + data object InProgress : UpdateCardState() data class NoUpdate( val flipperVersion: FirmwareVersion diff --git a/components/updater/card/build.gradle.kts b/components/updater/card/build.gradle.kts index 03e58dc900..11d1acc521 100644 --- a/components/updater/card/build.gradle.kts +++ b/components/updater/card/build.gradle.kts @@ -18,6 +18,7 @@ dependencies { implementation(projects.components.deeplink.api) implementation(projects.components.bridge.dao.api) implementation(projects.components.bridge.synchronization.api) + implementation(projects.components.core.ui.decompose) implementation(projects.components.updater.api) implementation(projects.components.updater.subghz) @@ -44,10 +45,6 @@ dependencies { implementation(libs.compose.material) implementation(libs.compose.activity) - implementation(libs.tangle.viewmodel.compose) - implementation(libs.tangle.viewmodel.api) - anvil(libs.tangle.viewmodel.compiler) - // Testing testImplementation(libs.junit) testImplementation(libs.kotlin.coroutines.test) diff --git a/components/updater/card/src/main/java/com/flipperdevices/updater/card/api/UpdaterCardApiImpl.kt b/components/updater/card/src/main/java/com/flipperdevices/updater/card/api/UpdaterCardApiImpl.kt index 0006bb2015..31eb3ba3a6 100644 --- a/components/updater/card/src/main/java/com/flipperdevices/updater/card/api/UpdaterCardApiImpl.kt +++ b/components/updater/card/src/main/java/com/flipperdevices/updater/card/api/UpdaterCardApiImpl.kt @@ -4,26 +4,43 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Modifier import com.flipperdevices.core.di.AppGraph +import com.flipperdevices.core.ui.ktx.viewModelWithFactory +import com.flipperdevices.deeplink.model.Deeplink import com.flipperdevices.updater.api.UpdaterCardApi import com.flipperdevices.updater.card.composable.ComposableUpdaterCardInternal import com.flipperdevices.updater.card.viewmodel.UpdateCardViewModel +import com.flipperdevices.updater.card.viewmodel.UpdateRequestViewModel import com.flipperdevices.updater.card.viewmodel.UpdateStateViewModel import com.flipperdevices.updater.model.UpdateRequest import com.squareup.anvil.annotations.ContributesBinding -import tangle.viewmodel.compose.tangleViewModel import javax.inject.Inject +import javax.inject.Provider @ContributesBinding(AppGraph::class) -class UpdaterCardApiImpl @Inject constructor() : UpdaterCardApi { +class UpdaterCardApiImpl @Inject constructor( + private val updateCardFactory: UpdateCardViewModel.Factory, + private val updateStateViewModelProvider: Provider, + private val updateRequestViewModelProvider: Provider +) : UpdaterCardApi { @Composable override fun ComposableUpdaterCard( modifier: Modifier, + deeplink: Deeplink?, onStartUpdateRequest: (UpdateRequest) -> Unit, requestRefresh: Boolean, onRefreshRequestExecuted: () -> Unit ) { - val updateStateViewModel: UpdateStateViewModel = tangleViewModel() - val updateCardViewModel: UpdateCardViewModel = tangleViewModel() + val updateStateViewModel: UpdateStateViewModel = viewModelWithFactory(key = null) { + updateStateViewModelProvider.get() + } + val updateCardViewModel: UpdateCardViewModel = viewModelWithFactory( + key = deeplink?.toString() + ) { + updateCardFactory(deeplink) + } + val updateRequestViewModel: UpdateRequestViewModel = viewModelWithFactory(key = null) { + updateRequestViewModelProvider.get() + } LaunchedEffect(requestRefresh) { if (requestRefresh) { @@ -36,7 +53,8 @@ class UpdaterCardApiImpl @Inject constructor() : UpdaterCardApi { modifier = modifier, onStartUpdateRequest = onStartUpdateRequest, updateStateViewModel = updateStateViewModel, - updateCardViewModel = updateCardViewModel + updateCardViewModel = updateCardViewModel, + updateRequestViewModel = updateRequestViewModel ) } } diff --git a/components/updater/card/src/main/java/com/flipperdevices/updater/card/composable/ComposableFirmwareUpdaterContent.kt b/components/updater/card/src/main/java/com/flipperdevices/updater/card/composable/ComposableFirmwareUpdaterContent.kt index 0076030eea..911b4ccb04 100644 --- a/components/updater/card/src/main/java/com/flipperdevices/updater/card/composable/ComposableFirmwareUpdaterContent.kt +++ b/components/updater/card/src/main/java/com/flipperdevices/updater/card/composable/ComposableFirmwareUpdaterContent.kt @@ -1,28 +1,19 @@ package com.flipperdevices.updater.card.composable -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import com.flipperdevices.core.ui.theme.FlipperThemeInternal import com.flipperdevices.info.shared.ComposableDeviceInfoRow import com.flipperdevices.info.shared.ComposableInfoDivider import com.flipperdevices.updater.card.R -import com.flipperdevices.updater.model.DistributionFile +import com.flipperdevices.updater.card.viewmodel.UpdateRequestViewModel import com.flipperdevices.updater.model.FirmwareChannel import com.flipperdevices.updater.model.FirmwareVersion -import com.flipperdevices.updater.model.OfficialFirmware import com.flipperdevices.updater.model.UpdateCardState -import com.flipperdevices.updater.model.UpdateErrorType import com.flipperdevices.updater.model.UpdateRequest @Composable fun ComposableFirmwareUpdaterContent( + updateRequestViewModel: UpdateRequestViewModel, version: FirmwareVersion?, updateCardState: UpdateCardState, onSelectFirmwareChannel: (FirmwareChannel) -> Unit, @@ -43,54 +34,10 @@ fun ComposableFirmwareUpdaterContent( modifier = modifier ) ComposableInfoDivider() - ComposableUpdateButton(updateCardState, inProgress, onStartUpdateRequest = onStartUpdateRequest) -} - -@Preview( - showBackground = true, - showSystemUi = true -) -@Composable -private fun ComposableFirmwareUpdaterContentPreview() { - FlipperThemeInternal { - val lastVersion = FirmwareVersion( - channel = FirmwareChannel.DEV, - version = "1.4.5" - ) - val cardStates = listOf( - UpdateCardState.InProgress, - UpdateCardState.NoUpdate(lastVersion), - UpdateCardState.UpdateAvailable( - update = UpdateRequest( - updateFrom = lastVersion, - updateTo = lastVersion, - content = OfficialFirmware(DistributionFile(url = "", sha256 = "")), - changelog = null - ), - isOtherChannel = false - ), - UpdateCardState.UpdateAvailable( - update = UpdateRequest( - updateFrom = lastVersion, - updateTo = lastVersion, - content = OfficialFirmware(DistributionFile(url = "", sha256 = "")), - changelog = null - ), - isOtherChannel = true - ), - UpdateCardState.Error(UpdateErrorType.NO_SD_CARD), - UpdateCardState.Error(UpdateErrorType.NO_INTERNET), - UpdateCardState.Error(UpdateErrorType.UNABLE_TO_SERVER) - ) - Column(Modifier.verticalScroll(rememberScrollState())) { - cardStates.forEach { - ComposableFirmwareUpdaterContent( - lastVersion, - updateCardState = it, - onSelectFirmwareChannel = {} - ) {} - Spacer(modifier = Modifier.height(2.dp)) - } - } - } + ComposableUpdateButton( + updateCardState = updateCardState, + inProgress = inProgress, + onStartUpdateRequest = onStartUpdateRequest, + updateRequestViewModel = updateRequestViewModel + ) } diff --git a/components/updater/card/src/main/java/com/flipperdevices/updater/card/composable/ComposableUpdateButton.kt b/components/updater/card/src/main/java/com/flipperdevices/updater/card/composable/ComposableUpdateButton.kt index 1d4823d275..156bb38406 100644 --- a/components/updater/card/src/main/java/com/flipperdevices/updater/card/composable/ComposableUpdateButton.kt +++ b/components/updater/card/src/main/java/com/flipperdevices/updater/card/composable/ComposableUpdateButton.kt @@ -28,11 +28,13 @@ import com.flipperdevices.core.ui.theme.LocalTypography import com.flipperdevices.updater.card.R import com.flipperdevices.updater.card.composable.dialogs.ComposableUpdateRequest import com.flipperdevices.updater.card.model.UpdatePending +import com.flipperdevices.updater.card.viewmodel.UpdateRequestViewModel import com.flipperdevices.updater.model.UpdateCardState import com.flipperdevices.updater.model.UpdateRequest @Composable fun ComposableUpdateButton( + updateRequestViewModel: UpdateRequestViewModel, updateCardState: UpdateCardState, inProgress: Boolean, modifier: Modifier = Modifier, @@ -46,7 +48,11 @@ fun ComposableUpdateButton( var pendingUpdateRequest by remember { mutableStateOf(null) } val localUpdaterRequest = pendingUpdateRequest if (localUpdaterRequest != null) { - ComposableUpdateRequest(pendingUpdateRequest = localUpdaterRequest, onStartUpdateRequest) { + ComposableUpdateRequest( + pendingUpdateRequest = localUpdaterRequest, + onStartUpdateRequest = onStartUpdateRequest, + updateRequestViewModel = updateRequestViewModel + ) { pendingUpdateRequest = null } } @@ -60,11 +66,13 @@ fun ComposableUpdateButton( descriptionId = R.string.updater_card_updater_button_no_updates_desc, color = LocalPallet.current.text20 ) + is UpdateCardState.UpdateFromFile -> ComposableUpdateButtonContentChooseFile( modifier = buttonModifier, updateCardState = updateCardState, onChoose = { pendingUpdateRequest = it } ) + is UpdateCardState.UpdateAvailable -> { buttonModifier = buttonModifier.clickableRipple { pendingUpdateRequest = UpdatePending.Request(updateCardState.update) diff --git a/components/updater/card/src/main/java/com/flipperdevices/updater/card/composable/ComposableUpdateButtonPreview.kt b/components/updater/card/src/main/java/com/flipperdevices/updater/card/composable/ComposableUpdateButtonPreview.kt deleted file mode 100644 index 2f00698478..0000000000 --- a/components/updater/card/src/main/java/com/flipperdevices/updater/card/composable/ComposableUpdateButtonPreview.kt +++ /dev/null @@ -1,55 +0,0 @@ -package com.flipperdevices.updater.card.composable - -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import com.flipperdevices.core.ui.theme.FlipperThemeInternal -import com.flipperdevices.core.ui.theme.LocalPallet -import com.flipperdevices.updater.model.DistributionFile -import com.flipperdevices.updater.model.FirmwareChannel -import com.flipperdevices.updater.model.FirmwareVersion -import com.flipperdevices.updater.model.OfficialFirmware -import com.flipperdevices.updater.model.UpdateCardState -import com.flipperdevices.updater.model.UpdateRequest - -@Preview( - showBackground = true, - showSystemUi = true -) -@Composable -private fun ComposableUpdateButtonPreview() { - FlipperThemeInternal { - val version = FirmwareVersion( - channel = FirmwareChannel.RELEASE, - version = "1.1.1" - ) - val updateCardState = setOf( - UpdateCardState.NoUpdate(flipperVersion = version), - UpdateCardState.UpdateAvailable( - update = UpdateRequest( - updateFrom = version, - updateTo = version, - content = OfficialFirmware(DistributionFile(url = "", sha256 = "")), - changelog = null - ), - isOtherChannel = false - ) - ) - Column( - modifier = Modifier - .fillMaxSize() - .padding(12.dp) - .background(LocalPallet.current.background) - ) { - updateCardState.forEach { - ComposableUpdateButton(it, false, onStartUpdateRequest = {}) - ComposableUpdateButton(it, true, onStartUpdateRequest = {}) - } - } - } -} diff --git a/components/updater/card/src/main/java/com/flipperdevices/updater/card/composable/ComposableUpdaterCard.kt b/components/updater/card/src/main/java/com/flipperdevices/updater/card/composable/ComposableUpdaterCard.kt index 071ab49880..bc0d7d73b4 100644 --- a/components/updater/card/src/main/java/com/flipperdevices/updater/card/composable/ComposableUpdaterCard.kt +++ b/components/updater/card/src/main/java/com/flipperdevices/updater/card/composable/ComposableUpdaterCard.kt @@ -1,30 +1,19 @@ package com.flipperdevices.updater.card.composable -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import com.flipperdevices.core.ui.theme.FlipperThemeInternal import com.flipperdevices.info.shared.InfoElementCard import com.flipperdevices.updater.card.R import com.flipperdevices.updater.card.composable.dialogs.ComposableFailedUpdate import com.flipperdevices.updater.card.composable.dialogs.ComposableSuccessfulUpdate import com.flipperdevices.updater.card.viewmodel.UpdateCardViewModel +import com.flipperdevices.updater.card.viewmodel.UpdateRequestViewModel import com.flipperdevices.updater.card.viewmodel.UpdateStateViewModel -import com.flipperdevices.updater.model.DistributionFile import com.flipperdevices.updater.model.FirmwareChannel -import com.flipperdevices.updater.model.FirmwareVersion import com.flipperdevices.updater.model.FlipperUpdateState -import com.flipperdevices.updater.model.OfficialFirmware import com.flipperdevices.updater.model.UpdateCardState -import com.flipperdevices.updater.model.UpdateErrorType import com.flipperdevices.updater.model.UpdateRequest @Composable @@ -32,6 +21,7 @@ import com.flipperdevices.updater.model.UpdateRequest internal fun ComposableUpdaterCardInternal( updateStateViewModel: UpdateStateViewModel, updateCardViewModel: UpdateCardViewModel, + updateRequestViewModel: UpdateRequestViewModel, modifier: Modifier = Modifier, onStartUpdateRequest: (UpdateRequest) -> Unit = {}, ) { @@ -70,11 +60,13 @@ internal fun ComposableUpdaterCardInternal( onSelectChannel = updateCardViewModel::onSelectChannel, retryUpdate = updateCardViewModel::refresh, onStartUpdateRequest = onStartUpdateRequest, + updateRequestViewModel = updateRequestViewModel ) } @Composable private fun ComposableUpdaterCard( + updateRequestViewModel: UpdateRequestViewModel, cardStateLocal: UpdateCardState, modifier: Modifier = Modifier, onSelectChannel: (FirmwareChannel) -> Unit = {}, @@ -95,71 +87,34 @@ private fun ComposableUpdaterCard( version = null, updateCardState = cardStateLocal, onSelectFirmwareChannel = onSelectChannel, - onStartUpdateRequest = onStartUpdateRequest + onStartUpdateRequest = onStartUpdateRequest, + updateRequestViewModel = updateRequestViewModel + ) is UpdateCardState.NoUpdate -> ComposableFirmwareUpdaterContent( version = cardStateLocal.flipperVersion, updateCardState = cardStateLocal, onSelectFirmwareChannel = onSelectChannel, - onStartUpdateRequest = onStartUpdateRequest + onStartUpdateRequest = onStartUpdateRequest, + updateRequestViewModel = updateRequestViewModel ) is UpdateCardState.UpdateAvailable -> ComposableFirmwareUpdaterContent( version = cardStateLocal.update.updateTo, updateCardState = cardStateLocal, onSelectFirmwareChannel = onSelectChannel, - onStartUpdateRequest = onStartUpdateRequest + onStartUpdateRequest = onStartUpdateRequest, + updateRequestViewModel = updateRequestViewModel ) is UpdateCardState.UpdateFromFile -> ComposableFirmwareUpdaterContent( version = cardStateLocal.updateVersion, updateCardState = cardStateLocal, onSelectFirmwareChannel = onSelectChannel, - onStartUpdateRequest = onStartUpdateRequest + onStartUpdateRequest = onStartUpdateRequest, + updateRequestViewModel = updateRequestViewModel ) } } } - -@Preview -@Composable -private fun ComposableUpdaterCardPreview() { - FlipperThemeInternal { - val lastVersion = FirmwareVersion( - channel = FirmwareChannel.DEV, - version = "1.4.5" - ) - val cardStates = listOf( - UpdateCardState.InProgress, - UpdateCardState.NoUpdate(lastVersion), - UpdateCardState.UpdateAvailable( - update = UpdateRequest( - updateFrom = lastVersion, - updateTo = lastVersion, - content = OfficialFirmware(DistributionFile(url = "", sha256 = "")), - changelog = null - ), - isOtherChannel = false - ), - UpdateCardState.UpdateAvailable( - update = UpdateRequest( - updateFrom = lastVersion, - updateTo = lastVersion, - content = OfficialFirmware(DistributionFile(url = "", sha256 = "")), - changelog = null - ), - isOtherChannel = true - ), - UpdateCardState.Error(UpdateErrorType.NO_SD_CARD), - UpdateCardState.Error(UpdateErrorType.NO_INTERNET), - UpdateCardState.Error(UpdateErrorType.UNABLE_TO_SERVER) - ) - Column(Modifier.verticalScroll(rememberScrollState())) { - cardStates.forEach { - ComposableUpdaterCard(it) - Spacer(modifier = Modifier.height(2.dp)) - } - } - } -} diff --git a/components/updater/card/src/main/java/com/flipperdevices/updater/card/composable/dialogs/ComposableUpdateRequest.kt b/components/updater/card/src/main/java/com/flipperdevices/updater/card/composable/dialogs/ComposableUpdateRequest.kt index 6c84081b98..cc17fbc78f 100644 --- a/components/updater/card/src/main/java/com/flipperdevices/updater/card/composable/dialogs/ComposableUpdateRequest.kt +++ b/components/updater/card/src/main/java/com/flipperdevices/updater/card/composable/dialogs/ComposableUpdateRequest.kt @@ -9,13 +9,12 @@ import com.flipperdevices.updater.card.model.UpdatePending import com.flipperdevices.updater.card.model.UpdatePendingState import com.flipperdevices.updater.card.viewmodel.UpdateRequestViewModel import com.flipperdevices.updater.model.UpdateRequest -import tangle.viewmodel.compose.tangleViewModel @Composable fun ComposableUpdateRequest( pendingUpdateRequest: UpdatePending, onStartUpdateRequest: (UpdateRequest) -> Unit, - updateRequestViewModel: UpdateRequestViewModel = tangleViewModel(), + updateRequestViewModel: UpdateRequestViewModel, onDismiss: () -> Unit ) { val updatePendingState by updateRequestViewModel.getUpdatePendingState().collectAsState() diff --git a/components/updater/card/src/main/java/com/flipperdevices/updater/card/viewmodel/UpdateCardViewModel.kt b/components/updater/card/src/main/java/com/flipperdevices/updater/card/viewmodel/UpdateCardViewModel.kt index d8edaa0107..3c85c77d01 100644 --- a/components/updater/card/src/main/java/com/flipperdevices/updater/card/viewmodel/UpdateCardViewModel.kt +++ b/components/updater/card/src/main/java/com/flipperdevices/updater/card/viewmodel/UpdateCardViewModel.kt @@ -12,7 +12,6 @@ import com.flipperdevices.core.preference.pb.SelectedChannel import com.flipperdevices.core.preference.pb.Settings import com.flipperdevices.core.ui.lifecycle.LifecycleViewModel import com.flipperdevices.deeplink.model.Deeplink -import com.flipperdevices.deeplink.model.DeeplinkConstants import com.flipperdevices.updater.api.DownloaderApi import com.flipperdevices.updater.api.FlipperVersionProviderApi import com.flipperdevices.updater.card.helpers.StorageExistHelper @@ -20,6 +19,9 @@ import com.flipperdevices.updater.card.helpers.UpdateCardHelper import com.flipperdevices.updater.card.helpers.UpdateOfferProviderApi import com.flipperdevices.updater.model.FirmwareChannel import com.flipperdevices.updater.model.UpdateCardState +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.async @@ -30,19 +32,16 @@ import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.combine import kotlinx.coroutines.launch import kotlinx.coroutines.sync.Mutex -import tangle.inject.TangleParam -import tangle.viewmodel.VMInject @Suppress("LongParameterList") -class UpdateCardViewModel @VMInject constructor( +class UpdateCardViewModel @AssistedInject constructor( private val downloaderApi: DownloaderApi, private val flipperVersionProviderApi: FlipperVersionProviderApi, private val serviceProvider: FlipperServiceProvider, private val dataStoreSettings: DataStore, private val updateOfferHelper: UpdateOfferProviderApi, private val storageExistHelper: StorageExistHelper, - @TangleParam(DeeplinkConstants.KEY) - private val deeplink: Deeplink? + @Assisted private val deeplink: Deeplink? ) : LifecycleViewModel(), FlipperBleServiceConsumer, LogTagProvider { @@ -81,6 +80,7 @@ class UpdateCardViewModel @VMInject constructor( .setSelectedChannel(channel.toSelectedChannel()) .build() } + else -> {} } } @@ -146,6 +146,13 @@ class UpdateCardViewModel @VMInject constructor( } } } + + @AssistedFactory + fun interface Factory { + operator fun invoke( + deeplink: Deeplink? + ): UpdateCardViewModel + } } private fun SelectedChannel.toFirmwareChannel(): FirmwareChannel? = when (this) { @@ -161,5 +168,6 @@ private fun FirmwareChannel?.toSelectedChannel(): SelectedChannel = when (this) FirmwareChannel.DEV -> SelectedChannel.DEV FirmwareChannel.UNKNOWN, FirmwareChannel.CUSTOM -> error("Can`t convert unknown firmware channel to internal channel") + null -> SelectedChannel.UNRECOGNIZED } diff --git a/components/updater/card/src/main/java/com/flipperdevices/updater/card/viewmodel/UpdateRequestViewModel.kt b/components/updater/card/src/main/java/com/flipperdevices/updater/card/viewmodel/UpdateRequestViewModel.kt index dbb20feadb..628019f3c6 100644 --- a/components/updater/card/src/main/java/com/flipperdevices/updater/card/viewmodel/UpdateRequestViewModel.kt +++ b/components/updater/card/src/main/java/com/flipperdevices/updater/card/viewmodel/UpdateRequestViewModel.kt @@ -24,13 +24,13 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch -import tangle.viewmodel.VMInject import java.io.File +import javax.inject.Inject private const val EXT_UPDATER_FILE = "tgz" private const val SIZE_FOLDER_UPDATE_MAX = 1024L * 1024L * 1024L * 10 // 10Mb -class UpdateRequestViewModel @VMInject constructor( +class UpdateRequestViewModel @Inject constructor( serviceProvider: FlipperServiceProvider, private val synchronizationApi: SynchronizationApi ) : LifecycleViewModel(), FlipperBleServiceConsumer { diff --git a/components/updater/card/src/main/java/com/flipperdevices/updater/card/viewmodel/UpdateStateViewModel.kt b/components/updater/card/src/main/java/com/flipperdevices/updater/card/viewmodel/UpdateStateViewModel.kt index 14b14d5012..c68b3f9ca4 100644 --- a/components/updater/card/src/main/java/com/flipperdevices/updater/card/viewmodel/UpdateStateViewModel.kt +++ b/components/updater/card/src/main/java/com/flipperdevices/updater/card/viewmodel/UpdateStateViewModel.kt @@ -16,9 +16,9 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach -import tangle.viewmodel.VMInject +import javax.inject.Inject -class UpdateStateViewModel @VMInject constructor( +class UpdateStateViewModel @Inject constructor( serviceProvider: FlipperServiceProvider, private val updaterApi: UpdaterApi, private val metricApi: MetricApi, diff --git a/config/detekt/detekt.yml b/config/detekt/detekt.yml index b2ca0768c5..5bd67690ee 100755 --- a/config/detekt/detekt.yml +++ b/config/detekt/detekt.yml @@ -776,6 +776,6 @@ Compose: # You can optionally add your own ViewModel factories here # viewModelFactories: hiltViewModel,potatoViewModel ViewModelForwarding: - active: true + active: false ModifierNotUsedAtRoot: active: true \ No newline at end of file