Merge "Allow different tick rates for some clock designs" into tm-qpr-dev

This commit is contained in:
Hawkwood Glazier
2023-01-31 20:36:11 +00:00
committed by Android (Google) Code Review
8 changed files with 127 additions and 18 deletions

View File

@@ -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) }

View File

@@ -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,

View File

@@ -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
}
}

View File

@@ -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();
}
}

View File

@@ -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(

View File

@@ -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()

View File

@@ -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);

View File

@@ -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()