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 7645decfde241..d1108503b5565 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 a2a07095c16cd..7727589490d10 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 3a940e95bd12f..7a094a76d0482 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 dd42c8ee76d40..3eec5653d1914 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