Allow different tick rates for some clock designs
Some clocks want to refresh faster than the current once-per-minute rate. This allows them to do that on the lockscreen (but not AOD) Bug: 265846399 Test: Manually validated faster ticking analog clock and default clock Test: Manually checked faster updates enabled only in non-aod keyguard Change-Id: Icc14517e8fde6de650c6fdde2126a714bf4e51da
This commit is contained in:
@@ -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) }
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -323,7 +323,8 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
|
||||
}
|
||||
ClockController clock = getClock();
|
||||
if (clock != null) {
|
||||
clock.getEvents().onTimeTick();
|
||||
clock.getSmallClock().getEvents().onTimeTick();
|
||||
clock.getLargeClock().getEvents().onTimeTick();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -47,6 +47,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;
|
||||
@@ -99,6 +100,8 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase {
|
||||
@Mock
|
||||
private ClockEvents mClockEvents;
|
||||
@Mock
|
||||
private ClockFaceEvents mClockFaceEvents;
|
||||
@Mock
|
||||
DumpManager mDumpManager;
|
||||
@Mock
|
||||
ClockEventController mClockEventController;
|
||||
@@ -168,6 +171,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);
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user