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

Commit af3330ec authored by Tianfan Zhang's avatar Tianfan Zhang Committed by Android (Google) Code Review
Browse files

Merge "Control AmbientCueTimeout by secure settings." into main

parents ba55b9ac 87f97601
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
@@ -362,6 +366,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}")
@@ -373,6 +391,7 @@ constructor(
        pw.println("isGestureNav: ${isGestureNav.value}")
        pw.println("actions: ${actions.value}")
        pw.println("isAmbientCueEnabled: ${isAmbientCueEnabled.value}")
        pw.println("ambientCueTimeoutMs: ${ambientCueTimeoutMs.value}")
    }

    companion object {
@@ -390,9 +409,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 }
    }
}