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

Commit d47f4077 authored by Matías Hernández's avatar Matías Hernández
Browse files

Only show the DND icon in Weather Clock if the DND mode is active

For other modes, show nothing at all (because it's not clear we're allowed to show 3P-provided icons).

Fixes: 369154614
Test: atest ClockEventControllerTest + manual
Flag: android.app.modes_ui
Change-Id: I878bc6fc9186662a0290b6416a9b1e326b0b912f
parent 5b2f1b8b
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -74,6 +74,10 @@ class FakeZenModeRepository : ZenModeRepository {
        mutableModesFlow.value = mutableModesFlow.value.filter { it.id != id }
    }

    fun replaceMode(modeId: String, mode: ZenMode) {
        mutableModesFlow.value = (mutableModesFlow.value.filter { it.id != modeId }) + mode
    }

    fun getMode(id: String): ZenMode? {
        return mutableModesFlow.value.find { it.id == id }
    }
+59 −25
Original line number Diff line number Diff line
@@ -15,12 +15,15 @@
 */
package com.android.keyguard

import android.app.NotificationManager.zenModeFromInterruptionFilter
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.res.Resources
import android.os.Trace
import android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
import android.provider.Settings.Global.ZEN_MODE_OFF
import android.text.format.DateFormat
import android.util.Log
import android.util.TypedValue
@@ -49,6 +52,7 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.log.core.Logger
import com.android.systemui.modes.shared.ModesUi
import com.android.systemui.plugins.clocks.AlarmData
import com.android.systemui.plugins.clocks.ClockController
import com.android.systemui.plugins.clocks.ClockFaceController
@@ -63,6 +67,7 @@ import com.android.systemui.statusbar.policy.BatteryController
import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.ZenModeController
import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor
import com.android.systemui.util.concurrency.DelayableExecutor
import java.util.Locale
import java.util.TimeZone
@@ -97,12 +102,13 @@ constructor(
    private val clockBuffers: ClockMessageBuffers,
    private val featureFlags: FeatureFlagsClassic,
    private val zenModeController: ZenModeController,
    private val zenModeInteractor: ZenModeInteractor,
) {
    var loggers =
        listOf(
                clockBuffers.infraMessageBuffer,
                clockBuffers.smallClockMessageBuffer,
                clockBuffers.largeClockMessageBuffer
                clockBuffers.largeClockMessageBuffer,
            )
            .map { Logger(it, TAG) }

@@ -146,7 +152,7 @@ constructor(
                        bgExecutor,
                        regionSamplingEnabled,
                        isLockscreen = true,
                        ::updateColors
                        ::updateColors,
                    )
                    .apply { startRegionSampler() }

@@ -157,7 +163,7 @@ constructor(
                        bgExecutor,
                        regionSamplingEnabled,
                        isLockscreen = true,
                        ::updateColors
                        ::updateColors,
                    )
                    .apply { startRegionSampler() }

@@ -271,7 +277,7 @@ constructor(
        bgExecutor: Executor?,
        regionSamplingEnabled: Boolean,
        isLockscreen: Boolean,
        updateColors: () -> Unit
        updateColors: () -> Unit,
    ): RegionSampler {
        return RegionSampler(
            sampledView,
@@ -384,23 +390,29 @@ constructor(
            }
        }

    @VisibleForTesting
    internal fun listenForDnd(scope: CoroutineScope): Job {
        ModesUi.assertInNewMode()
        return scope.launch {
            zenModeInteractor.dndMode.collect {
                val zenMode =
                    if (it != null && it.isActive)
                        zenModeFromInterruptionFilter(
                            it.interruptionFilter,
                            ZEN_MODE_IMPORTANT_INTERRUPTIONS,
                        )
                    else ZEN_MODE_OFF

                handleZenMode(zenMode)
            }
        }
    }

    private val zenModeCallback =
        object : ZenModeController.Callback {
            override fun onZenChanged(zen: Int) {
                var mode = ZenMode.fromInt(zen)
                if (mode == null) {
                    Log.e(TAG, "Failed to get zen mode from int: $zen")
                    return
                }

                zenData =
                    ZenData(
                            mode,
                            if (mode == ZenMode.OFF) SysuiR.string::dnd_is_off.name
                            else SysuiR.string::dnd_is_on.name
                        )
                        .also { data ->
                            mainExecutor.execute { clock?.run { events.onZenDataChanged(data) } }
                if (!ModesUi.isEnabled) {
                    handleZenMode(zen)
                }
            }

@@ -409,7 +421,7 @@ constructor(
                alarmData =
                    AlarmData(
                            if (nextAlarmMillis > 0) nextAlarmMillis else null,
                            SysuiR.string::status_bar_alarm.name
                            SysuiR.string::status_bar_alarm.name,
                        )
                        .also { data ->
                            mainExecutor.execute { clock?.run { events.onAlarmDataChanged(data) } }
@@ -417,6 +429,24 @@ constructor(
            }
        }

    private fun handleZenMode(zen: Int) {
        val mode = ZenMode.fromInt(zen)
        if (mode == null) {
            Log.e(TAG, "Failed to get zen mode from int: $zen")
            return
        }

        zenData =
            ZenData(
                    mode,
                    if (mode == ZenMode.OFF) SysuiR.string::dnd_is_off.name
                    else SysuiR.string::dnd_is_on.name,
                )
                .also { data ->
                    mainExecutor.execute { clock?.run { events.onZenDataChanged(data) } }
                }
    }

    fun registerListeners(parent: View) {
        if (isRegistered) {
            return
@@ -424,7 +454,7 @@ constructor(
        isRegistered = true
        broadcastDispatcher.registerReceiver(
            localeBroadcastReceiver,
            IntentFilter(Intent.ACTION_LOCALE_CHANGED)
            IntentFilter(Intent.ACTION_LOCALE_CHANGED),
        )
        configurationController.addCallback(configListener)
        batteryController.addCallback(batteryCallback)
@@ -434,6 +464,9 @@ constructor(
            parent.repeatWhenAttached {
                repeatOnLifecycle(Lifecycle.State.CREATED) {
                    listenForDozing(this)
                    if (ModesUi.isEnabled) {
                        listenForDnd(this)
                    }
                    if (MigrateClocksToBlueprint.isEnabled) {
                        listenForDozeAmountTransition(this)
                        listenForAnyStateToAodTransition(this)
@@ -449,7 +482,9 @@ constructor(

        bgExecutor.execute {
            // Query ZenMode data
            if (!ModesUi.isEnabled) {
                zenModeCallback.onZenChanged(zenModeController.zen)
            }
            zenModeCallback.onNextAlarmChanged()
        }
    }
@@ -605,10 +640,9 @@ constructor(
    @VisibleForTesting
    internal fun listenForDozing(scope: CoroutineScope): Job {
        return scope.launch {
            combine(
                    keyguardInteractor.dozeAmount,
                    keyguardInteractor.isDozing,
                ) { localDozeAmount, localIsDozing ->
            combine(keyguardInteractor.dozeAmount, keyguardInteractor.isDozing) {
                    localDozeAmount,
                    localIsDozing ->
                    localDozeAmount > dozeAmount || localIsDozing
                }
                .collect { localIsDozing -> isDozing = localIsDozing }
+95 −27
Original line number Diff line number Diff line
@@ -17,11 +17,16 @@ package com.android.keyguard

import android.content.BroadcastReceiver
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.provider.Settings
import android.view.View
import android.view.ViewTreeObserver
import android.widget.FrameLayout
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.notification.modes.TestModeBuilder.MANUAL_DND_ACTIVE
import com.android.settingslib.notification.modes.TestModeBuilder.MANUAL_DND_INACTIVE
import com.android.systemui.Flags as AConfigFlags
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.flags.Flags
@@ -36,6 +41,7 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.kosmos.testScope
import com.android.systemui.log.core.LogLevel
import com.android.systemui.log.core.LogcatOnlyMessageBuffer
import com.android.systemui.plugins.clocks.ClockAnimations
@@ -46,9 +52,15 @@ import com.android.systemui.plugins.clocks.ClockFaceController
import com.android.systemui.plugins.clocks.ClockFaceEvents
import com.android.systemui.plugins.clocks.ClockMessageBuffers
import com.android.systemui.plugins.clocks.ClockTickRate
import com.android.systemui.plugins.clocks.ZenData
import com.android.systemui.plugins.clocks.ZenData.ZenMode
import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.BatteryController
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.ZenModeController
import com.android.systemui.statusbar.policy.data.repository.fakeZenModeRepository
import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor
import com.android.systemui.testKosmos
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
@@ -57,9 +69,12 @@ import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
import java.util.TimeZone
import java.util.concurrent.Executor
import java.util.concurrent.TimeUnit
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.yield
import org.junit.Assert.assertEquals
import org.junit.Before
@@ -73,15 +88,26 @@ import org.mockito.Mock
import org.mockito.Mockito.never
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
import com.android.systemui.Flags as AConfigFlags
import org.mockito.Mockito.`when` as whenever
import org.mockito.junit.MockitoJUnit

@RunWith(AndroidJUnit4::class)
@SmallTest
@OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
class ClockEventControllerTest : SysuiTestCase() {

    private val kosmos = testKosmos()
    private val zenModeRepository = kosmos.fakeZenModeRepository
    private val testScope = kosmos.testScope

    @JvmField @Rule val mockito = MockitoJUnit.rule()

    private val mainExecutor = ImmediateExecutor()
    private lateinit var repository: FakeKeyguardRepository
    private val messageBuffer = LogcatOnlyMessageBuffer(LogLevel.DEBUG)
    private val clockBuffers = ClockMessageBuffers(messageBuffer, messageBuffer, messageBuffer)
    private lateinit var underTest: ClockEventController

    @Mock private lateinit var broadcastDispatcher: BroadcastDispatcher
    @Mock private lateinit var batteryController: BatteryController
    @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@@ -89,7 +115,6 @@ class ClockEventControllerTest : SysuiTestCase() {
    @Mock private lateinit var animations: ClockAnimations
    @Mock private lateinit var events: ClockEvents
    @Mock private lateinit var clock: ClockController
    @Mock private lateinit var mainExecutor: DelayableExecutor
    @Mock private lateinit var bgExecutor: Executor
    @Mock private lateinit var smallClockController: ClockFaceController
    @Mock private lateinit var smallClockView: View
@@ -102,12 +127,10 @@ class ClockEventControllerTest : SysuiTestCase() {
    @Mock private lateinit var smallClockEvents: ClockFaceEvents
    @Mock private lateinit var largeClockEvents: ClockFaceEvents
    @Mock private lateinit var parentView: View
    private lateinit var repository: FakeKeyguardRepository
    @Mock private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
    private val messageBuffer = LogcatOnlyMessageBuffer(LogLevel.DEBUG)
    private val clockBuffers = ClockMessageBuffers(messageBuffer, messageBuffer, messageBuffer)
    private lateinit var underTest: ClockEventController

    @Mock private lateinit var zenModeController: ZenModeController
    private var zenModeControllerCallback: ZenModeController.Callback? = null

    @Before
    fun setUp() {
@@ -129,12 +152,11 @@ class ClockEventControllerTest : SysuiTestCase() {
        whenever(largeClockController.config)
            .thenReturn(ClockFaceConfig(tickRate = ClockTickRate.PER_MINUTE))

        zenModeRepository.addMode(MANUAL_DND_INACTIVE)

        repository = FakeKeyguardRepository()

        val withDeps =
            KeyguardInteractorFactory.create(
                repository = repository,
            )
        val withDeps = KeyguardInteractorFactory.create(repository = repository)

        withDeps.featureFlags.apply { set(Flags.REGION_SAMPLING, false) }
        underTest =
@@ -151,7 +173,8 @@ class ClockEventControllerTest : SysuiTestCase() {
                bgExecutor,
                clockBuffers,
                withDeps.featureFlags,
                zenModeController
                zenModeController,
                kosmos.zenModeInteractor,
            )
        underTest.clock = clock

@@ -161,6 +184,10 @@ class ClockEventControllerTest : SysuiTestCase() {
            repository.setIsDozing(true)
            repository.setDozeAmount(1f)
        }

        val zenCallbackCaptor = argumentCaptor<ZenModeController.Callback>()
        verify(zenModeController).addCallback(zenCallbackCaptor.capture())
        zenModeControllerCallback = zenCallbackCaptor.value
    }

    @Test
@@ -349,17 +376,12 @@ class ClockEventControllerTest : SysuiTestCase() {
    fun listenForTransitionToAodFromGone_updatesClockDozeAmountToOne() =
        runBlocking(IMMEDIATE) {
            val transitionStep = MutableStateFlow(TransitionStep())
            whenever(keyguardTransitionInteractor
                .transition(Edge.create(to = AOD)))
            whenever(keyguardTransitionInteractor.transition(Edge.create(to = AOD)))
                .thenReturn(transitionStep)

            val job = underTest.listenForAnyStateToAodTransition(this)
            transitionStep.value =
                TransitionStep(
                    from = GONE,
                    to = AOD,
                    transitionState = TransitionState.STARTED,
                )
                TransitionStep(from = GONE, to = AOD, transitionState = TransitionState.STARTED)
            yield()

            verify(animations, times(2)).doze(1f)
@@ -371,8 +393,7 @@ class ClockEventControllerTest : SysuiTestCase() {
    fun listenForTransitionToLSFromOccluded_updatesClockDozeAmountToZero() =
        runBlocking(IMMEDIATE) {
            val transitionStep = MutableStateFlow(TransitionStep())
            whenever(keyguardTransitionInteractor
                .transition(Edge.create(to = LOCKSCREEN)))
            whenever(keyguardTransitionInteractor.transition(Edge.create(to = LOCKSCREEN)))
                .thenReturn(transitionStep)

            val job = underTest.listenForAnyStateToLockscreenTransition(this)
@@ -393,8 +414,7 @@ class ClockEventControllerTest : SysuiTestCase() {
    fun listenForTransitionToAodFromLockscreen_neverUpdatesClockDozeAmount() =
        runBlocking(IMMEDIATE) {
            val transitionStep = MutableStateFlow(TransitionStep())
            whenever(keyguardTransitionInteractor
                .transition(Edge.create(to = AOD)))
            whenever(keyguardTransitionInteractor.transition(Edge.create(to = AOD)))
                .thenReturn(transitionStep)

            val job = underTest.listenForAnyStateToAodTransition(this)
@@ -415,8 +435,7 @@ class ClockEventControllerTest : SysuiTestCase() {
    fun listenForAnyStateToLockscreenTransition_neverUpdatesClockDozeAmount() =
        runBlocking(IMMEDIATE) {
            val transitionStep = MutableStateFlow(TransitionStep())
            whenever(keyguardTransitionInteractor
                .transition(Edge.create(to = LOCKSCREEN)))
            whenever(keyguardTransitionInteractor.transition(Edge.create(to = LOCKSCREEN)))
                .thenReturn(transitionStep)

            val job = underTest.listenForAnyStateToLockscreenTransition(this)
@@ -437,8 +456,7 @@ class ClockEventControllerTest : SysuiTestCase() {
    fun listenForAnyStateToDozingTransition_UpdatesClockDozeAmountToOne() =
        runBlocking(IMMEDIATE) {
            val transitionStep = MutableStateFlow(TransitionStep())
            whenever(keyguardTransitionInteractor
                .transition(Edge.create(to = DOZING)))
            whenever(keyguardTransitionInteractor.transition(Edge.create(to = DOZING)))
                .thenReturn(transitionStep)

            val job = underTest.listenForAnyStateToDozingTransition(this)
@@ -498,7 +516,57 @@ class ClockEventControllerTest : SysuiTestCase() {
            verify(smallClockFrame.viewTreeObserver).removeOnGlobalLayoutListener(any())
        }

    @Test
    @EnableFlags(android.app.Flags.FLAG_MODES_UI)
    fun listenForDnd_onDndChange_updatesClockZenMode() =
        testScope.runTest {
            underTest.listenForDnd(testScope.backgroundScope)

            zenModeRepository.replaceMode(MANUAL_DND_INACTIVE.id, MANUAL_DND_ACTIVE)
            runCurrent()

            verify(events)
                .onZenDataChanged(
                    eq(ZenData(ZenMode.IMPORTANT_INTERRUPTIONS, R.string::dnd_is_on.name))
                )

            zenModeRepository.replaceMode(MANUAL_DND_ACTIVE.id, MANUAL_DND_INACTIVE)
            runCurrent()

            verify(events).onZenDataChanged(eq(ZenData(ZenMode.OFF, R.string::dnd_is_off.name)))
        }

    @Test
    @DisableFlags(android.app.Flags.FLAG_MODES_UI)
    fun zenModeControllerCallback_onDndChange_updatesClockZenMode() =
        runBlocking(IMMEDIATE) {
            zenModeControllerCallback!!.onZenChanged(
                Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
            )

            verify(events)
                .onZenDataChanged(
                    eq(ZenData(ZenMode.IMPORTANT_INTERRUPTIONS, R.string::dnd_is_on.name))
                )

            zenModeControllerCallback!!.onZenChanged(Settings.Global.ZEN_MODE_OFF)

            verify(events).onZenDataChanged(eq(ZenData(ZenMode.OFF, R.string::dnd_is_off.name)))
        }

    companion object {
        private val IMMEDIATE = Dispatchers.Main.immediate
    }
}

private class ImmediateExecutor : DelayableExecutor {
    override fun execute(runnable: Runnable) {
        runnable.run()
    }

    override fun executeDelayed(runnable: Runnable, delay: Long, unit: TimeUnit) =
        runnable.apply { run() }

    override fun executeAtTime(runnable: Runnable, uptimeMillis: Long, unit: TimeUnit) =
        runnable.apply { run() }
}