Skip to content

Commit

Permalink
Migrate screenstreaming to new api (#1004)
Browse files Browse the repository at this point in the history
**Background**

Migrate screenstreaming to new api

**Changes**

- Migrated screenstreaming module
- Added screenstreaming feature module

**Test plan**

- Open screenstreaming in mobile app
- Close it
- See there's no evet subscription leaks
- See it's working as intended
  • Loading branch information
makeevrserg authored Dec 19, 2024
1 parent 2ca9625 commit e59392f
Show file tree
Hide file tree
Showing 21 changed files with 454 additions and 147 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ commonDependencies {
implementation(projects.components.bridge.connection.feature.alarm.api)
implementation(projects.components.bridge.connection.feature.deviceColor.api)
implementation(projects.components.bridge.connection.feature.appstart.api)
implementation(projects.components.bridge.connection.feature.screenstreaming.api)

implementation(libs.kotlin.coroutines)
implementation(libs.kotlin.immutable.collections)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import com.flipperdevices.bridge.connection.feature.protocolversion.api.FVersion
import com.flipperdevices.bridge.connection.feature.restartrpc.api.FRestartRpcFeatureApi
import com.flipperdevices.bridge.connection.feature.rpc.api.FRpcFeatureApi
import com.flipperdevices.bridge.connection.feature.rpcinfo.api.FRpcInfoFeatureApi
import com.flipperdevices.bridge.connection.feature.screenstreaming.api.FScreenStreamingFeatureApi
import com.flipperdevices.bridge.connection.feature.screenstreaming.api.FScreenUnlockFeatureApi
import com.flipperdevices.bridge.connection.feature.seriallagsdetector.api.FLagsDetectorFeature
import com.flipperdevices.bridge.connection.feature.serialspeed.api.FSpeedFeatureApi
import com.flipperdevices.bridge.connection.feature.storage.api.FStorageFeatureApi
Expand Down Expand Up @@ -41,6 +43,8 @@ object FZeroFeatureClassToEnumMapper {
FDeviceFeature.GATT_INFO -> FGattInfoFeatureApi::class
FDeviceFeature.SDK_VERSION -> FSdkVersionFeatureApi::class
FDeviceFeature.APP_START -> FAppStartFeatureApi::class
FDeviceFeature.SCREEN_STREAMING -> FScreenStreamingFeatureApi::class
FDeviceFeature.SCREEN_UNLOCK -> FScreenUnlockFeatureApi::class
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ enum class FDeviceFeature {
DEVICE_COLOR,
GATT_INFO,
SDK_VERSION,
APP_START
APP_START,
SCREEN_STREAMING,
SCREEN_UNLOCK
}

@Retention(AnnotationRetention.RUNTIME)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
plugins {
id("flipper.multiplatform")
id("flipper.multiplatform-dependencies")
}

android.namespace = "com.flipperdevices.bridge.connection.feature.screenstreaming.api"

commonDependencies {
implementation(projects.components.core.data)
implementation(projects.components.core.ktx)

implementation(projects.components.bridge.connection.feature.common.api)
implementation(projects.components.bridge.connection.transport.common.api)
implementation(projects.components.bridge.connection.feature.rpcinfo.api)

implementation(projects.components.bridge.connection.pbutils)

implementation(libs.kotlin.immutable.collections)
implementation(libs.kotlin.coroutines)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.flipperdevices.bridge.connection.feature.screenstreaming.api

import com.flipperdevices.bridge.connection.feature.common.api.FDeviceFeatureApi
import com.flipperdevices.protobuf.Main
import com.flipperdevices.protobuf.screen.InputKey
import com.flipperdevices.protobuf.screen.InputType
import com.flipperdevices.protobuf.screen.ScreenFrame
import kotlinx.coroutines.flow.Flow

interface FScreenStreamingFeatureApi : FDeviceFeatureApi {
suspend fun awaitInput(inputKey: InputKey, inputType: InputType): Flow<Result<Main>>

suspend fun sendInputAndForget(inputKey: InputKey, inputType: InputType)

suspend fun stop(): Result<Main>

/**
* Start collect [ScreenFrame].
* Need to call [stop] to cancel data sending from flipper
*/
suspend fun guiScreenFrameFlow(): Flow<ScreenFrame>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.flipperdevices.bridge.connection.feature.screenstreaming.api

import com.flipperdevices.bridge.connection.feature.common.api.FDeviceFeatureApi
import com.flipperdevices.protobuf.Main

interface FScreenUnlockFeatureApi : FDeviceFeatureApi {
suspend fun unlock(): Result<Main>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
plugins {
id("flipper.multiplatform")
id("flipper.multiplatform-dependencies")
id("flipper.anvil-multiplatform")
}

android.namespace = "com.flipperdevices.bridge.connection.feature.screenstreaming.impl"

commonDependencies {
implementation(projects.components.bridge.connection.feature.screenstreaming.api)

implementation(projects.components.core.di)
implementation(projects.components.core.log)
implementation(projects.components.core.ktx)
implementation(projects.components.core.data)

implementation(projects.components.bridge.connection.feature.common.api)
implementation(projects.components.bridge.connection.transport.common.api)
implementation(projects.components.bridge.connection.feature.rpc.api)
implementation(projects.components.bridge.connection.feature.rpc.model)
implementation(projects.components.bridge.connection.feature.rpcinfo.api)
implementation(projects.components.bridge.connection.feature.protocolversion.api)

implementation(projects.components.bridge.connection.pbutils)

implementation(libs.kotlin.coroutines)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.flipperdevices.bridge.connection.feature.screenstreaming.impl.api

import com.flipperdevices.bridge.connection.feature.common.api.FDeviceFeature
import com.flipperdevices.bridge.connection.feature.common.api.FDeviceFeatureApi
import com.flipperdevices.bridge.connection.feature.common.api.FDeviceFeatureQualifier
import com.flipperdevices.bridge.connection.feature.common.api.FUnsafeDeviceFeatureApi
import com.flipperdevices.bridge.connection.feature.rpc.api.FRpcFeatureApi
import com.flipperdevices.bridge.connection.transport.common.api.FConnectedDeviceApi
import com.flipperdevices.core.di.AppGraph
import com.squareup.anvil.annotations.ContributesMultibinding
import kotlinx.coroutines.CoroutineScope
import javax.inject.Inject

@FDeviceFeatureQualifier(FDeviceFeature.SCREEN_STREAMING)
@ContributesMultibinding(AppGraph::class, FDeviceFeatureApi.Factory::class)
class FScreenStreamingFactoryImpl @Inject constructor(
private val factory: FScreenStreamingFeatureApiImpl.InternalFactory
) : FDeviceFeatureApi.Factory {
override suspend fun invoke(
unsafeFeatureDeviceApi: FUnsafeDeviceFeatureApi,
scope: CoroutineScope,
connectedDevice: FConnectedDeviceApi
): FDeviceFeatureApi? {
val rpcApi = unsafeFeatureDeviceApi.getUnsafe(FRpcFeatureApi::class) ?: return null
return factory(
rpcFeatureApi = rpcApi,
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package com.flipperdevices.bridge.connection.feature.screenstreaming.impl.api

import com.flipperdevices.bridge.connection.feature.rpc.api.FRpcFeatureApi
import com.flipperdevices.bridge.connection.feature.rpc.model.wrapToRequest
import com.flipperdevices.bridge.connection.feature.screenstreaming.api.FScreenStreamingFeatureApi
import com.flipperdevices.core.log.LogTagProvider
import com.flipperdevices.protobuf.Main
import com.flipperdevices.protobuf.screen.InputKey
import com.flipperdevices.protobuf.screen.InputType
import com.flipperdevices.protobuf.screen.ScreenFrame
import com.flipperdevices.protobuf.screen.SendInputEventRequest
import com.flipperdevices.protobuf.screen.StartScreenStreamRequest
import com.flipperdevices.protobuf.screen.StopScreenStreamRequest
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.mapNotNull

class FScreenStreamingFeatureApiImpl @AssistedInject constructor(
@Assisted private val rpcFeatureApi: FRpcFeatureApi,
) : FScreenStreamingFeatureApi,
LogTagProvider {
override val TAG = "FScreenStreamingFeatureApi"

override suspend fun sendInputAndForget(inputKey: InputKey, inputType: InputType) {
return rpcFeatureApi.requestWithoutAnswer(
Main(
gui_send_input_event_request = SendInputEventRequest(
key = inputKey,
type = inputType
)
).wrapToRequest()
)
}

override suspend fun awaitInput(inputKey: InputKey, inputType: InputType): Flow<Result<Main>> {
return rpcFeatureApi.request(
Main(
gui_send_input_event_request = SendInputEventRequest(
key = inputKey,
type = inputType
)
).wrapToRequest()
)
}

override suspend fun stop(): Result<Main> {
return rpcFeatureApi.requestOnce(
Main(
gui_stop_screen_stream_request = StopScreenStreamRequest()
).wrapToRequest()
)
}

override suspend fun guiScreenFrameFlow(): Flow<ScreenFrame> {
return rpcFeatureApi.request(
Main(
gui_start_screen_stream_request = StartScreenStreamRequest()
).wrapToRequest()
).mapNotNull { result -> result.getOrNull()?.gui_screen_frame }
}

@AssistedFactory
fun interface InternalFactory {
operator fun invoke(
rpcFeatureApi: FRpcFeatureApi,
): FScreenStreamingFeatureApiImpl
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.flipperdevices.bridge.connection.feature.screenstreaming.impl.api

import com.flipperdevices.bridge.connection.feature.common.api.FDeviceFeature
import com.flipperdevices.bridge.connection.feature.common.api.FDeviceFeatureApi
import com.flipperdevices.bridge.connection.feature.common.api.FDeviceFeatureQualifier
import com.flipperdevices.bridge.connection.feature.common.api.FUnsafeDeviceFeatureApi
import com.flipperdevices.bridge.connection.feature.protocolversion.api.FVersionFeatureApi
import com.flipperdevices.bridge.connection.feature.rpc.api.FRpcFeatureApi
import com.flipperdevices.bridge.connection.transport.common.api.FConnectedDeviceApi
import com.flipperdevices.core.data.SemVer
import com.flipperdevices.core.di.AppGraph
import com.flipperdevices.core.log.LogTagProvider
import com.flipperdevices.core.log.error
import com.flipperdevices.core.log.info
import com.squareup.anvil.annotations.ContributesMultibinding
import kotlinx.coroutines.CoroutineScope
import javax.inject.Inject

private val API_SUPPORTED_UNLOCK = SemVer(
majorVersion = 0,
minorVersion = 16
)

@FDeviceFeatureQualifier(FDeviceFeature.SCREEN_UNLOCK)
@ContributesMultibinding(AppGraph::class, FDeviceFeatureApi.Factory::class)
class FScreenUnlockFactoryImpl @Inject constructor(
private val factory: FScreenUnlockFeatureApiImpl.InternalFactory
) : FDeviceFeatureApi.Factory, LogTagProvider {
override val TAG: String = "FScreenUnlockFactoryImpl"

override suspend fun invoke(
unsafeFeatureDeviceApi: FUnsafeDeviceFeatureApi,
scope: CoroutineScope,
connectedDevice: FConnectedDeviceApi
): FDeviceFeatureApi? {
val versionApi = unsafeFeatureDeviceApi.getUnsafe(FVersionFeatureApi::class) ?: return null
info { "Start request supported state for api level $API_SUPPORTED_UNLOCK" }
val isSupported = versionApi.isSupported(API_SUPPORTED_UNLOCK)
if (!isSupported) {
error { "Failed init FScreenUnlockFeatureApi, because isSupported=false" }
return null
}
info { "Version $API_SUPPORTED_UNLOCK supported, so continue building FScreenUnlockFeatureApi" }

val rpcApi = unsafeFeatureDeviceApi.getUnsafe(FRpcFeatureApi::class) ?: return null
return factory(
rpcFeatureApi = rpcApi,
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.flipperdevices.bridge.connection.feature.screenstreaming.impl.api

import com.flipperdevices.bridge.connection.feature.rpc.api.FRpcFeatureApi
import com.flipperdevices.bridge.connection.feature.rpc.model.FlipperRequestPriority
import com.flipperdevices.bridge.connection.feature.rpc.model.wrapToRequest
import com.flipperdevices.bridge.connection.feature.screenstreaming.api.FScreenUnlockFeatureApi
import com.flipperdevices.core.log.LogTagProvider
import com.flipperdevices.protobuf.Main
import com.flipperdevices.protobuf.desktop.UnlockRequest
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject

class FScreenUnlockFeatureApiImpl @AssistedInject constructor(
@Assisted private val rpcFeatureApi: FRpcFeatureApi,
) : FScreenUnlockFeatureApi,
LogTagProvider {
override val TAG = "FScreenUnlockFeatureApi"

override suspend fun unlock(): Result<Main> {
return rpcFeatureApi.requestOnce(
Main(
desktop_unlock_request = UnlockRequest()
).wrapToRequest(FlipperRequestPriority.FOREGROUND)
)
}

@AssistedFactory
fun interface InternalFactory {
operator fun invoke(
rpcFeatureApi: FRpcFeatureApi,
): FScreenUnlockFeatureApiImpl
}
}
11 changes: 8 additions & 3 deletions components/screenstreaming/impl/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,15 @@ plugins {
android.namespace = "com.flipperdevices.screenstreaming.impl"

dependencies {
implementation(projects.components.bridge.api)
implementation(projects.components.bridge.service.api)
implementation(projects.components.screenstreaming.api)
implementation(projects.components.bridge.pbutils)

implementation(projects.components.bridge.connection.pbutils)
implementation(projects.components.bridge.connection.orchestrator.api)
implementation(projects.components.bridge.connection.feature.provider.api)
implementation(projects.components.bridge.connection.feature.common.api)
implementation(projects.components.bridge.connection.feature.screenstreaming.api)
implementation(projects.components.bridge.connection.feature.protocolversion.api)
implementation(projects.components.bridge.connection.feature.rpc.api)

implementation(projects.components.core.di)
implementation(projects.components.core.log)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,25 @@ package com.flipperdevices.screenstreaming.impl.composable

import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import com.flipperdevices.protobuf.screen.Gui
import com.flipperdevices.protobuf.screen.InputKey
import com.flipperdevices.screenstreaming.impl.R
import com.flipperdevices.screenstreaming.impl.model.ButtonAnimEnum

enum class ButtonEnum(
@DrawableRes val icon: Int,
@StringRes val description: Int,
val key: Gui.InputKey,
val key: InputKey,
val animEnum: ButtonAnimEnum
) {
LEFT(R.drawable.ic_control_left, R.string.control_left, Gui.InputKey.LEFT, ButtonAnimEnum.LEFT),
LEFT(R.drawable.ic_control_left, R.string.control_left, InputKey.LEFT, ButtonAnimEnum.LEFT),
RIGHT(
R.drawable.ic_control_right,
R.string.control_right,
Gui.InputKey.RIGHT,
InputKey.RIGHT,
ButtonAnimEnum.RIGHT
),
UP(R.drawable.ic_control_up, R.string.control_up, Gui.InputKey.UP, ButtonAnimEnum.UP),
DOWN(R.drawable.ic_control_down, R.string.control_down, Gui.InputKey.DOWN, ButtonAnimEnum.DOWN),
OK(R.drawable.ic_control_ok, R.string.control_ok, Gui.InputKey.OK, ButtonAnimEnum.OK),
BACK(R.drawable.ic_control_back, R.string.control_back, Gui.InputKey.BACK, ButtonAnimEnum.BACK)
UP(R.drawable.ic_control_up, R.string.control_up, InputKey.UP, ButtonAnimEnum.UP),
DOWN(R.drawable.ic_control_down, R.string.control_down, InputKey.DOWN, ButtonAnimEnum.DOWN),
OK(R.drawable.ic_control_ok, R.string.control_ok, InputKey.OK, ButtonAnimEnum.OK),
BACK(R.drawable.ic_control_back, R.string.control_back, InputKey.BACK, ButtonAnimEnum.BACK)
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import androidx.compose.ui.unit.dp
import com.flipperdevices.core.ui.dialog.composable.FlipperDialog
import com.flipperdevices.core.ui.ktx.OrangeAppBar
import com.flipperdevices.core.ui.theme.LocalPallet
import com.flipperdevices.protobuf.screen.Gui
import com.flipperdevices.protobuf.screen.InputType
import com.flipperdevices.screenstreaming.impl.R
import com.flipperdevices.screenstreaming.impl.composable.controls.ComposableFlipperControls
import com.flipperdevices.screenstreaming.impl.composable.screen.ComposableFlipperScreenWithOptions
Expand Down Expand Up @@ -78,10 +78,10 @@ fun ComposableStreamingScreen(
)
ComposableFlipperControls(
onPressButton = {
screenStreamingViewModel.onPressButton(it, Gui.InputType.SHORT)
screenStreamingViewModel.onPressButton(it, InputType.SHORT)
},
onLongPressButton = {
screenStreamingViewModel.onPressButton(it, Gui.InputType.LONG)
screenStreamingViewModel.onPressButton(it, InputType.LONG)
}
)
}
Expand Down
Loading

0 comments on commit e59392f

Please sign in to comment.