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

Commit 87f97601 authored by Tianfan Zhang's avatar Tianfan Zhang
Browse files

Control AmbientCueTimeout by secure settings.

Bug: 424715465
Flag: com.android.systemui.enable_underlay
Test: atest AmbientCueViewModelTest
Change-Id: Ib0fb237bde4545ce62dbd6e3d6c330e76e7d395c
parent cce71e41
Loading
Loading
Loading
Loading
+55 −8
Original line number Diff line number Diff line
@@ -38,9 +38,9 @@ import com.android.systemui.lifecycle.activateIn
import com.android.systemui.res.R
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.launch
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith

@@ -51,19 +51,53 @@ class AmbientCueViewModelTest : SysuiTestCase() {
    private val kosmos = testKosmos()
    private val viewModel = kosmos.ambientCueViewModelFactory.create()

    @Before
    fun setUp() {
    @Test
    fun isVisible_timesOut() =
        kosmos.runTest {
            viewModel.activateIn(kosmos.testScope)
            initializeIsVisible()
            assertThat(viewModel.isVisible).isTrue()

            // Times out when there's no interaction
            advanceTimeBy(AmbientCueViewModel.AMBIENT_CUE_TIMEOUT_MS.milliseconds)
            runCurrent()
            ambientCueRepository.fake.updateRootViewAttached()
            runCurrent()

            assertThat(viewModel.isVisible).isFalse()
        }

    @Test
    fun isVisible_timesOut() =
    fun isVisible_setTimeoutAs60sec_stillVisibleAfter30sec() =
        kosmos.runTest {
            ambientCueRepository.fake.setAmbientCueTimeoutMs(60_000)
            viewModel.activateIn(kosmos.testScope)
            initializeIsVisible()
            assertThat(viewModel.isVisible).isTrue()

            // Times out when there's no interaction
            advanceTimeBy(AmbientCueViewModel.AMBIENT_CUE_TIMEOUT_SEC)
            runCurrent()
            advanceTimeBy(30.seconds)
            runCurrent()
            ambientCueRepository.fake.updateRootViewAttached()
            runCurrent()

            assertThat(viewModel.isVisible).isTrue()
        }

    @Test
    fun isVisible_setTimeoutAs60sec_invisibleAfter60sec() =
        kosmos.runTest {
            ambientCueRepository.fake.setAmbientCueTimeoutMs(60_000)
            viewModel.activateIn(kosmos.testScope)
            initializeIsVisible()
            assertThat(viewModel.isVisible).isTrue()

            advanceTimeBy(30.seconds)
            runCurrent()
            ambientCueRepository.fake.updateRootViewAttached()
            runCurrent()
            assertThat(viewModel.isVisible).isTrue()
            advanceTimeBy(30.seconds)
            runCurrent()
            ambientCueRepository.fake.updateRootViewAttached()
            runCurrent()
@@ -74,6 +108,7 @@ class AmbientCueViewModelTest : SysuiTestCase() {
    @Test
    fun isVisible_imeNotVisible_true() =
        kosmos.runTest {
            viewModel.activateIn(kosmos.testScope)
            ambientCueRepository.fake.setActions(testActions(applicationContext))
            ambientCueInteractor.setDeactivated(false)

@@ -87,6 +122,7 @@ class AmbientCueViewModelTest : SysuiTestCase() {
    @Test
    fun isVisible_imeVisible_false() =
        kosmos.runTest {
            viewModel.activateIn(kosmos.testScope)
            initializeIsVisible()
            assertThat(viewModel.isVisible).isTrue()

@@ -100,6 +136,7 @@ class AmbientCueViewModelTest : SysuiTestCase() {
    @Test
    fun isVisible_isOccludedBySystemUi_true() =
        kosmos.runTest {
            viewModel.activateIn(kosmos.testScope)
            initializeIsVisible()
            assertThat(viewModel.isVisible).isTrue()

@@ -112,6 +149,7 @@ class AmbientCueViewModelTest : SysuiTestCase() {
    @Test
    fun isVisible_isOccludedBySystemUi_false() =
        kosmos.runTest {
            viewModel.activateIn(kosmos.testScope)
            initializeIsVisible()
            assertThat(viewModel.isVisible).isTrue()

@@ -124,6 +162,7 @@ class AmbientCueViewModelTest : SysuiTestCase() {
    @Test
    fun onClick_collapses() =
        kosmos.runTest {
            viewModel.activateIn(kosmos.testScope)
            ambientCueRepository.fake.setActions(testActions(applicationContext))
            ambientCueInteractor.setDeactivated(false)
            viewModel.expand()
@@ -140,6 +179,10 @@ class AmbientCueViewModelTest : SysuiTestCase() {
    @Test
    fun delayAndDeactivateCueBar_refreshTimeout() =
        kosmos.runTest {
            ambientCueRepository.fake.setAmbientCueTimeoutMs(
                AmbientCueViewModel.AMBIENT_CUE_TIMEOUT_MS
            )
            viewModel.activateIn(kosmos.testScope)
            ambientCueInteractor.setDeactivated(false)
            testScope.backgroundScope.launch { viewModel.delayAndDeactivateCueBar() }
            advanceTimeBy(10.seconds)
@@ -147,7 +190,7 @@ class AmbientCueViewModelTest : SysuiTestCase() {
            assertThat(ambientCueRepository.isDeactivated.value).isFalse()

            testScope.backgroundScope.launch { viewModel.delayAndDeactivateCueBar() }
            advanceTimeBy(AmbientCueViewModel.AMBIENT_CUE_TIMEOUT_SEC - 10.seconds)
            advanceTimeBy(AmbientCueViewModel.AMBIENT_CUE_TIMEOUT_MS.milliseconds - 10.seconds)
            runCurrent()
            // 5 seconds after calling delayAndDeactivateCueBar() again (totally 15 seconds after
            // test begins), isDeactivated should still be false.
@@ -162,6 +205,7 @@ class AmbientCueViewModelTest : SysuiTestCase() {

    fun pillStyle_gestureNav_isInNavbarMode() =
        kosmos.runTest {
            viewModel.activateIn(kosmos.testScope)
            ambientCueRepository.fake.setIsGestureNav(true)
            ambientCueRepository.fake.setTaskBarVisible(false)

@@ -172,6 +216,7 @@ class AmbientCueViewModelTest : SysuiTestCase() {
    @Test
    fun pillStyle_gestureNavAndTaskBar_shortPillEndAligned() =
        kosmos.runTest {
            viewModel.activateIn(kosmos.testScope)
            ambientCueRepository.fake.setIsGestureNav(true)
            ambientCueRepository.fake.setTaskBarVisible(true)

@@ -183,6 +228,7 @@ class AmbientCueViewModelTest : SysuiTestCase() {
    @Test
    fun pillStyle_3ButtonNav_shortPill() =
        kosmos.runTest {
            viewModel.activateIn(kosmos.testScope)
            val recentsButtonPosition = Rect(10, 20, 30, 40)
            ambientCueRepository.fake.setIsGestureNav(false)
            ambientCueRepository.fake.setTaskBarVisible(true)
@@ -196,6 +242,7 @@ class AmbientCueViewModelTest : SysuiTestCase() {
    @Test
    fun pillStyle_3ButtonNavAndTaskBar_shortPill() =
        kosmos.runTest {
            viewModel.activateIn(kosmos.testScope)
            ambientCueRepository.fake.setIsGestureNav(false)
            ambientCueRepository.fake.setTaskBarVisible(false)

+21 −0
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import android.app.smartspace.SmartspaceManager
import android.app.smartspace.SmartspaceSession.OnTargetsAvailableListener
import android.content.Context
import android.graphics.Rect
import android.provider.Settings
import android.util.Log
import android.view.autofill.AutofillId
import android.view.autofill.AutofillManager
@@ -97,6 +98,9 @@ interface AmbientCueRepository {

    /* If AmbientCue is enabled. */
    val isAmbientCueEnabled: StateFlow<Boolean>

    /* The timeout for Ambient Cue to disappear. */
    val ambientCueTimeoutMs: StateFlow<Int>
}

@SysUISingleton
@@ -358,6 +362,20 @@ constructor(
                initialValue = false,
            )

    override val ambientCueTimeoutMs: StateFlow<Int> =
        secureSettingsRepository
            .intSetting(
                name = Settings.Secure.ACCESSIBILITY_INTERACTIVE_UI_TIMEOUT_MS,
                AMBIENT_CUE_DEFAULT_TIMEOUT_MS,
            )
            .map { if (it == 0) AMBIENT_CUE_DEFAULT_TIMEOUT_MS else it }
            .flowOn(backgroundDispatcher)
            .stateIn(
                scope = backgroundScope,
                started = SharingStarted.WhileSubscribed(),
                initialValue = AMBIENT_CUE_DEFAULT_TIMEOUT_MS,
            )

    override fun dump(pw: PrintWriter, args: Array<out String>) {
        pw.println("isRootViewAttached: ${isRootViewAttached.value}")
        pw.println("targetTaskId: ${targetTaskId.value}")
@@ -369,6 +387,7 @@ constructor(
        pw.println("isGestureNav: ${isGestureNav.value}")
        pw.println("actions: ${actions.value}")
        pw.println("isAmbientCueEnabled: ${isAmbientCueEnabled.value}")
        pw.println("ambientCueTimeoutMs: ${ambientCueTimeoutMs.value}")
    }

    companion object {
@@ -386,9 +405,11 @@ constructor(
        private const val DEBUG = false
        private const val INVALID_TASK_ID = ActivityTaskManager.INVALID_TASK_ID
        @VisibleForTesting const val AMBIENT_CUE_SETTING = "spoonBarOptedIn"
        @VisibleForTesting const val AMBIENT_CUE_TIMEOUT_SETTING = "ambientCueTimeoutSec"
        @VisibleForTesting const val OPTED_IN = 0x10
        @VisibleForTesting const val OPTED_OUT = 0x01
        const val DEBOUNCE_DELAY_MS = 100L
        private const val AMBIENT_CUE_DEFAULT_TIMEOUT_MS = 30_000
        @VisibleForTesting const val MA_ACTION_TYPE_NAME = "ma"
        @VisibleForTesting const val MR_ACTION_TYPE_NAME = "mr"
    }
+1 −0
Original line number Diff line number Diff line
@@ -47,6 +47,7 @@ constructor(
    val recentsButtonPosition: StateFlow<Rect?> = repository.recentsButtonPosition
    val isTaskBarVisible: StateFlow<Boolean> = repository.isTaskBarVisible
    val isAmbientCueEnabled: StateFlow<Boolean> = repository.isAmbientCueEnabled
    val ambientCueTimeoutMs: StateFlow<Int> = repository.ambientCueTimeoutMs

    fun setDeactivated(isDeactivated: Boolean) {
        repository.isDeactivated.update { isDeactivated }
+11 −3
Original line number Diff line number Diff line
@@ -31,7 +31,7 @@ import com.android.systemui.util.kotlin.launchAndDispose
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import java.io.PrintWriter
import kotlin.time.Duration.Companion.seconds
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.Job
import kotlinx.coroutines.awaitCancellation
@@ -71,6 +71,13 @@ constructor(
            source = ambientCueInteractor.isOccludedBySystemUi,
        )

    private val ambientCueTimeoutMs: Int by
        hydrator.hydratedStateOf(
            traceName = "ambientCueTimeoutMs",
            initialValue = AMBIENT_CUE_TIMEOUT_MS,
            source = ambientCueInteractor.ambientCueTimeoutMs,
        )

    val isVisible: Boolean
        get() = isRootViewAttached && !isImeVisible && !isOccludedBySystemUi

@@ -162,7 +169,7 @@ constructor(

        coroutineScopeTraced("AmbientCueViewModel") {
            deactivateCueBarJob = launch {
                delay(AMBIENT_CUE_TIMEOUT_SEC)
                delay(ambientCueTimeoutMs.milliseconds)
                // TODO(b/425279501) Log ambient cue timeout status.
                ambientCueInteractor.setDeactivated(true)
            }
@@ -198,6 +205,7 @@ constructor(
        pw.println("pillStyle: $pillStyle")
        pw.println("deactivateCueBarJob: $deactivateCueBarJob")
        pw.println("actions: $actions")
        pw.println("ambientCueTimeoutMs: $ambientCueTimeoutMs")
    }

    @AssistedFactory
@@ -207,6 +215,6 @@ constructor(

    companion object {
        private const val TAG = "AmbientCueViewModel"
        @VisibleForTesting val AMBIENT_CUE_TIMEOUT_SEC = 30.seconds
        @VisibleForTesting const val AMBIENT_CUE_TIMEOUT_MS = 30_000
    }
}
+7 −0
Original line number Diff line number Diff line
@@ -53,6 +53,9 @@ class FakeAmbientCueRepository : AmbientCueRepository {
    private val _isAmbientCueEnabled = MutableStateFlow(false)
    override val isAmbientCueEnabled: StateFlow<Boolean> = _isAmbientCueEnabled.asStateFlow()

    private val _ambientCueTimeoutMs = MutableStateFlow(0)
    override val ambientCueTimeoutMs: StateFlow<Int> = _ambientCueTimeoutMs.asStateFlow()

    fun setActions(actions: List<ActionModel>) {
        _actions.update { actions }
    }
@@ -84,4 +87,8 @@ class FakeAmbientCueRepository : AmbientCueRepository {
    fun setAmbientCueEnabled(isEnabled: Boolean) {
        _isAmbientCueEnabled.update { isEnabled }
    }

    fun setAmbientCueTimeoutMs(timeoutMs: Int) {
        _ambientCueTimeoutMs.update { timeoutMs }
    }
}