feat(infrared): add infrared control, learning, and virtual remote module #9
@@ -1,26 +0,0 @@
|
|||||||
package com.flagship.abox.manager.ui
|
|
||||||
|
|
||||||
import androidx.compose.ui.test.assertIsDisplayed
|
|
||||||
import androidx.compose.ui.test.junit4.createAndroidComposeRule
|
|
||||||
import androidx.compose.ui.test.onNodeWithTag
|
|
||||||
import androidx.compose.ui.test.onNodeWithText
|
|
||||||
import androidx.compose.ui.test.performClick
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
|
||||||
import com.flagship.abox.manager.MainActivity
|
|
||||||
import org.junit.Rule
|
|
||||||
import org.junit.Test
|
|
||||||
import org.junit.runner.RunWith
|
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
|
||||||
class ABoxManagerAppTest {
|
|
||||||
|
|
||||||
@get:Rule
|
|
||||||
val composeTestRule = createAndroidComposeRule<MainActivity>()
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun opensInfraredControlScreenFromFoundationScreen() {
|
|
||||||
composeTestRule.onNodeWithText("Infrared Control").performClick()
|
|
||||||
composeTestRule.onNodeWithTag("infrared_mode_label").assertIsDisplayed()
|
|
||||||
composeTestRule.onNodeWithTag("infrared_device_name_input").assertIsDisplayed()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
|
||||||
<application android:usesCleartextTraffic="true" />
|
|
||||||
</manifest>
|
|
||||||
-41
@@ -1,41 +0,0 @@
|
|||||||
package com.flagship.abox.manager.di
|
|
||||||
|
|
||||||
import com.flagship.abox.manager.domain.DeviceName
|
|
||||||
import com.flagship.abox.manager.domain.DomainResult
|
|
||||||
import com.flagship.abox.manager.domain.InfraredCode
|
|
||||||
import com.flagship.abox.manager.domain.InfraredCodeDownloadResult
|
|
||||||
import com.flagship.abox.manager.domain.InfraredGateway
|
|
||||||
import com.flagship.abox.manager.domain.ProviderProfileId
|
|
||||||
import com.flagship.abox.manager.infrared.runtime.InfraredRuntimeMode
|
|
||||||
import com.flagship.abox.manager.infrared.runtime.InfraredRuntimeModeStore
|
|
||||||
|
|
||||||
class DebugRoutingInfraredGateway(
|
|
||||||
private val modeStore: InfraredRuntimeModeStore,
|
|
||||||
private val fakeGateway: InfraredGateway,
|
|
||||||
private val realGateway: InfraredGateway,
|
|
||||||
) : InfraredGateway {
|
|
||||||
|
|
||||||
private suspend fun delegate(): InfraredGateway {
|
|
||||||
return when (modeStore.currentMode()) {
|
|
||||||
InfraredRuntimeMode.REAL_ABSDK -> realGateway
|
|
||||||
InfraredRuntimeMode.FAKE -> fakeGateway
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun sendKey(
|
|
||||||
profileId: ProviderProfileId,
|
|
||||||
deviceName: DeviceName,
|
|
||||||
key: String,
|
|
||||||
): DomainResult<Unit> = delegate().sendKey(profileId, deviceName, key)
|
|
||||||
|
|
||||||
override suspend fun learnKey(
|
|
||||||
profileId: ProviderProfileId,
|
|
||||||
deviceName: DeviceName,
|
|
||||||
key: String,
|
|
||||||
): DomainResult<Unit> = delegate().learnKey(profileId, deviceName, key)
|
|
||||||
|
|
||||||
override suspend fun downloadCodes(
|
|
||||||
profileId: ProviderProfileId,
|
|
||||||
codes: List<InfraredCode>,
|
|
||||||
): DomainResult<List<InfraredCodeDownloadResult>> = delegate().downloadCodes(profileId, codes)
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
package com.flagship.abox.manager.di
|
|
||||||
|
|
||||||
import com.flagship.abox.manager.domain.DeviceName
|
|
||||||
import com.flagship.abox.manager.domain.DomainError
|
|
||||||
import com.flagship.abox.manager.domain.DomainResult
|
|
||||||
import com.flagship.abox.manager.domain.InfraredCode
|
|
||||||
import com.flagship.abox.manager.domain.InfraredCodeDownloadResult
|
|
||||||
import com.flagship.abox.manager.domain.InfraredGateway
|
|
||||||
import com.flagship.abox.manager.domain.ProviderProfileId
|
|
||||||
import dagger.Module
|
|
||||||
import dagger.Provides
|
|
||||||
import dagger.hilt.InstallIn
|
|
||||||
import dagger.hilt.components.SingletonComponent
|
|
||||||
import javax.inject.Singleton
|
|
||||||
|
|
||||||
@Module
|
|
||||||
@InstallIn(SingletonComponent::class)
|
|
||||||
object InfraredReleaseModule {
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
fun provideInfraredGateway(): InfraredGateway = object : InfraredGateway {
|
|
||||||
override suspend fun sendKey(
|
|
||||||
profileId: ProviderProfileId,
|
|
||||||
deviceName: DeviceName,
|
|
||||||
key: String,
|
|
||||||
): DomainResult<Unit> = disabledFailure()
|
|
||||||
|
|
||||||
override suspend fun learnKey(
|
|
||||||
profileId: ProviderProfileId,
|
|
||||||
deviceName: DeviceName,
|
|
||||||
key: String,
|
|
||||||
): DomainResult<Unit> = disabledFailure()
|
|
||||||
|
|
||||||
override suspend fun downloadCodes(
|
|
||||||
profileId: ProviderProfileId,
|
|
||||||
codes: List<InfraredCode>,
|
|
||||||
): DomainResult<List<InfraredCodeDownloadResult>> = disabledFailure()
|
|
||||||
|
|
||||||
private fun <T> disabledFailure(): DomainResult<T> = DomainResult.Failure(
|
|
||||||
DomainError.OperationFailed(
|
|
||||||
externalCode = "REAL_ABSDK_DISABLED",
|
|
||||||
detail = "Infrared hardware transport is disabled in release builds",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-70
@@ -1,70 +0,0 @@
|
|||||||
package com.flagship.abox.manager.di
|
|
||||||
|
|
||||||
import com.flagship.abox.manager.domain.DeviceName
|
|
||||||
import com.flagship.abox.manager.domain.DomainResult
|
|
||||||
import com.flagship.abox.manager.domain.InfraredCode
|
|
||||||
import com.flagship.abox.manager.domain.ProviderProfileId
|
|
||||||
import com.flagship.abox.manager.infrared.runtime.InfraredRuntimeMode
|
|
||||||
import com.flagship.abox.manager.infrared.runtime.InfraredRuntimeModeStore
|
|
||||||
import com.flagship.abox.manager.testing.fakes.FakeInfraredGateway
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
|
||||||
import kotlinx.coroutines.test.runTest
|
|
||||||
import org.junit.Assert.assertEquals
|
|
||||||
import org.junit.Assert.assertTrue
|
|
||||||
import org.junit.Test
|
|
||||||
|
|
||||||
private class TestModeStore : InfraredRuntimeModeStore {
|
|
||||||
private val modeFlow = MutableStateFlow(InfraredRuntimeMode.FAKE)
|
|
||||||
override val mode: Flow<InfraredRuntimeMode> = modeFlow
|
|
||||||
override suspend fun currentMode(): InfraredRuntimeMode = modeFlow.value
|
|
||||||
override suspend fun setMode(mode: InfraredRuntimeMode): DomainResult<Unit> {
|
|
||||||
modeFlow.value = mode
|
|
||||||
return DomainResult.Success(Unit)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class DebugRoutingInfraredGatewayTest {
|
|
||||||
|
|
||||||
private val modeStore = TestModeStore()
|
|
||||||
private val fake = FakeInfraredGateway()
|
|
||||||
private val real = FakeInfraredGateway()
|
|
||||||
private val router = DebugRoutingInfraredGateway(modeStore, fake, real)
|
|
||||||
|
|
||||||
private val profile = ProviderProfileId("profile1")
|
|
||||||
private val device = DeviceName("ir-1")
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun delegatesToFakeByDefault() = runTest {
|
|
||||||
val result = router.sendKey(profile, device, "key1")
|
|
||||||
assertTrue(result is DomainResult.Success)
|
|
||||||
assertEquals(1, fake.sentKeys.size)
|
|
||||||
assertTrue(real.sentKeys.isEmpty())
|
|
||||||
|
|
||||||
router.learnKey(profile, device, "key2")
|
|
||||||
assertEquals(1, fake.learnedKeys.size)
|
|
||||||
assertTrue(real.learnedKeys.isEmpty())
|
|
||||||
|
|
||||||
router.downloadCodes(profile, listOf(InfraredCode(device, "k", "code")))
|
|
||||||
assertEquals(1, fake.downloadCalls.size)
|
|
||||||
assertTrue(real.downloadCalls.isEmpty())
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun delegatesToRealWhenModeIsRealAbsdk() = runTest {
|
|
||||||
modeStore.setMode(InfraredRuntimeMode.REAL_ABSDK)
|
|
||||||
|
|
||||||
val result = router.sendKey(profile, device, "key1")
|
|
||||||
assertTrue(result is DomainResult.Success)
|
|
||||||
assertTrue(fake.sentKeys.isEmpty())
|
|
||||||
assertEquals(1, real.sentKeys.size)
|
|
||||||
|
|
||||||
router.learnKey(profile, device, "key2")
|
|
||||||
assertTrue(fake.learnedKeys.isEmpty())
|
|
||||||
assertEquals(1, real.learnedKeys.size)
|
|
||||||
|
|
||||||
router.downloadCodes(profile, listOf(InfraredCode(device, "k", "code")))
|
|
||||||
assertTrue(fake.downloadCalls.isEmpty())
|
|
||||||
assertEquals(1, real.downloadCalls.size)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -17,6 +17,7 @@ espresso = "3.6.1"
|
|||||||
[libraries]
|
[libraries]
|
||||||
androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "activityCompose" }
|
androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "activityCompose" }
|
||||||
androidx-lifecycle-runtime-compose = { module = "androidx.lifecycle:lifecycle-runtime-compose", version.ref = "lifecycle" }
|
androidx-lifecycle-runtime-compose = { module = "androidx.lifecycle:lifecycle-runtime-compose", version.ref = "lifecycle" }
|
||||||
|
androidx-lifecycle-viewmodel-ktx = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "lifecycle" }
|
||||||
androidx-lifecycle-viewmodel-compose = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "lifecycle" }
|
androidx-lifecycle-viewmodel-compose = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "lifecycle" }
|
||||||
androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigationCompose" }
|
androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigationCompose" }
|
||||||
androidx-hilt-navigation-compose = { module = "androidx.hilt:hilt-navigation-compose", version = "1.2.0" }
|
androidx-hilt-navigation-compose = { module = "androidx.hilt:hilt-navigation-compose", version = "1.2.0" }
|
||||||
|
|||||||
Reference in New Issue
Block a user