Skip to content

Commit

Permalink
convert faphub module into new api
Browse files Browse the repository at this point in the history
  • Loading branch information
makeevrserg committed Dec 18, 2024
1 parent 0d533fa commit 234b6a1
Show file tree
Hide file tree
Showing 17 changed files with 204 additions and 68 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ commonDependencies {
implementation(projects.components.bridge.connection.feature.getinfo.api)
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(libs.kotlin.coroutines)
implementation(libs.kotlin.immutable.collections)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.flipperdevices.bridge.connection.device.fzero.impl.utils

import com.flipperdevices.bridge.connection.feature.alarm.api.FAlarmFeatureApi
import com.flipperdevices.bridge.connection.feature.appstart.api.FAppStartFeatureApi
import com.flipperdevices.bridge.connection.feature.common.api.FDeviceFeature
import com.flipperdevices.bridge.connection.feature.common.api.FDeviceFeatureApi
import com.flipperdevices.bridge.connection.feature.devicecolor.api.FDeviceColorFeatureApi
Expand All @@ -23,6 +24,7 @@ object FZeroFeatureClassToEnumMapper {
private val classToEnumMap: ImmutableMap<KClass<out FDeviceFeatureApi>, FDeviceFeature> =
FDeviceFeature.entries.associateBy { featureEnumToClass(it) }.toPersistentMap()

@Suppress("CyclomaticComplexMethod")
private fun featureEnumToClass(feature: FDeviceFeature): KClass<out FDeviceFeatureApi> {
return when (feature) {
FDeviceFeature.RPC -> FRpcFeatureApi::class
Expand All @@ -38,6 +40,7 @@ object FZeroFeatureClassToEnumMapper {
FDeviceFeature.DEVICE_COLOR -> FDeviceColorFeatureApi::class
FDeviceFeature.GATT_INFO -> FGattInfoFeatureApi::class
FDeviceFeature.SDK_VERSION -> FSdkVersionFeatureApi::class
FDeviceFeature.APP_START -> FAppStartFeatureApi::class
}
}

Expand Down
19 changes: 19 additions & 0 deletions components/bridge/connection/feature/appstart/api/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
plugins {
id("flipper.multiplatform")
id("flipper.multiplatform-dependencies")
}

android.namespace = "com.flipperdevices.bridge.connection.feature.appstart.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(libs.kotlin.immutable.collections)
implementation(libs.kotlin.coroutines)
implementation(libs.okio)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.flipperdevices.bridge.connection.feature.appstart.api

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

interface FAppStartFeatureApi : FDeviceFeatureApi {
suspend fun startApp(path: okio.Path): Result<Unit>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
plugins {
id("flipper.multiplatform")
id("flipper.multiplatform-dependencies")
id("flipper.anvil-multiplatform")
}

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

commonDependencies {
implementation(projects.components.bridge.connection.feature.appstart.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)
implementation(libs.okio)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.flipperdevices.bridge.connection.feature.appstart.impl.api

import com.flipperdevices.bridge.connection.feature.appstart.api.FAppStartFeatureApi
import com.flipperdevices.bridge.connection.feature.rpc.api.FRpcFeatureApi
import com.flipperdevices.bridge.connection.feature.rpc.model.wrapToRequest
import com.flipperdevices.core.log.LogTagProvider
import com.flipperdevices.protobuf.Main
import com.flipperdevices.protobuf.app.StartRequest
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import okio.Path

class FAppStartFeatureApiImpl @AssistedInject constructor(
@Assisted private val rpcFeatureApi: FRpcFeatureApi,
) : FAppStartFeatureApi,
LogTagProvider {
override val TAG = "FAlarmFeatureApi"

override suspend fun startApp(path: Path): Result<Unit> {
return rpcFeatureApi.requestOnce(
Main(
app_start_request = StartRequest(path.toString())
).wrapToRequest()
).map { }
}

@AssistedFactory
fun interface InternalFactory {
operator fun invoke(
rpcFeatureApi: FRpcFeatureApi,
): FAppStartFeatureApiImpl
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.flipperdevices.bridge.connection.feature.appstart.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.error
import com.flipperdevices.core.log.info
import com.squareup.anvil.annotations.ContributesMultibinding
import kotlinx.coroutines.CoroutineScope
import javax.inject.Inject

// TODO move to consts
val API_SUPPORTED_LOAD_FAP = SemVer(
majorVersion = 0,
minorVersion = 18
)

@FDeviceFeatureQualifier(FDeviceFeature.APP_START)
@ContributesMultibinding(AppGraph::class, FDeviceFeatureApi.Factory::class)
class FAppStartFeatureFactoryImpl @Inject constructor(
private val factory: FAppStartFeatureApiImpl.InternalFactory
) : FDeviceFeatureApi.Factory {
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_LOAD_FAP" }
val isSupported = versionApi.isSupported(API_SUPPORTED_LOAD_FAP)
if (!isSupported) {
error { "Failed init FDeviceColorFeatureApi, because isSupported=false" }
return null
}
val rpcApi = unsafeFeatureDeviceApi.getUnsafe(FRpcFeatureApi::class) ?: return null
return factory(
rpcFeatureApi = rpcApi,
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ enum class FDeviceFeature {
ALARM,
DEVICE_COLOR,
GATT_INFO,
SDK_VERSION
SDK_VERSION,
APP_START
}

@Retention(AnnotationRetention.RUNTIME)
Expand Down
14 changes: 11 additions & 3 deletions components/faphub/installation/button/impl/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,16 @@ dependencies {
implementation(projects.components.rootscreen.api)
implementation(projects.components.deeplink.api)

implementation(projects.components.bridge.api)
implementation(projects.components.bridge.service.api)
implementation(projects.components.bridge.pbutils)
// implementation(projects.components.bridge.api)
// implementation(projects.components.bridge.service.api)
// implementation(projects.components.bridge.pbutils)
implementation(projects.components.bridge.connection.feature.common.api)
implementation(projects.components.bridge.connection.feature.rpcinfo.api)
implementation(projects.components.bridge.connection.feature.provider.api)
implementation(projects.components.bridge.connection.feature.storage.api)
implementation(projects.components.bridge.connection.feature.protocolversion.api)
implementation(projects.components.bridge.connection.feature.appstart.api)
implementation(projects.components.bridge.connection.feature.rpc.api)

implementation(projects.components.faphub.installation.stateprovider.api)
implementation(projects.components.faphub.installation.manifest.api)
Expand All @@ -43,4 +50,5 @@ dependencies {

implementation(libs.kotlin.coroutines)
implementation(libs.lifecycle.viewmodel.ktx)
implementation(libs.okio)
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package com.flipperdevices.faphub.installation.button.impl.helper

import com.flipperdevices.bridge.api.model.FlipperRequestPriority
import com.flipperdevices.bridge.api.model.wrapToRequest
import com.flipperdevices.bridge.api.utils.Constants
import com.flipperdevices.bridge.service.api.provider.FlipperServiceProvider
import com.flipperdevices.core.data.SemVer
import com.flipperdevices.bridge.connection.feature.appstart.api.FAppStartFeatureApi
import com.flipperdevices.bridge.connection.feature.provider.api.FFeatureProvider
import com.flipperdevices.bridge.connection.feature.provider.api.FFeatureStatus
import com.flipperdevices.bridge.connection.feature.provider.api.get
import com.flipperdevices.bridge.connection.feature.provider.api.getSync
import com.flipperdevices.bridge.connection.feature.rpc.api.exception.FRpcAppSystemLockedException
import com.flipperdevices.core.di.AppGraph
import com.flipperdevices.core.ktx.jre.FlipperDispatchers
import com.flipperdevices.core.log.LogTagProvider
Expand All @@ -13,18 +14,13 @@ import com.flipperdevices.faphub.dao.api.model.FapBuildState
import com.flipperdevices.faphub.installation.button.api.FapButtonConfig
import com.flipperdevices.faphub.installation.button.impl.model.OpenFapResult
import com.flipperdevices.faphub.installation.button.impl.model.OpenFapState
import com.flipperdevices.protobuf.Flipper
import com.flipperdevices.protobuf.app.startRequest
import com.flipperdevices.protobuf.main
import com.squareup.anvil.annotations.ContributesBinding
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.launch
import okio.Path.Companion.toPath
import javax.inject.Inject
import javax.inject.Singleton

Expand All @@ -39,43 +35,32 @@ interface OpenFapHelper {
@Singleton
@ContributesBinding(AppGraph::class, OpenFapHelper::class)
class OpenFapHelperImpl @Inject constructor(
private val serviceProvider: FlipperServiceProvider
private val fFeatureProvider: FFeatureProvider
) : OpenFapHelper, LogTagProvider {
override val TAG: String = "OpenFapHelperImpl"

private val currentOpenAppFlow = MutableStateFlow<FapButtonConfig?>(null)
private val rpcVersionFlow = MutableStateFlow<SemVer?>(null)

private val scope = CoroutineScope(SupervisorJob() + FlipperDispatchers.workStealingDispatcher)

override fun getOpenFapState(fapButtonConfig: FapButtonConfig?): Flow<OpenFapState> {
return combine(
currentOpenAppFlow,
rpcVersionFlow,
) { currentApp, rpcVersion ->
return@combine when {
fapButtonConfig == null -> OpenFapState.NotSupported
rpcVersion == null || rpcVersion < Constants.API_SUPPORTED_LOAD_FAP ->
OpenFapState.NotSupported
flow = fFeatureProvider.get<FAppStartFeatureApi>(),
flow2 = currentOpenAppFlow,
transform = { status, currentApp ->
when {
status is FFeatureStatus.Unsupported -> OpenFapState.NotSupported
status is FFeatureStatus.NotFound -> OpenFapState.NotSupported
fapButtonConfig == null -> OpenFapState.NotSupported

fapButtonConfig.version.buildState != FapBuildState.READY ->
OpenFapState.NotSupported
fapButtonConfig.version.buildState != FapBuildState.READY -> {
OpenFapState.NotSupported
}

currentApp != null ->
OpenFapState.InProgress(currentApp)
currentApp != null -> OpenFapState.InProgress(currentApp)

else -> OpenFapState.Ready
else -> OpenFapState.Ready
}
}
}
}

init {
scope.launch(FlipperDispatchers.workStealingDispatcher) {
val serviceApi = serviceProvider.getServiceApi()
serviceApi.flipperVersionApi.getVersionInformationFlow().collectLatest {
rpcVersionFlow.emit(it)
}
}
)
}

override suspend fun loadFap(
Expand All @@ -87,25 +72,32 @@ class OpenFapHelperImpl @Inject constructor(
return
}

val path = "${Constants.PATH.APPS}${config.categoryAlias}/${config.applicationAlias}.fap"
val serviceApi = serviceProvider.getServiceApi()

val appLoadResponse = serviceApi.requestApi.request(
flowOf(
main {
appStartRequest = startRequest {
name = path
}
}.wrapToRequest(FlipperRequestPriority.FOREGROUND)
)
)

val result: OpenFapResult = when (appLoadResponse.commandStatus) {
Flipper.CommandStatus.OK -> OpenFapResult.AllGood
Flipper.CommandStatus.ERROR_APP_SYSTEM_LOCKED -> OpenFapResult.FlipperIsBusy
else -> OpenFapResult.Error
val path = APPS_PATH
.resolve(config.categoryAlias)
.resolve(config.applicationAlias.plus(".fap"))
val fAppStartFeatureApi = fFeatureProvider.getSync<FAppStartFeatureApi>()
if (fAppStartFeatureApi == null) {
currentOpenAppFlow.emit(null)
onResult(OpenFapResult.Error)
return
}

currentOpenAppFlow.emit(null)
onResult(result)
fAppStartFeatureApi.startApp(path)
.onFailure {
val result = when (it) {
is FRpcAppSystemLockedException -> OpenFapResult.FlipperIsBusy
else -> OpenFapResult.Error
}
onResult(result)
}
.onSuccess {
onResult(OpenFapResult.AllGood)
}
}

companion object {
// TODO move somewhere else
private val APPS_PATH = "/ext/apps/".toPath()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class FapExistChecker @Inject constructor(
?.ls(folder)
?.getOrNull()
.orEmpty()
.map { item-> Path(folder).resolve(item.fileName).toString() }
.map { item -> Path(folder).resolve(item.fileName).toString() }
.also { paths -> cacheFolderToPaths[folder] = paths }

return@withLockResult fileList.contains(path)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import com.flipperdevices.core.log.error
import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.withContext
import okio.Path
import okio.Path.Companion.toPath
import java.io.File
import javax.inject.Inject
Expand Down Expand Up @@ -38,7 +37,6 @@ class FapManifestAtomicMover @Inject constructor(

val deleteTargets = fromToPair.map { pair -> pair.second }


withContext(NonCancellable) {
if (fSemVer == null || fSemVer < UNIX_MV_SUPPORTED_VERSION_API) {
deleteTargets.map { target ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import com.flipperdevices.core.log.info
import com.flipperdevices.faphub.installation.manifest.model.FapManifestItem
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.withContext
import okio.Path
import java.io.File
import javax.inject.Inject
import javax.inject.Singleton
Expand Down
Loading

0 comments on commit 234b6a1

Please sign in to comment.