Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ecf0cb0f authored by Behnam Heydarshahi's avatar Behnam Heydarshahi
Browse files

Allow temporarily unavailable flashlight tile

Fixes: 323348156
Flag: aconfig com.android.systemui.qs_new_tiles DEVELOPMENT
Test: atest SystemUiRoboTests
Test: atest FlashlightTileDataInteractorTest
FlashlightTileUserActionInteractorTest FlashlightMapperTest

Change-Id: Icf7e83e88f77df53e2ecea356a4d18148b560004
parent 80f831d8
Loading
Loading
Loading
Loading
+31 −6
Original line number Original line Diff line number Diff line
@@ -50,7 +50,8 @@ class FlashlightMapperTest : SysuiTestCase() {


    @Test
    @Test
    fun mapsDisabledDataToInactiveState() {
    fun mapsDisabledDataToInactiveState() {
        val tileState: QSTileState = mapper.map(qsTileConfig, FlashlightTileModel(false))
        val tileState: QSTileState =
            mapper.map(qsTileConfig, FlashlightTileModel.FlashlightAvailable(false))


        val actualActivationState = tileState.activationState
        val actualActivationState = tileState.activationState


@@ -59,7 +60,8 @@ class FlashlightMapperTest : SysuiTestCase() {


    @Test
    @Test
    fun mapsEnabledDataToActiveState() {
    fun mapsEnabledDataToActiveState() {
        val tileState: QSTileState = mapper.map(qsTileConfig, FlashlightTileModel(true))
        val tileState: QSTileState =
            mapper.map(qsTileConfig, FlashlightTileModel.FlashlightAvailable(true))


        val actualActivationState = tileState.activationState
        val actualActivationState = tileState.activationState
        assertEquals(QSTileState.ActivationState.ACTIVE, actualActivationState)
        assertEquals(QSTileState.ActivationState.ACTIVE, actualActivationState)
@@ -67,7 +69,8 @@ class FlashlightMapperTest : SysuiTestCase() {


    @Test
    @Test
    fun mapsEnabledDataToOnIconState() {
    fun mapsEnabledDataToOnIconState() {
        val tileState: QSTileState = mapper.map(qsTileConfig, FlashlightTileModel(true))
        val tileState: QSTileState =
            mapper.map(qsTileConfig, FlashlightTileModel.FlashlightAvailable(true))


        val expectedIcon =
        val expectedIcon =
            Icon.Loaded(context.getDrawable(R.drawable.qs_flashlight_icon_on)!!, null)
            Icon.Loaded(context.getDrawable(R.drawable.qs_flashlight_icon_on)!!, null)
@@ -77,7 +80,8 @@ class FlashlightMapperTest : SysuiTestCase() {


    @Test
    @Test
    fun mapsDisabledDataToOffIconState() {
    fun mapsDisabledDataToOffIconState() {
        val tileState: QSTileState = mapper.map(qsTileConfig, FlashlightTileModel(false))
        val tileState: QSTileState =
            mapper.map(qsTileConfig, FlashlightTileModel.FlashlightAvailable(false))


        val expectedIcon =
        val expectedIcon =
            Icon.Loaded(context.getDrawable(R.drawable.qs_flashlight_icon_off)!!, null)
            Icon.Loaded(context.getDrawable(R.drawable.qs_flashlight_icon_off)!!, null)
@@ -86,11 +90,32 @@ class FlashlightMapperTest : SysuiTestCase() {
    }
    }


    @Test
    @Test
    fun supportsOnlyClickAction() {
    fun mapsUnavailableDataToOffIconState() {
        val tileState: QSTileState =
            mapper.map(qsTileConfig, FlashlightTileModel.FlashlightTemporarilyUnavailable)

        val expectedIcon =
            Icon.Loaded(context.getDrawable(R.drawable.qs_flashlight_icon_off)!!, null)
        val actualIcon = tileState.icon()
        assertThat(actualIcon).isEqualTo(expectedIcon)
    }

    @Test
    fun supportClickActionWhenAvailable() {
        val dontCare = true
        val dontCare = true
        val tileState: QSTileState = mapper.map(qsTileConfig, FlashlightTileModel(dontCare))
        val tileState: QSTileState =
            mapper.map(qsTileConfig, FlashlightTileModel.FlashlightAvailable(dontCare))


        val supportedActions = tileState.supportedActions
        val supportedActions = tileState.supportedActions
        assertThat(supportedActions).containsExactly(QSTileState.UserAction.CLICK)
        assertThat(supportedActions).containsExactly(QSTileState.UserAction.CLICK)
    }
    }

    @Test
    fun doesNotSupportClickActionWhenUnavailable() {
        val tileState: QSTileState =
            mapper.map(qsTileConfig, FlashlightTileModel.FlashlightTemporarilyUnavailable)

        val supportedActions = tileState.supportedActions
        assertThat(supportedActions).isEmpty()
    }
}
}
+30 −4
Original line number Original line Diff line number Diff line
@@ -70,8 +70,7 @@ class FlashlightTileDataInteractorTest : SysuiTestCase() {
    }
    }


    @Test
    @Test
    fun dataMatchesController() = runTest {
    fun isEnabledDataMatchesControllerWhenAvailable() = runTest {
        controller.setFlashlight(false)
        val flowValues: List<FlashlightTileModel> by
        val flowValues: List<FlashlightTileModel> by
            collectValues(underTest.tileData(TEST_USER, flowOf(DataUpdateTrigger.InitialRequest)))
            collectValues(underTest.tileData(TEST_USER, flowOf(DataUpdateTrigger.InitialRequest)))


@@ -81,8 +80,35 @@ class FlashlightTileDataInteractorTest : SysuiTestCase() {
        controller.setFlashlight(false)
        controller.setFlashlight(false)
        runCurrent()
        runCurrent()


        assertThat(flowValues.size).isEqualTo(3)
        assertThat(flowValues.size).isEqualTo(4) // 2 from setup(), 2 from this test
        assertThat(flowValues.map { it.isEnabled }).containsExactly(false, true, false).inOrder()
        assertThat(
                flowValues.filterIsInstance<FlashlightTileModel.FlashlightAvailable>().map {
                    it.isEnabled
                }
            )
            .containsExactly(false, false, true, false)
            .inOrder()
    }

    /**
     * Simulates the scenario of changes in flashlight tile availability when camera is initially
     * closed, then opened, and closed again.
     */
    @Test
    fun availabilityDataMatchesControllerAvailability() = runTest {
        val flowValues: List<FlashlightTileModel> by
            collectValues(underTest.tileData(TEST_USER, flowOf(DataUpdateTrigger.InitialRequest)))

        runCurrent()
        controller.onFlashlightAvailabilityChanged(false)
        runCurrent()
        controller.onFlashlightAvailabilityChanged(true)
        runCurrent()

        assertThat(flowValues.size).isEqualTo(4) // 2 from setup + 2 from this test
        assertThat(flowValues.map { it is FlashlightTileModel.FlashlightAvailable })
            .containsExactly(true, true, false, true)
            .inOrder()
    }
    }


    private companion object {
    private companion object {
+13 −2
Original line number Original line Diff line number Diff line
@@ -29,7 +29,9 @@ import org.junit.Assume.assumeFalse
import org.junit.Before
import org.junit.Before
import org.junit.Test
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.Mock
import org.mockito.Mock
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.Mockito.verify


@SmallTest
@SmallTest
@@ -51,7 +53,7 @@ class FlashlightTileUserActionInteractorTest : SysuiTestCase() {
        assumeFalse(ActivityManager.isUserAMonkey())
        assumeFalse(ActivityManager.isUserAMonkey())
        val stateBeforeClick = false
        val stateBeforeClick = false


        underTest.handleInput(click(FlashlightTileModel(stateBeforeClick)))
        underTest.handleInput(click(FlashlightTileModel.FlashlightAvailable(stateBeforeClick)))


        verify(controller).setFlashlight(!stateBeforeClick)
        verify(controller).setFlashlight(!stateBeforeClick)
    }
    }
@@ -61,8 +63,17 @@ class FlashlightTileUserActionInteractorTest : SysuiTestCase() {
        assumeFalse(ActivityManager.isUserAMonkey())
        assumeFalse(ActivityManager.isUserAMonkey())
        val stateBeforeClick = true
        val stateBeforeClick = true


        underTest.handleInput(click(FlashlightTileModel(stateBeforeClick)))
        underTest.handleInput(click(FlashlightTileModel.FlashlightAvailable(stateBeforeClick)))


        verify(controller).setFlashlight(!stateBeforeClick)
        verify(controller).setFlashlight(!stateBeforeClick)
    }
    }

    @Test
    fun handleClickWhenUnavailable() = runTest {
        assumeFalse(ActivityManager.isUserAMonkey())

        underTest.handleInput(click(FlashlightTileModel.FlashlightTemporarilyUnavailable))

        verify(controller, never()).setFlashlight(anyBoolean())
    }
}
}
+12 −7
Original line number Original line Diff line number Diff line
@@ -40,7 +40,7 @@ constructor(
            val icon =
            val icon =
                Icon.Loaded(
                Icon.Loaded(
                    resources.getDrawable(
                    resources.getDrawable(
                        if (data.isEnabled) {
                        if (data is FlashlightTileModel.FlashlightAvailable && data.isEnabled) {
                            R.drawable.qs_flashlight_icon_on
                            R.drawable.qs_flashlight_icon_on
                        } else {
                        } else {
                            R.drawable.qs_flashlight_icon_off
                            R.drawable.qs_flashlight_icon_off
@@ -51,17 +51,22 @@ constructor(
                )
                )
            this.icon = { icon }
            this.icon = { icon }


            if (data.isEnabled) {
            contentDescription = label

            if (data is FlashlightTileModel.FlashlightTemporarilyUnavailable) {
                activationState = QSTileState.ActivationState.UNAVAILABLE
                secondaryLabel =
                    resources.getString(R.string.quick_settings_flashlight_camera_in_use)
                stateDescription = secondaryLabel
                supportedActions = setOf()
                return@build
            } else if (data is FlashlightTileModel.FlashlightAvailable && data.isEnabled) {
                activationState = QSTileState.ActivationState.ACTIVE
                activationState = QSTileState.ActivationState.ACTIVE
                secondaryLabel = resources.getStringArray(R.array.tile_states_flashlight)[2]
                secondaryLabel = resources.getStringArray(R.array.tile_states_flashlight)[2]
            } else {
            } else {
                activationState = QSTileState.ActivationState.INACTIVE
                activationState = QSTileState.ActivationState.INACTIVE
                secondaryLabel = resources.getStringArray(R.array.tile_states_flashlight)[1]
                secondaryLabel = resources.getStringArray(R.array.tile_states_flashlight)[1]
            }
            }
            contentDescription = label
            supportedActions = setOf(QSTileState.UserAction.CLICK)
            supportedActions =
                setOf(
                    QSTileState.UserAction.CLICK,
                )
        }
        }
}
}
+11 −6
Original line number Original line Diff line number Diff line
@@ -38,25 +38,30 @@ constructor(
        user: UserHandle,
        user: UserHandle,
        triggers: Flow<DataUpdateTrigger>
        triggers: Flow<DataUpdateTrigger>
    ): Flow<FlashlightTileModel> = conflatedCallbackFlow {
    ): Flow<FlashlightTileModel> = conflatedCallbackFlow {
        val initialValue = flashlightController.isEnabled
        trySend(FlashlightTileModel(initialValue))

        val callback =
        val callback =
            object : FlashlightController.FlashlightListener {
            object : FlashlightController.FlashlightListener {
                override fun onFlashlightChanged(enabled: Boolean) {
                override fun onFlashlightChanged(enabled: Boolean) {
                    trySend(FlashlightTileModel(enabled))
                    trySend(FlashlightTileModel.FlashlightAvailable(enabled))
                }
                }
                override fun onFlashlightError() {
                override fun onFlashlightError() {
                    trySend(FlashlightTileModel(false))
                    trySend(FlashlightTileModel.FlashlightAvailable(false))
                }
                }
                override fun onFlashlightAvailabilityChanged(available: Boolean) {
                override fun onFlashlightAvailabilityChanged(available: Boolean) {
                    trySend(FlashlightTileModel(flashlightController.isEnabled))
                    trySend(
                        if (available)
                            FlashlightTileModel.FlashlightAvailable(flashlightController.isEnabled)
                        else FlashlightTileModel.FlashlightTemporarilyUnavailable
                    )
                }
                }
            }
            }
        flashlightController.addCallback(callback)
        flashlightController.addCallback(callback)
        awaitClose { flashlightController.removeCallback(callback) }
        awaitClose { flashlightController.removeCallback(callback) }
    }
    }


    /**
     * Used to determine if the tile should be displayed. Not to be confused with the availability
     * in the data model above. This flow signals whether the tile should be shown or hidden.
     */
    override fun availability(user: UserHandle): Flow<Boolean> =
    override fun availability(user: UserHandle): Flow<Boolean> =
        flowOf(flashlightController.hasFlashlight())
        flowOf(flashlightController.hasFlashlight())
}
}
Loading