Rewire dozing for clock events

Continuing exploration of moving all doze events to flowables,
including both isDozing and the dozeAmount animations. At the moment,
the source of these events still comes from StatusBarStateController,
which informs the KeyguardRepository. Next up is to create a parallel
way to drive these animations that is solely based on repository
information.

Bug: 242853098
Test: atest ClockEventControllerTest KeyguardClockSwitchControllerTest
Change-Id: I8c40076ad7e716093f602e5bbf5d9f03871ac679
This commit is contained in:
Matt Pietal
2022-09-28 18:46:55 +00:00
parent 0eee4af3a5
commit 8e771d0d45
4 changed files with 126 additions and 108 deletions

View File

@@ -23,12 +23,17 @@ import android.content.res.Resources
import android.text.format.DateFormat
import android.util.TypedValue
import android.view.View
import androidx.annotation.VisibleForTesting
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.plugins.ClockController
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.flags.Flags.REGION_SAMPLING
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.Clock
import com.android.systemui.shared.regionsampling.RegionSamplingInstance
import com.android.systemui.statusbar.policy.BatteryController
import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback
@@ -38,13 +43,19 @@ import java.util.Locale
import java.util.TimeZone
import java.util.concurrent.Executor
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.launch
/**
* Controller for a Clock provided by the registry and used on the keyguard. Instantiated by
* [KeyguardClockSwitchController]. Functionality is forked from [AnimatableClockController].
*/
open class ClockEventController @Inject constructor(
private val statusBarStateController: StatusBarStateController,
private val keyguardInteractor: KeyguardInteractor,
private val broadcastDispatcher: BroadcastDispatcher,
private val batteryController: BatteryController,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
@@ -53,7 +64,7 @@ open class ClockEventController @Inject constructor(
private val context: Context,
@Main private val mainExecutor: Executor,
@Background private val bgExecutor: Executor,
private val featureFlags: FeatureFlags,
private val featureFlags: FeatureFlags
) {
var clock: ClockController? = null
set(value) {
@@ -70,9 +81,9 @@ open class ClockEventController @Inject constructor(
private var isCharging = false
private var dozeAmount = 0f
private var isKeyguardVisible = false
private val regionSamplingEnabled =
featureFlags.isEnabled(com.android.systemui.flags.Flags.REGION_SAMPLING)
private var isRegistered = false
private var disposableHandle: DisposableHandle? = null
private val regionSamplingEnabled = featureFlags.isEnabled(REGION_SAMPLING)
private fun updateColors() {
if (regionSamplingEnabled && smallRegionSampler != null && largeRegionSampler != null) {
@@ -165,15 +176,6 @@ open class ClockEventController @Inject constructor(
}
}
private val statusBarStateListener = object : StatusBarStateController.StateListener {
override fun onDozeAmountChanged(linear: Float, eased: Float) {
clock?.animations?.doze(linear)
isDozing = linear > dozeAmount
dozeAmount = linear
}
}
private val keyguardUpdateMonitorCallback = object : KeyguardUpdateMonitorCallback() {
override fun onKeyguardVisibilityChanged(visible: Boolean) {
isKeyguardVisible = visible
@@ -195,13 +197,11 @@ open class ClockEventController @Inject constructor(
}
}
init {
isDozing = statusBarStateController.isDozing
}
fun registerListeners() {
dozeAmount = statusBarStateController.dozeAmount
isDozing = statusBarStateController.isDozing || dozeAmount != 0f
fun registerListeners(parent: View) {
if (isRegistered) {
return
}
isRegistered = true
broadcastDispatcher.registerReceiver(
localeBroadcastReceiver,
@@ -210,17 +210,27 @@ open class ClockEventController @Inject constructor(
configurationController.addCallback(configListener)
batteryController.addCallback(batteryCallback)
keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback)
statusBarStateController.addCallback(statusBarStateListener)
smallRegionSampler?.startRegionSampler()
largeRegionSampler?.startRegionSampler()
disposableHandle = parent.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.STARTED) {
listenForDozing(this)
listenForDozeAmount(this)
}
}
}
fun unregisterListeners() {
if (!isRegistered) {
return
}
isRegistered = false
disposableHandle?.dispose()
broadcastDispatcher.unregisterReceiver(localeBroadcastReceiver)
configurationController.removeCallback(configListener)
batteryController.removeCallback(batteryCallback)
keyguardUpdateMonitor.removeCallback(keyguardUpdateMonitorCallback)
statusBarStateController.removeCallback(statusBarStateListener)
smallRegionSampler?.stopRegionSampler()
largeRegionSampler?.stopRegionSampler()
}
@@ -235,8 +245,28 @@ open class ClockEventController @Inject constructor(
largeRegionSampler?.dump(pw)
}
companion object {
private val TAG = ClockEventController::class.simpleName
private const val FORMAT_NUMBER = 1234567890
@VisibleForTesting
internal suspend fun listenForDozeAmount(scope: CoroutineScope): Job {
return scope.launch {
keyguardInteractor.dozeAmount.collect {
dozeAmount = it
clock?.animations?.doze(dozeAmount)
}
}
}
@VisibleForTesting
internal suspend fun listenForDozing(scope: CoroutineScope): Job {
return scope.launch {
combine (
keyguardInteractor.dozeAmount,
keyguardInteractor.isDozing,
) { localDozeAmount, localIsDozing ->
localDozeAmount > dozeAmount || localIsDozing
}
.collect { localIsDozing ->
isDozing = localIsDozing
}
}
}
}

View File

@@ -164,7 +164,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
protected void onViewAttached() {
mClockRegistry.registerClockChangeListener(mClockChangedListener);
setClock(mClockRegistry.createCurrentClock());
mClockEventController.registerListeners();
mClockEventController.registerListeners(mView);
mKeyguardClockTopMargin =
mView.getResources().getDimensionPixelSize(R.dimen.keyguard_clock_top_margin);

View File

@@ -17,11 +17,14 @@ package com.android.keyguard
import android.content.BroadcastReceiver
import android.testing.AndroidTestingRunner
import android.view.View
import android.widget.TextView
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.plugins.ClockAnimations
import com.android.systemui.plugins.ClockController
import com.android.systemui.plugins.ClockEvents
@@ -37,6 +40,9 @@ import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
import java.util.TimeZone
import java.util.concurrent.Executor
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.yield
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Rule
@@ -57,7 +63,7 @@ import org.mockito.junit.MockitoJUnit
class ClockEventControllerTest : SysuiTestCase() {
@JvmField @Rule val mockito = MockitoJUnit.rule()
@Mock private lateinit var statusBarStateController: StatusBarStateController
@Mock private lateinit var keyguardInteractor: KeyguardInteractor
@Mock private lateinit var broadcastDispatcher: BroadcastDispatcher
@Mock private lateinit var batteryController: BatteryController
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@@ -72,8 +78,10 @@ class ClockEventControllerTest : SysuiTestCase() {
@Mock private lateinit var largeClockController: ClockFaceController
@Mock private lateinit var smallClockEvents: ClockFaceEvents
@Mock private lateinit var largeClockEvents: ClockFaceEvents
@Mock private lateinit var parentView: View
private lateinit var repository: FakeKeyguardRepository
private lateinit var clockEventController: ClockEventController
private lateinit var underTest: ClockEventController
@Before
fun setUp() {
@@ -86,8 +94,10 @@ class ClockEventControllerTest : SysuiTestCase() {
whenever(clock.events).thenReturn(events)
whenever(clock.animations).thenReturn(animations)
clockEventController = ClockEventController(
statusBarStateController,
repository = FakeKeyguardRepository()
underTest = ClockEventController(
KeyguardInteractor(repository = repository),
broadcastDispatcher,
batteryController,
keyguardUpdateMonitor,
@@ -98,31 +108,34 @@ class ClockEventControllerTest : SysuiTestCase() {
bgExecutor,
featureFlags
)
underTest.clock = clock
runBlocking(IMMEDIATE) {
underTest.registerListeners(parentView)
repository.setDozing(true)
repository.setDozeAmount(1f)
}
}
@Test
fun clockSet_validateInitialization() {
clockEventController.clock = clock
verify(clock).initialize(any(), anyFloat(), anyFloat())
}
@Test
fun clockUnset_validateState() {
clockEventController.clock = clock
clockEventController.clock = null
underTest.clock = null
assertEquals(clockEventController.clock, null)
assertEquals(underTest.clock, null)
}
@Test
fun themeChanged_verifyClockPaletteUpdated() {
fun themeChanged_verifyClockPaletteUpdated() = runBlocking(IMMEDIATE) {
clockEventController.clock = clock
verify(smallClockEvents).onRegionDarknessChanged(anyBoolean())
verify(largeClockEvents).onRegionDarknessChanged(anyBoolean())
clockEventController.registerListeners()
val captor = argumentCaptor<ConfigurationController.ConfigurationListener>()
verify(configurationController).addCallback(capture(captor))
captor.value.onThemeChanged()
@@ -131,13 +144,11 @@ class ClockEventControllerTest : SysuiTestCase() {
}
@Test
fun fontChanged_verifyFontSizeUpdated() {
fun fontChanged_verifyFontSizeUpdated() = runBlocking(IMMEDIATE) {
clockEventController.clock = clock
verify(smallClockEvents).onRegionDarknessChanged(anyBoolean())
verify(largeClockEvents).onRegionDarknessChanged(anyBoolean())
clockEventController.registerListeners()
val captor = argumentCaptor<ConfigurationController.ConfigurationListener>()
verify(configurationController).addCallback(capture(captor))
captor.value.onDensityOrFontScaleChanged()
@@ -146,10 +157,7 @@ class ClockEventControllerTest : SysuiTestCase() {
}
@Test
fun batteryCallback_keyguardShowingCharging_verifyChargeAnimation() {
clockEventController.clock = clock
clockEventController.registerListeners()
fun batteryCallback_keyguardShowingCharging_verifyChargeAnimation() = runBlocking(IMMEDIATE) {
val batteryCaptor = argumentCaptor<BatteryController.BatteryStateChangeCallback>()
verify(batteryController).addCallback(capture(batteryCaptor))
val keyguardCaptor = argumentCaptor<KeyguardUpdateMonitorCallback>()
@@ -161,26 +169,21 @@ class ClockEventControllerTest : SysuiTestCase() {
}
@Test
fun batteryCallback_keyguardShowingCharging_Duplicate_verifyChargeAnimation() {
clockEventController.clock = clock
clockEventController.registerListeners()
fun batteryCallback_keyguardShowingCharging_Duplicate_verifyChargeAnimation() =
runBlocking(IMMEDIATE) {
val batteryCaptor = argumentCaptor<BatteryController.BatteryStateChangeCallback>()
verify(batteryController).addCallback(capture(batteryCaptor))
val keyguardCaptor = argumentCaptor<KeyguardUpdateMonitorCallback>()
verify(keyguardUpdateMonitor).registerCallback(capture(keyguardCaptor))
keyguardCaptor.value.onKeyguardVisibilityChanged(true)
batteryCaptor.value.onBatteryLevelChanged(10, false, true)
batteryCaptor.value.onBatteryLevelChanged(10, false, true)
val batteryCaptor = argumentCaptor<BatteryController.BatteryStateChangeCallback>()
verify(batteryController).addCallback(capture(batteryCaptor))
val keyguardCaptor = argumentCaptor<KeyguardUpdateMonitorCallback>()
verify(keyguardUpdateMonitor).registerCallback(capture(keyguardCaptor))
keyguardCaptor.value.onKeyguardVisibilityChanged(true)
batteryCaptor.value.onBatteryLevelChanged(10, false, true)
batteryCaptor.value.onBatteryLevelChanged(10, false, true)
verify(animations, times(1)).charge()
}
verify(animations, times(1)).charge()
}
@Test
fun batteryCallback_keyguardHiddenCharging_verifyChargeAnimation() {
clockEventController.clock = clock
clockEventController.registerListeners()
fun batteryCallback_keyguardHiddenCharging_verifyChargeAnimation() = runBlocking(IMMEDIATE) {
val batteryCaptor = argumentCaptor<BatteryController.BatteryStateChangeCallback>()
verify(batteryController).addCallback(capture(batteryCaptor))
val keyguardCaptor = argumentCaptor<KeyguardUpdateMonitorCallback>()
@@ -192,25 +195,20 @@ class ClockEventControllerTest : SysuiTestCase() {
}
@Test
fun batteryCallback_keyguardShowingNotCharging_verifyChargeAnimation() {
clockEventController.clock = clock
clockEventController.registerListeners()
fun batteryCallback_keyguardShowingNotCharging_verifyChargeAnimation() =
runBlocking(IMMEDIATE) {
val batteryCaptor = argumentCaptor<BatteryController.BatteryStateChangeCallback>()
verify(batteryController).addCallback(capture(batteryCaptor))
val keyguardCaptor = argumentCaptor<KeyguardUpdateMonitorCallback>()
verify(keyguardUpdateMonitor).registerCallback(capture(keyguardCaptor))
keyguardCaptor.value.onKeyguardVisibilityChanged(true)
batteryCaptor.value.onBatteryLevelChanged(10, false, false)
val batteryCaptor = argumentCaptor<BatteryController.BatteryStateChangeCallback>()
verify(batteryController).addCallback(capture(batteryCaptor))
val keyguardCaptor = argumentCaptor<KeyguardUpdateMonitorCallback>()
verify(keyguardUpdateMonitor).registerCallback(capture(keyguardCaptor))
keyguardCaptor.value.onKeyguardVisibilityChanged(true)
batteryCaptor.value.onBatteryLevelChanged(10, false, false)
verify(animations, never()).charge()
}
verify(animations, never()).charge()
}
@Test
fun localeCallback_verifyClockNotified() {
clockEventController.clock = clock
clockEventController.registerListeners()
fun localeCallback_verifyClockNotified() = runBlocking(IMMEDIATE) {
val captor = argumentCaptor<BroadcastReceiver>()
verify(broadcastDispatcher).registerReceiver(
capture(captor), any(), eq(null), eq(null), anyInt(), eq(null)
@@ -221,10 +219,7 @@ class ClockEventControllerTest : SysuiTestCase() {
}
@Test
fun keyguardCallback_visibilityChanged_clockDozeCalled() {
clockEventController.clock = clock
clockEventController.registerListeners()
fun keyguardCallback_visibilityChanged_clockDozeCalled() = runBlocking(IMMEDIATE) {
val captor = argumentCaptor<KeyguardUpdateMonitorCallback>()
verify(keyguardUpdateMonitor).registerCallback(capture(captor))
@@ -236,10 +231,7 @@ class ClockEventControllerTest : SysuiTestCase() {
}
@Test
fun keyguardCallback_timeFormat_clockNotified() {
clockEventController.clock = clock
clockEventController.registerListeners()
fun keyguardCallback_timeFormat_clockNotified() = runBlocking(IMMEDIATE) {
val captor = argumentCaptor<KeyguardUpdateMonitorCallback>()
verify(keyguardUpdateMonitor).registerCallback(capture(captor))
captor.value.onTimeFormatChanged("12h")
@@ -248,11 +240,8 @@ class ClockEventControllerTest : SysuiTestCase() {
}
@Test
fun keyguardCallback_timezoneChanged_clockNotified() {
fun keyguardCallback_timezoneChanged_clockNotified() = runBlocking(IMMEDIATE) {
val mockTimeZone = mock<TimeZone>()
clockEventController.clock = clock
clockEventController.registerListeners()
val captor = argumentCaptor<KeyguardUpdateMonitorCallback>()
verify(keyguardUpdateMonitor).registerCallback(capture(captor))
captor.value.onTimeZoneChanged(mockTimeZone)
@@ -261,10 +250,7 @@ class ClockEventControllerTest : SysuiTestCase() {
}
@Test
fun keyguardCallback_userSwitched_clockNotified() {
clockEventController.clock = clock
clockEventController.registerListeners()
fun keyguardCallback_userSwitched_clockNotified() = runBlocking(IMMEDIATE) {
val captor = argumentCaptor<KeyguardUpdateMonitorCallback>()
verify(keyguardUpdateMonitor).registerCallback(capture(captor))
captor.value.onUserSwitchComplete(10)
@@ -273,25 +259,27 @@ class ClockEventControllerTest : SysuiTestCase() {
}
@Test
fun keyguardCallback_verifyKeyguardChanged() {
clockEventController.clock = clock
clockEventController.registerListeners()
fun keyguardCallback_verifyKeyguardChanged() = runBlocking(IMMEDIATE) {
val job = underTest.listenForDozeAmount(this)
repository.setDozeAmount(0.4f)
val captor = argumentCaptor<StatusBarStateController.StateListener>()
verify(statusBarStateController).addCallback(capture(captor))
captor.value.onDozeAmountChanged(0.4f, 0.6f)
yield()
verify(animations).doze(0.4f)
job.cancel()
}
@Test
fun unregisterListeners_validate() {
clockEventController.clock = clock
clockEventController.unregisterListeners()
fun unregisterListeners_validate() = runBlocking(IMMEDIATE) {
underTest.unregisterListeners()
verify(broadcastDispatcher).unregisterReceiver(any())
verify(configurationController).removeCallback(any())
verify(batteryController).removeCallback(any())
verify(keyguardUpdateMonitor).removeCallback(any())
verify(statusBarStateController).removeCallback(any())
}
companion object {
private val IMMEDIATE = Dispatchers.Main.immediate
}
}

View File

@@ -265,6 +265,6 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase {
private void verifyAttachment(VerificationMode times) {
verify(mClockRegistry, times).registerClockChangeListener(
any(ClockRegistry.ClockChangeListener.class));
verify(mClockEventController, times).registerListeners();
verify(mClockEventController, times).registerListeners(mView);
}
}