diff options
8 files changed, 127 insertions, 18 deletions
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt index 7645decfde24..d1108503b556 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt @@ -85,7 +85,8 @@ class DefaultClockController( animations = DefaultClockAnimations(dozeFraction, foldFraction) events.onColorPaletteChanged(resources) events.onTimeZoneChanged(TimeZone.getDefault()) - events.onTimeTick() + smallClock.events.onTimeTick() + largeClock.events.onTimeTick() } open inner class DefaultClockFaceController( @@ -109,6 +110,8 @@ class DefaultClockController( override val events = object : ClockFaceEvents { + override fun onTimeTick() = view.refreshTime() + override fun onRegionDarknessChanged(isRegionDark: Boolean) { this@DefaultClockFaceController.isRegionDark = isRegionDark updateColor() @@ -169,8 +172,6 @@ class DefaultClockController( } inner class DefaultClockEvents : ClockEvents { - override fun onTimeTick() = clocks.forEach { it.refreshTime() } - override fun onTimeFormatChanged(is24Hr: Boolean) = clocks.forEach { it.refreshFormat(is24Hr) } diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt index a2a07095c16c..7727589490d1 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt @@ -66,7 +66,8 @@ interface ClockController { events.onColorPaletteChanged(resources) animations.doze(dozeFraction) animations.fold(foldFraction) - events.onTimeTick() + smallClock.events.onTimeTick() + largeClock.events.onTimeTick() } /** Optional method for dumping debug information */ @@ -87,9 +88,6 @@ interface ClockFaceController { /** Events that should call when various rendering parameters change */ interface ClockEvents { - /** Call every time tick */ - fun onTimeTick() {} - /** Call whenever timezone changes */ fun onTimeZoneChanged(timeZone: TimeZone) {} @@ -131,6 +129,13 @@ interface ClockAnimations { /** Events that have specific data about the related face */ interface ClockFaceEvents { + /** Call every time tick */ + fun onTimeTick() {} + + /** Expected interval between calls to onTimeTick. Can always reduce to PER_MINUTE in AOD. */ + val tickRate: ClockTickRate + get() = ClockTickRate.PER_MINUTE + /** Region Darkness specific to the clock face */ fun onRegionDarknessChanged(isDark: Boolean) {} @@ -150,6 +155,13 @@ interface ClockFaceEvents { fun onTargetRegionChanged(targetRegion: Rect?) {} } +/** Tick rates for clocks */ +enum class ClockTickRate(val value: Int) { + PER_MINUTE(2), // Update the clock once per minute. + PER_SECOND(1), // Update the clock once per second. + PER_FRAME(0), // Update the clock every second. +} + /** Some data about a clock design */ data class ClockMetadata( val clockId: ClockId, diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt index 3a940e95bd12..7a094a76d048 100644 --- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt +++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt @@ -25,6 +25,7 @@ import android.text.format.DateFormat import android.util.TypedValue import android.view.View import android.widget.FrameLayout +import android.view.ViewTreeObserver import androidx.annotation.VisibleForTesting import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle @@ -42,12 +43,15 @@ import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.log.dagger.KeyguardSmallClockLog import com.android.systemui.log.dagger.KeyguardLargeClockLog import com.android.systemui.plugins.ClockController +import com.android.systemui.plugins.ClockFaceController +import com.android.systemui.plugins.ClockTickRate import com.android.systemui.plugins.log.LogBuffer import com.android.systemui.plugins.log.LogLevel.DEBUG import com.android.systemui.shared.regionsampling.RegionSampler 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.util.concurrency.DelayableExecutor import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.DisposableHandle import kotlinx.coroutines.Job @@ -72,7 +76,7 @@ open class ClockEventController @Inject constructor( private val configurationController: ConfigurationController, @Main private val resources: Resources, private val context: Context, - @Main private val mainExecutor: Executor, + @Main private val mainExecutor: DelayableExecutor, @Background private val bgExecutor: Executor, @KeyguardSmallClockLog private val smallLogBuffer: LogBuffer?, @KeyguardLargeClockLog private val largeLogBuffer: LogBuffer?, @@ -94,6 +98,7 @@ open class ClockEventController @Inject constructor( clock?.largeClock?.view?.addOnLayoutChangeListener(mLayoutChangedListener) } updateFontSizes() + updateTimeListeners() } } @@ -208,6 +213,10 @@ open class ClockEventController @Inject constructor( } var regionSampler: RegionSampler? = null + var smallTimeListener: TimeListener? = null + var largeTimeListener: TimeListener? = null + val shouldTimeListenerRun: Boolean + get() = isKeyguardVisible && dozeAmount < DOZE_TICKRATE_THRESHOLD private var smallClockIsDark = true private var largeClockIsDark = true @@ -246,6 +255,9 @@ open class ClockEventController @Inject constructor( clock?.animations?.doze(if (isDozing) 1f else 0f) } } + + smallTimeListener?.update(shouldTimeListenerRun) + largeTimeListener?.update(shouldTimeListenerRun) } override fun onTimeFormatChanged(timeFormat: String) { @@ -285,6 +297,8 @@ open class ClockEventController @Inject constructor( } } } + smallTimeListener?.update(shouldTimeListenerRun) + largeTimeListener?.update(shouldTimeListenerRun) } fun unregisterListeners() { @@ -299,6 +313,25 @@ open class ClockEventController @Inject constructor( batteryController.removeCallback(batteryCallback) keyguardUpdateMonitor.removeCallback(keyguardUpdateMonitorCallback) regionSampler?.stopRegionSampler() + smallTimeListener?.stop() + largeTimeListener?.stop() + } + + private fun updateTimeListeners() { + smallTimeListener?.stop() + largeTimeListener?.stop() + + smallTimeListener = null + largeTimeListener = null + + clock?.smallClock?.let { + smallTimeListener = TimeListener(it, mainExecutor) + smallTimeListener?.update(shouldTimeListenerRun) + } + clock?.largeClock?.let { + largeTimeListener = TimeListener(it, mainExecutor) + largeTimeListener?.update(shouldTimeListenerRun) + } } private fun updateFontSizes() { @@ -308,12 +341,18 @@ open class ClockEventController @Inject constructor( resources.getDimensionPixelSize(R.dimen.large_clock_text_size).toFloat()) } + private fun handleDoze(doze: Float) { + dozeAmount = doze + clock?.animations?.doze(dozeAmount) + smallTimeListener?.update(doze < DOZE_TICKRATE_THRESHOLD) + largeTimeListener?.update(doze < DOZE_TICKRATE_THRESHOLD) + } + @VisibleForTesting internal fun listenForDozeAmount(scope: CoroutineScope): Job { return scope.launch { keyguardInteractor.dozeAmount.collect { - dozeAmount = it - clock?.animations?.doze(dozeAmount) + handleDoze(it) } } } @@ -322,8 +361,7 @@ open class ClockEventController @Inject constructor( internal fun listenForDozeAmountTransition(scope: CoroutineScope): Job { return scope.launch { keyguardTransitionInteractor.dozeAmountTransition.collect { - dozeAmount = it.value - clock?.animations?.doze(dozeAmount) + handleDoze(it.value) } } } @@ -338,8 +376,7 @@ open class ClockEventController @Inject constructor( keyguardTransitionInteractor.anyStateToAodTransition.filter { it.transitionState == TransitionState.FINISHED }.collect { - dozeAmount = 1f - clock?.animations?.doze(dozeAmount) + handleDoze(1f) } } } @@ -359,7 +396,54 @@ open class ClockEventController @Inject constructor( } } + class TimeListener(val clockFace: ClockFaceController, val executor: DelayableExecutor) { + val predrawListener = ViewTreeObserver.OnPreDrawListener { + clockFace.events.onTimeTick() + true + } + + val secondsRunnable = object : Runnable { + override fun run() { + if (!isRunning) { + return + } + + executor.executeDelayed(this, 990) + clockFace.events.onTimeTick() + } + } + + var isRunning: Boolean = false + private set + + fun start() { + if (isRunning) { + return + } + + isRunning = true + when (clockFace.events.tickRate) { + ClockTickRate.PER_MINUTE -> {/* Handled by KeyguardClockSwitchController */} + ClockTickRate.PER_SECOND -> executor.execute(secondsRunnable) + ClockTickRate.PER_FRAME -> { + clockFace.view.viewTreeObserver.addOnPreDrawListener(predrawListener) + clockFace.view.invalidate() + } + } + } + + fun stop() { + if (!isRunning) { return } + + isRunning = false + clockFace.view.viewTreeObserver.removeOnPreDrawListener(predrawListener) + } + + fun update(shouldRun: Boolean) = if (shouldRun) start() else stop() + } + companion object { private val TAG = ClockEventController::class.simpleName!! + private val DOZE_TICKRATE_THRESHOLD = 0.99f } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java index dd42c8ee76d4..3eec5653d191 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java @@ -344,7 +344,8 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS } ClockController clock = getClock(); if (clock != null) { - clock.getEvents().onTimeTick(); + clock.getSmallClock().getEvents().onTimeTick(); + clock.getLargeClock().getEvents().onTimeTick(); } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt index 88085749ece0..adde595247f8 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt @@ -177,7 +177,8 @@ constructor( val receiver = object : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { - clockController.clock?.events?.onTimeTick() + clockController.clock?.smallClock?.events?.onTimeTick() + clockController.clock?.largeClock?.events?.onTimeTick() } } broadcastDispatcher.registerReceiver( diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt index b9c23d4af8a1..43a201735cbb 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt @@ -32,10 +32,12 @@ import com.android.systemui.plugins.ClockController import com.android.systemui.plugins.ClockEvents import com.android.systemui.plugins.ClockFaceController import com.android.systemui.plugins.ClockFaceEvents +import com.android.systemui.plugins.ClockTickRate import com.android.systemui.plugins.log.LogBuffer import com.android.systemui.statusbar.CommandQueue import com.android.systemui.statusbar.policy.BatteryController import com.android.systemui.statusbar.policy.ConfigurationController +import com.android.systemui.util.concurrency.DelayableExecutor import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.argumentCaptor import com.android.systemui.util.mockito.capture @@ -72,7 +74,7 @@ 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: Executor + @Mock private lateinit var mainExecutor: DelayableExecutor @Mock private lateinit var bgExecutor: Executor @Mock private lateinit var featureFlags: FeatureFlags @Mock private lateinit var smallClockController: ClockFaceController @@ -97,6 +99,8 @@ class ClockEventControllerTest : SysuiTestCase() { whenever(largeClockController.events).thenReturn(largeClockEvents) whenever(clock.events).thenReturn(events) whenever(clock.animations).thenReturn(animations) + whenever(smallClockEvents.tickRate).thenReturn(ClockTickRate.PER_MINUTE) + whenever(largeClockEvents.tickRate).thenReturn(ClockTickRate.PER_MINUTE) repository = FakeKeyguardRepository() diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java index 512c3515b587..36b3f897190d 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java @@ -48,6 +48,7 @@ import com.android.systemui.plugins.ClockAnimations; import com.android.systemui.plugins.ClockController; import com.android.systemui.plugins.ClockEvents; import com.android.systemui.plugins.ClockFaceController; +import com.android.systemui.plugins.ClockFaceEvents; import com.android.systemui.plugins.log.LogBuffer; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.shared.clocks.AnimatableClockView; @@ -100,6 +101,8 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase { @Mock private ClockEvents mClockEvents; @Mock + private ClockFaceEvents mClockFaceEvents; + @Mock DumpManager mDumpManager; @Mock ClockEventController mClockEventController; @@ -176,6 +179,8 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase { when(mClockController.getLargeClock()).thenReturn(mLargeClockController); when(mClockController.getSmallClock()).thenReturn(mSmallClockController); when(mClockController.getEvents()).thenReturn(mClockEvents); + when(mSmallClockController.getEvents()).thenReturn(mClockFaceEvents); + when(mLargeClockController.getEvents()).thenReturn(mClockFaceEvents); when(mClockController.getAnimations()).thenReturn(mClockAnimations); when(mClockRegistry.createCurrentClock()).thenReturn(mClockController); when(mClockEventController.getClock()).thenReturn(mClockController); diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt index a7588ddcaeef..cd2efc061b72 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt @@ -114,7 +114,8 @@ class DefaultClockProviderTest : SysuiTestCase() { @Test fun defaultClock_events_onTimeTick() { val clock = provider.createClock(DEFAULT_CLOCK_ID) - clock.events.onTimeTick() + clock.smallClock.events.onTimeTick() + clock.largeClock.events.onTimeTick() verify(mockSmallClockView).refreshTime() verify(mockLargeClockView).refreshTime() |