Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
- [Feature] Add count subfolders for new file manager
- [Feature] Add file downloading for new file manager
- [Feature] Add move-to to new file manager
- [Feature] Single tap for infrared remotes
- [Refactor] Move rename and file create to separated modules
- [Refactor] Improve and refactor new FileManager Editor
- [FIX] Migrate url host from metric.flipperdevices.com to metric.flipp.dev
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ object Constants {
majorVersion = 0,
minorVersion = 21
)
val API_SUPPORTED_INFRARED_PRESS_RELEASE = SemVer(
majorVersion = 0,
minorVersion = 25
)
val API_SUPPORTED_GET_REQUEST = API_SUPPORTED_FLIPPER_ERROR
const val LAGS_FLIPPER_DETECT_TIMEOUT_MS = 10 * 1000L // 10 seconds

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ private fun KeyScreenState.Ready.toEmulateConfigs(): ImmutableList<EmulateConfig
keyType = FlipperKeyType.INFRARED,
keyPath = this.flipperKey.path,
args = name,
index = index
index = index,
)
}.toImmutableList()
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@ interface EmulateHelper {

suspend fun stopEmulate(
scope: CoroutineScope,
requestApi: FlipperRequestApi
requestApi: FlipperRequestApi,
isPressRelease: Boolean = false
)

suspend fun stopEmulateForce(
requestApi: FlipperRequestApi
requestApi: FlipperRequestApi,
isPressRelease: Boolean = false
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,17 @@ import com.flipperdevices.bridge.dao.api.model.FlipperKeyType
/*
Override equals and hashCode to compare emulate key, because emulate time not important
*/
/**
* @param isPressRelease one-time emulate for infrared-only
*/
@Immutable
data class EmulateConfig(
val keyType: FlipperKeyType,
val keyPath: FlipperFilePath,
val minEmulateTime: Long? = null,
val args: String? = null,
val index: Int? = null
val index: Int? = null,
val isPressRelease: Boolean = false
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ private fun ComposableActiveStateEmulateInternal(

val buttonActiveModifier = Modifier.onScrollHoldPress(
onTap = {
emulateViewModel.onSinglePress(emulateConfig)
emulateViewModel.onSinglePress(emulateConfig.copy(isPressRelease = true))
},
onLongPressStart = {
emulateViewModel.onStartEmulate(emulateConfig)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ class EmulateHelperImpl @Inject constructor(

override suspend fun stopEmulate(
scope: CoroutineScope,
requestApi: FlipperRequestApi
requestApi: FlipperRequestApi,
isPressRelease: Boolean
) = withLock(mutex, "schedule_stop") {
if (stopJob != null) {
info { "Return from #stopEmulate because stop already in progress" }
Expand Down Expand Up @@ -111,17 +112,21 @@ class EmulateHelperImpl @Inject constructor(
}

override suspend fun stopEmulateForce(
requestApi: FlipperRequestApi
requestApi: FlipperRequestApi,
isPressRelease: Boolean
) = withLock(mutex, "force_stop") {
if (stopJob != null) {
stopJob?.cancelAndJoin()
stopJob = null
}
stopEmulateInternal(requestApi)
stopEmulateInternal(requestApi, isPressRelease)
}

private suspend fun stopEmulateInternal(requestApi: FlipperRequestApi) {
stopEmulateHelper.onStop(requestApi)
private suspend fun stopEmulateInternal(
requestApi: FlipperRequestApi,
isPressRelease: Boolean = false
) {
stopEmulateHelper.onStop(requestApi, isPressRelease)
currentKeyEmulating.emit(null)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import com.flipperdevices.keyemulate.model.EmulateConfig
import com.flipperdevices.keyemulate.model.FlipperAppError
import com.flipperdevices.protobuf.Flipper
import com.flipperdevices.protobuf.app.Application
import com.flipperdevices.protobuf.app.appButtonPressReleaseRequest
import com.flipperdevices.protobuf.app.appButtonPressRequest
import com.flipperdevices.protobuf.app.appLoadFileRequest
import com.flipperdevices.protobuf.main
Expand All @@ -32,6 +33,7 @@ private const val APP_RETRY_COUNT = 3
private const val APP_RETRY_SLEEP_TIME_MS = 1 * 1000L // 1 second

interface StartEmulateHelper {
@Suppress("LongParameterList")
suspend fun onStart(
scope: CoroutineScope,
serviceApi: FlipperServiceApi,
Expand Down Expand Up @@ -97,13 +99,17 @@ class StartEmulateHelperImpl @Inject constructor(
val indexEmulateSupported =
serviceApi.flipperVersionApi.isSupported(Constants.API_SUPPORTED_INFRARED_EMULATE)

val isPressReleaseSupported =
serviceApi.flipperVersionApi.isSupported(Constants.API_SUPPORTED_INFRARED_PRESS_RELEASE)

info { "Support emulate by index: $indexEmulateSupported" }

return processButtonPress(
config = config,
onResultTime = onResultTime,
serviceApi = serviceApi,
isIndexEmulateSupport = indexEmulateSupported
isIndexEmulateSupport = indexEmulateSupported,
isPressReleaseSupported = isPressReleaseSupported
)
}

Expand All @@ -115,6 +121,7 @@ class StartEmulateHelperImpl @Inject constructor(
private suspend fun processButtonPress(
config: EmulateConfig,
isIndexEmulateSupport: Boolean,
isPressReleaseSupported: Boolean,
onResultTime: (Long) -> Unit,
serviceApi: FlipperServiceApi
): Boolean {
Expand All @@ -125,7 +132,17 @@ class StartEmulateHelperImpl @Inject constructor(
val appButtonPressResponse = serviceApi.requestApi.request(
flowOf(
main {
appButtonPressRequest = getAppButtonPressRequest(config, isIndexEmulateSupport)
if (config.isPressRelease && isPressReleaseSupported) {
appButtonPressReleaseRequest = getAppButtonPressReleaseRequest(
config,
isIndexEmulateSupport
)
} else {
appButtonPressRequest = getAppButtonPressRequest(
config,
isIndexEmulateSupport
)
}
}.wrapToRequest(FlipperRequestPriority.FOREGROUND)
)
)
Expand Down Expand Up @@ -164,6 +181,29 @@ class StartEmulateHelperImpl @Inject constructor(
}
}

private fun getAppButtonPressReleaseRequest(
config: EmulateConfig,
isIndexEmulateSupport: Boolean,
): Application.AppButtonPressReleaseRequest {
return when (config.keyType) {
FlipperKeyType.INFRARED -> if (isIndexEmulateSupport) {
val indexArgs = config.index ?: error("Index args is null")
info { "#getAppButtonPressReleaseRequest by index with $config" }
appButtonPressReleaseRequest {
index = indexArgs
}
} else {
val configArgs = config.args ?: error("Config args is null")
info { "#getAppButtonPressReleaseRequest by args with $config" }
appButtonPressReleaseRequest {
args = configArgs
}
}

else -> error("#getAppButtonPressReleaseRequest Unknown button press request with config $config")
}
}

private fun getAppButtonPressRequest(
config: EmulateConfig,
isIndexEmulateSupport: Boolean,
Expand All @@ -183,6 +223,7 @@ class StartEmulateHelperImpl @Inject constructor(
args = configArgs
}
}

else -> error("Unknown button press request with config $config")
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,29 @@ import kotlinx.coroutines.flow.flowOf
import javax.inject.Inject

interface StopEmulateHelper {
suspend fun onStop(requestApi: FlipperRequestApi)
suspend fun onStop(
requestApi: FlipperRequestApi,
isPressRelease: Boolean = false
)
}

@ContributesBinding(AppGraph::class, StopEmulateHelper::class)
class StopEmulateHelperImpl @Inject constructor() : StopEmulateHelper, LogTagProvider {
override val TAG = "StopEmulateHelper"

override suspend fun onStop(requestApi: FlipperRequestApi) {
override suspend fun onStop(requestApi: FlipperRequestApi, isPressRelease: Boolean) {
info { "stopEmulateInternal" }

val appButtonResponse = requestApi.request(
flowOf(
main {
appButtonReleaseRequest = appButtonReleaseRequest { }
}.wrapToRequest(FlipperRequestPriority.FOREGROUND)
if (!isPressRelease) {
val appButtonResponse = requestApi.request(
flowOf(
main {
appButtonReleaseRequest = appButtonReleaseRequest { }
}.wrapToRequest(FlipperRequestPriority.FOREGROUND)
)
)
)
info { "App button stop response: $appButtonResponse" }
info { "App button stop response: $appButtonResponse" }
}

val appExitResponse = requestApi.request(
flowOf(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.flipperdevices.keyemulate.viewmodel

import android.app.Application
import androidx.datastore.core.DataStore
import com.flipperdevices.bridge.api.utils.Constants
import com.flipperdevices.bridge.dao.api.model.FlipperKeyType
import com.flipperdevices.bridge.service.api.FlipperServiceApi
import com.flipperdevices.bridge.service.api.provider.FlipperServiceProvider
Expand Down Expand Up @@ -116,6 +117,7 @@ class InfraredViewModel @Inject constructor(
emulateHelper.stopEmulate(viewModelScope, serviceApi.requestApi)
}

@Suppress("CyclomaticComplexMethod", "LongMethod")
private suspend fun calculateTimeoutAndStartEmulate(
scope: CoroutineScope,
serviceApi: FlipperServiceApi,
Expand All @@ -125,12 +127,13 @@ class InfraredViewModel @Inject constructor(
val requestApi = serviceApi.requestApi
val timeout = config.minEmulateTime
val appStarted: Boolean?

val isPressReleaseSupported =
serviceApi.flipperVersionApi.isSupported(Constants.API_SUPPORTED_INFRARED_PRESS_RELEASE)
try {
appStarted = emulateHelper.startEmulate(
scope,
serviceApi,
config
scope = scope,
serviceApi = serviceApi,
config = config,
)
if (appStarted && timeout != null) {
if (oneTimePress) {
Expand All @@ -150,22 +153,34 @@ class InfraredViewModel @Inject constructor(
}
}
} catch (ignored: AlreadyOpenedAppException) {
emulateHelper.stopEmulateForce(requestApi)
emulateHelper.stopEmulateForce(
requestApi = requestApi,
isPressRelease = isPressReleaseSupported && oneTimePress
)
emulateButtonStateFlow.emit(EmulateButtonState.AppAlreadyOpenDialog)
return false
} catch (ignored: ForbiddenFrequencyException) {
emulateHelper.stopEmulateForce(requestApi)
emulateHelper.stopEmulateForce(
requestApi = requestApi,
isPressRelease = isPressReleaseSupported && oneTimePress
)
emulateButtonStateFlow.emit(EmulateButtonState.ForbiddenFrequencyDialog)
return false
} catch (fatal: Throwable) {
error(fatal) { "Handle fatal exception on emulate infrared" }
emulateHelper.stopEmulateForce(requestApi)
emulateHelper.stopEmulateForce(
requestApi = requestApi,
isPressRelease = isPressReleaseSupported && oneTimePress
)
emulateButtonStateFlow.emit(EmulateButtonState.Inactive())
return false
}
if (!appStarted) {
info { "Failed start emulation" }
emulateHelper.stopEmulateForce(requestApi)
emulateHelper.stopEmulateForce(
requestApi = requestApi,
isPressRelease = isPressReleaseSupported && oneTimePress
)
emulateButtonStateFlow.emit(EmulateButtonState.Inactive())
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ interface DispatchSignalApi : InstanceKeeper.Instance {
fun dispatch(
config: EmulateConfig,
identifier: IfrKeyIdentifier,
isOneTime: Boolean = true,
onDispatched: () -> Unit = {}
)

Expand Down
1 change: 1 addition & 0 deletions components/remote-controls/setup/impl/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ dependencies {
implementation(projects.components.core.di)
implementation(projects.components.core.ktx)
implementation(projects.components.core.log)
implementation(projects.components.core.data)

implementation(projects.components.core.ui.lifecycle)
implementation(projects.components.core.ui.theme)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,8 @@ class SetupComponentImpl @AssistedInject constructor(
),
keyType = FlipperKeyType.INFRARED,
args = signalModel.remote.name,
index = 0
index = 0,
isPressRelease = true
)
val keyIdentifier = (loadedState.response.signalResponse?.data as? SingleKeyButtonData)
?.keyIdentifier
Expand Down
Loading
Loading