Merge "Seperate clockface specific callbacks" into tm-qpr-dev am: 883ecccdfc

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/19930066

Change-Id: I93be6d54612b8c6913ef5b1e1d2cd33727227720
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Hawkwood Glazier
2022-10-11 15:32:39 +00:00
committed by Automerger Merge Worker
12 changed files with 427 additions and 322 deletions

View File

@@ -39,19 +39,19 @@ interface ClockProvider {
fun getClocks(): List<ClockMetadata>
/** Initializes and returns the target clock design */
fun createClock(id: ClockId): Clock
fun createClock(id: ClockId): ClockController
/** A static thumbnail for rendering in some examples */
fun getClockThumbnail(id: ClockId): Drawable?
}
/** Interface for controlling an active clock */
interface Clock {
interface ClockController {
/** A small version of the clock, appropriate for smaller viewports */
val smallClock: View
val smallClock: ClockFaceController
/** A large version of the clock, appropriate when a bigger viewport is available */
val largeClock: View
val largeClock: ClockFaceController
/** Events that clocks may need to respond to */
val events: ClockEvents
@@ -61,7 +61,7 @@ interface Clock {
/** Initializes various rendering parameters. If never called, provides reasonable defaults. */
fun initialize(resources: Resources, dozeFraction: Float, foldFraction: Float) {
events.onColorPaletteChanged(resources, true, true)
events.onColorPaletteChanged(resources)
animations.doze(dozeFraction)
animations.fold(foldFraction)
events.onTimeTick()
@@ -71,10 +71,19 @@ interface Clock {
fun dump(pw: PrintWriter) { }
}
/** Interface for a specific clock face version rendered by the clock */
interface ClockFaceController {
/** View that renders the clock face */
val view: View
/** Events specific to this clock face */
val events: ClockFaceEvents
}
/** Events that should call when various rendering parameters change */
interface ClockEvents {
/** Call every time tick */
fun onTimeTick()
fun onTimeTick() { }
/** Call whenever timezone changes */
fun onTimeZoneChanged(timeZone: TimeZone) { }
@@ -89,11 +98,7 @@ interface ClockEvents {
fun onFontSettingChanged() { }
/** Call whenever the color palette should update */
fun onColorPaletteChanged(
resources: Resources,
smallClockIsDark: Boolean,
largeClockIsDark: Boolean
) { }
fun onColorPaletteChanged(resources: Resources) { }
}
/** Methods which trigger various clock animations */
@@ -111,6 +116,12 @@ interface ClockAnimations {
fun charge() { }
}
/** Events that have specific data about the related face */
interface ClockFaceEvents {
/** Region Darkness specific to the clock face */
fun onRegionDarknessChanged(isDark: Boolean) { }
}
/** Some data about a clock design */
data class ClockMetadata(
val clockId: ClockId,

View File

@@ -22,7 +22,7 @@ import android.os.UserHandle
import android.provider.Settings
import android.util.Log
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.Clock
import com.android.systemui.plugins.ClockController
import com.android.systemui.plugins.ClockId
import com.android.systemui.plugins.ClockMetadata
import com.android.systemui.plugins.ClockProvider
@@ -33,7 +33,7 @@ import com.google.gson.Gson
import javax.inject.Inject
private val TAG = ClockRegistry::class.simpleName
private val DEBUG = true
private const val DEBUG = true
/** ClockRegistry aggregates providers and plugins */
open class ClockRegistry(
@@ -130,6 +130,10 @@ open class ClockRegistry(
}
availableClocks[id] = ClockInfo(clock, provider)
if (DEBUG) {
Log.i(TAG, "Added ${clock.clockId}")
}
if (currentId == id) {
if (DEBUG) {
Log.i(TAG, "Current clock ($currentId) was connected")
@@ -143,6 +147,9 @@ open class ClockRegistry(
val currentId = currentClockId
for (clock in provider.getClocks()) {
availableClocks.remove(clock.clockId)
if (DEBUG) {
Log.i(TAG, "Removed ${clock.clockId}")
}
if (currentId == clock.clockId) {
Log.w(TAG, "Current clock ($currentId) was disconnected")
@@ -161,7 +168,7 @@ open class ClockRegistry(
fun getClockThumbnail(clockId: ClockId): Drawable? =
availableClocks[clockId]?.provider?.getClockThumbnail(clockId)
fun createExampleClock(clockId: ClockId): Clock? = createClock(clockId)
fun createExampleClock(clockId: ClockId): ClockController? = createClock(clockId)
fun registerClockChangeListener(listener: ClockChangeListener) =
clockChangeListeners.add(listener)
@@ -169,11 +176,14 @@ open class ClockRegistry(
fun unregisterClockChangeListener(listener: ClockChangeListener) =
clockChangeListeners.remove(listener)
fun createCurrentClock(): Clock {
fun createCurrentClock(): ClockController {
val clockId = currentClockId
if (isEnabled && clockId.isNotEmpty()) {
val clock = createClock(clockId)
if (clock != null) {
if (DEBUG) {
Log.i(TAG, "Rendering clock $clockId")
}
return clock
} else {
Log.e(TAG, "Clock $clockId not found; using default")
@@ -183,7 +193,7 @@ open class ClockRegistry(
return createClock(DEFAULT_CLOCK_ID)!!
}
private fun createClock(clockId: ClockId): Clock? =
private fun createClock(clockId: ClockId): ClockController? =
availableClocks[clockId]?.provider?.createClock(clockId)
private data class ClockInfo(

View File

@@ -0,0 +1,234 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package com.android.systemui.shared.clocks
import android.content.Context
import android.content.res.Resources
import android.graphics.Color
import android.icu.text.NumberFormat
import android.util.TypedValue
import android.view.LayoutInflater
import android.widget.FrameLayout
import androidx.annotation.VisibleForTesting
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.shared.R
import java.io.PrintWriter
import java.util.Locale
import java.util.TimeZone
private val TAG = DefaultClockController::class.simpleName
/**
* Controls the default clock visuals.
*
* This serves as an adapter between the clock interface and the AnimatableClockView used by the
* existing lockscreen clock.
*/
class DefaultClockController(
ctx: Context,
private val layoutInflater: LayoutInflater,
private val resources: Resources,
) : ClockController {
override val smallClock: DefaultClockFaceController
override val largeClock: LargeClockFaceController
private val clocks: List<AnimatableClockView>
private val burmeseNf = NumberFormat.getInstance(Locale.forLanguageTag("my"))
private val burmeseNumerals = burmeseNf.format(FORMAT_NUMBER.toLong())
private val burmeseLineSpacing =
resources.getFloat(R.dimen.keyguard_clock_line_spacing_scale_burmese)
private val defaultLineSpacing = resources.getFloat(R.dimen.keyguard_clock_line_spacing_scale)
override val events: DefaultClockEvents
override lateinit var animations: DefaultClockAnimations
private set
init {
val parent = FrameLayout(ctx)
smallClock =
DefaultClockFaceController(
layoutInflater.inflate(R.layout.clock_default_small, parent, false)
as AnimatableClockView
)
largeClock =
LargeClockFaceController(
layoutInflater.inflate(R.layout.clock_default_large, parent, false)
as AnimatableClockView
)
clocks = listOf(smallClock.view, largeClock.view)
events = DefaultClockEvents()
animations = DefaultClockAnimations(0f, 0f)
events.onLocaleChanged(Locale.getDefault())
}
override fun initialize(resources: Resources, dozeFraction: Float, foldFraction: Float) {
largeClock.recomputePadding()
animations = DefaultClockAnimations(dozeFraction, foldFraction)
events.onColorPaletteChanged(resources)
events.onTimeZoneChanged(TimeZone.getDefault())
events.onTimeTick()
}
open inner class DefaultClockFaceController(
override val view: AnimatableClockView,
) : ClockFaceController {
// MAGENTA is a placeholder, and will be assigned correctly in initialize
private var currentColor = Color.MAGENTA
private var isRegionDark = false
init {
view.setColors(currentColor, currentColor)
}
override val events =
object : ClockFaceEvents {
override fun onRegionDarknessChanged(isRegionDark: Boolean) {
this@DefaultClockFaceController.isRegionDark = isRegionDark
updateColor()
}
}
fun updateColor() {
val color =
if (isRegionDark) {
resources.getColor(android.R.color.system_accent1_100)
} else {
resources.getColor(android.R.color.system_accent2_600)
}
if (currentColor == color) {
return
}
currentColor = color
view.setColors(DOZE_COLOR, color)
view.animateAppearOnLockscreen()
}
}
inner class LargeClockFaceController(
view: AnimatableClockView,
) : DefaultClockFaceController(view) {
fun recomputePadding() {
val lp = view.getLayoutParams() as FrameLayout.LayoutParams
lp.topMargin = (-0.5f * view.bottom).toInt()
view.setLayoutParams(lp)
}
}
inner class DefaultClockEvents : ClockEvents {
override fun onTimeTick() = clocks.forEach { it.refreshTime() }
override fun onTimeFormatChanged(is24Hr: Boolean) =
clocks.forEach { it.refreshFormat(is24Hr) }
override fun onTimeZoneChanged(timeZone: TimeZone) =
clocks.forEach { it.onTimeZoneChanged(timeZone) }
override fun onFontSettingChanged() {
smallClock.view.setTextSize(
TypedValue.COMPLEX_UNIT_PX,
resources.getDimensionPixelSize(R.dimen.small_clock_text_size).toFloat()
)
largeClock.view.setTextSize(
TypedValue.COMPLEX_UNIT_PX,
resources.getDimensionPixelSize(R.dimen.large_clock_text_size).toFloat()
)
largeClock.recomputePadding()
}
override fun onColorPaletteChanged(resources: Resources) {
largeClock.updateColor()
smallClock.updateColor()
}
override fun onLocaleChanged(locale: Locale) {
val nf = NumberFormat.getInstance(locale)
if (nf.format(FORMAT_NUMBER.toLong()) == burmeseNumerals) {
clocks.forEach { it.setLineSpacingScale(burmeseLineSpacing) }
} else {
clocks.forEach { it.setLineSpacingScale(defaultLineSpacing) }
}
clocks.forEach { it.refreshFormat() }
}
}
inner class DefaultClockAnimations(
dozeFraction: Float,
foldFraction: Float,
) : ClockAnimations {
private var foldState = AnimationState(0f)
private var dozeState = AnimationState(0f)
init {
dozeState = AnimationState(dozeFraction)
foldState = AnimationState(foldFraction)
if (foldState.isActive) {
clocks.forEach { it.animateFoldAppear(false) }
} else {
clocks.forEach { it.animateDoze(dozeState.isActive, false) }
}
}
override fun enter() {
if (!dozeState.isActive) {
clocks.forEach { it.animateAppearOnLockscreen() }
}
}
override fun charge() = clocks.forEach { it.animateCharge { dozeState.isActive } }
override fun fold(fraction: Float) {
val (hasChanged, hasJumped) = foldState.update(fraction)
if (hasChanged) {
clocks.forEach { it.animateFoldAppear(!hasJumped) }
}
}
override fun doze(fraction: Float) {
val (hasChanged, hasJumped) = dozeState.update(fraction)
if (hasChanged) {
clocks.forEach { it.animateDoze(dozeState.isActive, !hasJumped) }
}
}
}
private class AnimationState(
var fraction: Float,
) {
var isActive: Boolean = fraction < 0.5f
fun update(newFraction: Float): Pair<Boolean, Boolean> {
val wasActive = isActive
val hasJumped =
(fraction == 0f && newFraction == 1f) || (fraction == 1f && newFraction == 0f)
isActive = newFraction > fraction
fraction = newFraction
return Pair(wasActive != isActive, hasJumped)
}
}
override fun dump(pw: PrintWriter) = clocks.forEach { it.dump(pw) }
companion object {
@VisibleForTesting const val DOZE_COLOR = Color.WHITE
private const val FORMAT_NUMBER = 1234567890
}
}

View File

@@ -15,24 +15,14 @@ package com.android.systemui.shared.clocks
import android.content.Context
import android.content.res.Resources
import android.graphics.Color
import android.graphics.drawable.Drawable
import android.icu.text.NumberFormat
import android.util.TypedValue
import android.view.LayoutInflater
import android.widget.FrameLayout
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.Clock
import com.android.systemui.plugins.ClockAnimations
import com.android.systemui.plugins.ClockEvents
import com.android.systemui.plugins.ClockController
import com.android.systemui.plugins.ClockId
import com.android.systemui.plugins.ClockMetadata
import com.android.systemui.plugins.ClockProvider
import com.android.systemui.shared.R
import java.io.PrintWriter
import java.util.Locale
import java.util.TimeZone
import javax.inject.Inject
private val TAG = DefaultClockProvider::class.simpleName
@@ -48,11 +38,12 @@ class DefaultClockProvider @Inject constructor(
override fun getClocks(): List<ClockMetadata> =
listOf(ClockMetadata(DEFAULT_CLOCK_ID, DEFAULT_CLOCK_NAME))
override fun createClock(id: ClockId): Clock {
override fun createClock(id: ClockId): ClockController {
if (id != DEFAULT_CLOCK_ID) {
throw IllegalArgumentException("$id is unsupported by $TAG")
}
return DefaultClock(ctx, layoutInflater, resources)
return DefaultClockController(ctx, layoutInflater, resources)
}
override fun getClockThumbnail(id: ClockId): Drawable? {
@@ -64,190 +55,3 @@ class DefaultClockProvider @Inject constructor(
return resources.getDrawable(R.drawable.clock_default_thumbnail, null)
}
}
/**
* Controls the default clock visuals.
*
* This serves as an adapter between the clock interface and the
* AnimatableClockView used by the existing lockscreen clock.
*/
class DefaultClock(
ctx: Context,
private val layoutInflater: LayoutInflater,
private val resources: Resources
) : Clock {
override val smallClock: AnimatableClockView
override val largeClock: AnimatableClockView
private val clocks get() = listOf(smallClock, largeClock)
private val burmeseNf = NumberFormat.getInstance(Locale.forLanguageTag("my"))
private val burmeseNumerals = burmeseNf.format(FORMAT_NUMBER.toLong())
private val burmeseLineSpacing =
resources.getFloat(R.dimen.keyguard_clock_line_spacing_scale_burmese)
private val defaultLineSpacing = resources.getFloat(R.dimen.keyguard_clock_line_spacing_scale)
override val events: ClockEvents
override lateinit var animations: ClockAnimations
private set
private var smallRegionDarkness = false
private var largeRegionDarkness = false
init {
val parent = FrameLayout(ctx)
smallClock = layoutInflater.inflate(
R.layout.clock_default_small,
parent,
false
) as AnimatableClockView
largeClock = layoutInflater.inflate(
R.layout.clock_default_large,
parent,
false
) as AnimatableClockView
events = DefaultClockEvents()
animations = DefaultClockAnimations(0f, 0f)
events.onLocaleChanged(Locale.getDefault())
// DOZE_COLOR is a placeholder, and will be assigned correctly in initialize
clocks.forEach { it.setColors(DOZE_COLOR, DOZE_COLOR) }
}
override fun initialize(resources: Resources, dozeFraction: Float, foldFraction: Float) {
recomputePadding()
animations = DefaultClockAnimations(dozeFraction, foldFraction)
events.onColorPaletteChanged(resources, true, true)
events.onTimeZoneChanged(TimeZone.getDefault())
events.onTimeTick()
}
inner class DefaultClockEvents() : ClockEvents {
override fun onTimeTick() = clocks.forEach { it.refreshTime() }
override fun onTimeFormatChanged(is24Hr: Boolean) =
clocks.forEach { it.refreshFormat(is24Hr) }
override fun onTimeZoneChanged(timeZone: TimeZone) =
clocks.forEach { it.onTimeZoneChanged(timeZone) }
override fun onFontSettingChanged() {
smallClock.setTextSize(
TypedValue.COMPLEX_UNIT_PX,
resources.getDimensionPixelSize(R.dimen.small_clock_text_size).toFloat()
)
largeClock.setTextSize(
TypedValue.COMPLEX_UNIT_PX,
resources.getDimensionPixelSize(R.dimen.large_clock_text_size).toFloat()
)
recomputePadding()
}
override fun onColorPaletteChanged(
resources: Resources,
smallClockIsDark: Boolean,
largeClockIsDark: Boolean
) {
if (smallRegionDarkness != smallClockIsDark) {
smallRegionDarkness = smallClockIsDark
updateClockColor(smallClock, smallClockIsDark)
}
if (largeRegionDarkness != largeClockIsDark) {
largeRegionDarkness = largeClockIsDark
updateClockColor(largeClock, largeClockIsDark)
}
}
override fun onLocaleChanged(locale: Locale) {
val nf = NumberFormat.getInstance(locale)
if (nf.format(FORMAT_NUMBER.toLong()) == burmeseNumerals) {
clocks.forEach { it.setLineSpacingScale(burmeseLineSpacing) }
} else {
clocks.forEach { it.setLineSpacingScale(defaultLineSpacing) }
}
clocks.forEach { it.refreshFormat() }
}
}
inner class DefaultClockAnimations(
dozeFraction: Float,
foldFraction: Float
) : ClockAnimations {
private var foldState = AnimationState(0f)
private var dozeState = AnimationState(0f)
init {
dozeState = AnimationState(dozeFraction)
foldState = AnimationState(foldFraction)
if (foldState.isActive) {
clocks.forEach { it.animateFoldAppear(false) }
} else {
clocks.forEach { it.animateDoze(dozeState.isActive, false) }
}
}
override fun enter() {
if (!dozeState.isActive) {
clocks.forEach { it.animateAppearOnLockscreen() }
}
}
override fun charge() = clocks.forEach { it.animateCharge { dozeState.isActive } }
override fun fold(fraction: Float) {
val (hasChanged, hasJumped) = foldState.update(fraction)
if (hasChanged) {
clocks.forEach { it.animateFoldAppear(!hasJumped) }
}
}
override fun doze(fraction: Float) {
val (hasChanged, hasJumped) = dozeState.update(fraction)
if (hasChanged) {
clocks.forEach { it.animateDoze(dozeState.isActive, !hasJumped) }
}
}
}
private class AnimationState(
var fraction: Float
) {
var isActive: Boolean = fraction < 0.5f
fun update(newFraction: Float): Pair<Boolean, Boolean> {
val wasActive = isActive
val hasJumped = (fraction == 0f && newFraction == 1f) ||
(fraction == 1f && newFraction == 0f)
isActive = newFraction > fraction
fraction = newFraction
return Pair(wasActive != isActive, hasJumped)
}
}
private fun updateClockColor(clock: AnimatableClockView, isRegionDark: Boolean) {
val color = if (isRegionDark) {
resources.getColor(android.R.color.system_accent1_100)
} else {
resources.getColor(android.R.color.system_accent2_600)
}
clock.setColors(DOZE_COLOR, color)
clock.animateAppearOnLockscreen()
}
private fun recomputePadding() {
val lp = largeClock.getLayoutParams() as FrameLayout.LayoutParams
lp.topMargin = (-0.5f * largeClock.bottom).toInt()
largeClock.setLayoutParams(lp)
}
override fun dump(pw: PrintWriter) = clocks.forEach { it.dump(pw) }
companion object {
@VisibleForTesting const val DOZE_COLOR = Color.WHITE
private const val FORMAT_NUMBER = 1234567890
}
}

View File

@@ -27,7 +27,7 @@ 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.Clock
import com.android.systemui.plugins.ClockController
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.shared.regionsampling.RegionSamplingInstance
import com.android.systemui.statusbar.policy.BatteryController
@@ -44,18 +44,18 @@ import javax.inject.Inject
* [KeyguardClockSwitchController]. Functionality is forked from [AnimatableClockController].
*/
open class ClockEventController @Inject constructor(
private val statusBarStateController: StatusBarStateController,
private val broadcastDispatcher: BroadcastDispatcher,
private val batteryController: BatteryController,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
private val configurationController: ConfigurationController,
@Main private val resources: Resources,
private val context: Context,
@Main private val mainExecutor: Executor,
@Background private val bgExecutor: Executor,
private val featureFlags: FeatureFlags
private val statusBarStateController: StatusBarStateController,
private val broadcastDispatcher: BroadcastDispatcher,
private val batteryController: BatteryController,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
private val configurationController: ConfigurationController,
@Main private val resources: Resources,
private val context: Context,
@Main private val mainExecutor: Executor,
@Background private val bgExecutor: Executor,
private val featureFlags: FeatureFlags,
) {
var clock: Clock? = null
var clock: ClockController? = null
set(value) {
field = value
if (value != null) {
@@ -74,42 +74,45 @@ open class ClockEventController @Inject constructor(
private val regionSamplingEnabled =
featureFlags.isEnabled(com.android.systemui.flags.Flags.REGION_SAMPLING)
private val updateFun = object : RegionSamplingInstance.UpdateColorCallback {
override fun updateColors() {
if (regionSamplingEnabled) {
smallClockIsDark = smallRegionSamplingInstance.currentRegionDarkness().isDark
largeClockIsDark = largeRegionSamplingInstance.currentRegionDarkness().isDark
} else {
val isLightTheme = TypedValue()
context.theme.resolveAttribute(android.R.attr.isLightTheme, isLightTheme, true)
smallClockIsDark = isLightTheme.data == 0
largeClockIsDark = isLightTheme.data == 0
}
clock?.events?.onColorPaletteChanged(resources, smallClockIsDark, largeClockIsDark)
private fun updateColors() {
if (regionSamplingEnabled && smallRegionSampler != null && largeRegionSampler != null) {
smallClockIsDark = smallRegionSampler!!.currentRegionDarkness().isDark
largeClockIsDark = largeRegionSampler!!.currentRegionDarkness().isDark
} else {
val isLightTheme = TypedValue()
context.theme.resolveAttribute(android.R.attr.isLightTheme, isLightTheme, true)
smallClockIsDark = isLightTheme.data == 0
largeClockIsDark = isLightTheme.data == 0
}
clock?.smallClock?.events?.onRegionDarknessChanged(smallClockIsDark)
clock?.largeClock?.events?.onRegionDarknessChanged(largeClockIsDark)
}
fun updateRegionSamplers(currentClock: Clock?) {
smallRegionSamplingInstance = createRegionSampler(
currentClock?.smallClock,
private fun updateRegionSamplers(currentClock: ClockController?) {
smallRegionSampler?.stopRegionSampler()
largeRegionSampler?.stopRegionSampler()
smallRegionSampler = createRegionSampler(
currentClock?.smallClock?.view,
mainExecutor,
bgExecutor,
regionSamplingEnabled,
updateFun
::updateColors
)
largeRegionSamplingInstance = createRegionSampler(
currentClock?.largeClock,
largeRegionSampler = createRegionSampler(
currentClock?.largeClock?.view,
mainExecutor,
bgExecutor,
regionSamplingEnabled,
updateFun
::updateColors
)
smallRegionSamplingInstance.startRegionSampler()
largeRegionSamplingInstance.startRegionSampler()
smallRegionSampler!!.startRegionSampler()
largeRegionSampler!!.startRegionSampler()
updateFun.updateColors()
updateColors()
}
protected open fun createRegionSampler(
@@ -117,25 +120,29 @@ open class ClockEventController @Inject constructor(
mainExecutor: Executor?,
bgExecutor: Executor?,
regionSamplingEnabled: Boolean,
updateFun: RegionSamplingInstance.UpdateColorCallback
updateColors: () -> Unit
): RegionSamplingInstance {
return RegionSamplingInstance(
sampledView,
mainExecutor,
bgExecutor,
regionSamplingEnabled,
updateFun)
object : RegionSamplingInstance.UpdateColorCallback {
override fun updateColors() {
updateColors()
}
})
}
lateinit var smallRegionSamplingInstance: RegionSamplingInstance
lateinit var largeRegionSamplingInstance: RegionSamplingInstance
var smallRegionSampler: RegionSamplingInstance? = null
var largeRegionSampler: RegionSamplingInstance? = null
private var smallClockIsDark = true
private var largeClockIsDark = true
private val configListener = object : ConfigurationController.ConfigurationListener {
override fun onThemeChanged() {
updateFun.updateColors()
clock?.events?.onColorPaletteChanged(resources)
}
override fun onDensityOrFontScaleChanged() {
@@ -204,8 +211,8 @@ open class ClockEventController @Inject constructor(
batteryController.addCallback(batteryCallback)
keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback)
statusBarStateController.addCallback(statusBarStateListener)
smallRegionSamplingInstance.startRegionSampler()
largeRegionSamplingInstance.startRegionSampler()
smallRegionSampler?.startRegionSampler()
largeRegionSampler?.startRegionSampler()
}
fun unregisterListeners() {
@@ -214,8 +221,8 @@ open class ClockEventController @Inject constructor(
batteryController.removeCallback(batteryCallback)
keyguardUpdateMonitor.removeCallback(keyguardUpdateMonitorCallback)
statusBarStateController.removeCallback(statusBarStateListener)
smallRegionSamplingInstance.stopRegionSampler()
largeRegionSamplingInstance.stopRegionSampler()
smallRegionSampler?.stopRegionSampler()
largeRegionSampler?.stopRegionSampler()
}
/**
@@ -224,8 +231,8 @@ open class ClockEventController @Inject constructor(
fun dump(pw: PrintWriter) {
pw.println(this)
clock?.dump(pw)
smallRegionSamplingInstance.dump(pw)
largeRegionSamplingInstance.dump(pw)
smallRegionSampler?.dump(pw)
largeRegionSampler?.dump(pw)
}
companion object {

View File

@@ -17,7 +17,7 @@ import androidx.annotation.VisibleForTesting;
import com.android.keyguard.dagger.KeyguardStatusViewScope;
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
import com.android.systemui.plugins.Clock;
import com.android.systemui.plugins.ClockController;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -94,7 +94,7 @@ public class KeyguardClockSwitch extends RelativeLayout {
onDensityOrFontScaleChanged();
}
void setClock(Clock clock, int statusBarState) {
void setClock(ClockController clock, int statusBarState) {
// Disconnect from existing plugin.
mSmallClockFrame.removeAllViews();
mLargeClockFrame.removeAllViews();
@@ -105,8 +105,8 @@ public class KeyguardClockSwitch extends RelativeLayout {
}
// Attach small and big clock views to hierarchy.
mSmallClockFrame.addView(clock.getSmallClock());
mLargeClockFrame.addView(clock.getLargeClock());
mSmallClockFrame.addView(clock.getSmallClock().getView());
mLargeClockFrame.addView(clock.getLargeClock().getView());
}
private void updateClockViews(boolean useLargeClock, boolean animate) {

View File

@@ -40,7 +40,7 @@ import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.plugins.Clock;
import com.android.systemui.plugins.ClockController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shared.clocks.ClockRegistry;
import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController;
@@ -262,7 +262,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
mCurrentClockSize = clockSize;
Clock clock = getClock();
ClockController clock = getClock();
boolean appeared = mView.switchToClock(clockSize, animate);
if (clock != null && animate && appeared && clockSize == LARGE) {
clock.getAnimations().enter();
@@ -273,7 +273,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
* Animates the clock view between folded and unfolded states
*/
public void animateFoldToAod(float foldFraction) {
Clock clock = getClock();
ClockController clock = getClock();
if (clock != null) {
clock.getAnimations().fold(foldFraction);
}
@@ -286,7 +286,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
if (mSmartspaceController != null) {
mSmartspaceController.requestSmartspaceUpdate();
}
Clock clock = getClock();
ClockController clock = getClock();
if (clock != null) {
clock.getEvents().onTimeTick();
}
@@ -319,17 +319,17 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
* We can't directly getBottom() because clock changes positions in AOD for burn-in
*/
int getClockBottom(int statusBarHeaderHeight) {
Clock clock = getClock();
ClockController clock = getClock();
if (clock == null) {
return 0;
}
if (mLargeClockFrame.getVisibility() == View.VISIBLE) {
int frameHeight = mLargeClockFrame.getHeight();
int clockHeight = clock.getLargeClock().getHeight();
int clockHeight = clock.getLargeClock().getView().getHeight();
return frameHeight / 2 + clockHeight / 2;
} else {
int clockHeight = clock.getSmallClock().getHeight();
int clockHeight = clock.getSmallClock().getView().getHeight();
return clockHeight + statusBarHeaderHeight + mKeyguardClockTopMargin;
}
}
@@ -338,15 +338,15 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
* Get the height of the currently visible clock on the keyguard.
*/
int getClockHeight() {
Clock clock = getClock();
ClockController clock = getClock();
if (clock == null) {
return 0;
}
if (mLargeClockFrame.getVisibility() == View.VISIBLE) {
return clock.getLargeClock().getHeight();
return clock.getLargeClock().getView().getHeight();
} else {
return clock.getSmallClock().getHeight();
return clock.getSmallClock().getView().getHeight();
}
}
@@ -361,12 +361,12 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
mNotificationIconAreaController.setupAodIcons(nic);
}
private void setClock(Clock clock) {
private void setClock(ClockController clock) {
mClockEventController.setClock(clock);
mView.setClock(clock, mStatusBarStateController.getState());
}
private Clock getClock() {
private ClockController getClock() {
return mClockEventController.getClock();
}
@@ -398,7 +398,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
pw.println("currentClockSizeLarge=" + (mCurrentClockSize == LARGE));
pw.println("mCanShowDoubleLineClock=" + mCanShowDoubleLineClock);
Clock clock = getClock();
ClockController clock = getClock();
if (clock != null) {
clock.dump(pw);
}

View File

@@ -22,9 +22,11 @@ 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.plugins.Clock
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.statusbar.StatusBarStateController
import com.android.systemui.statusbar.policy.BatteryController
import com.android.systemui.statusbar.policy.ConfigurationController
@@ -40,6 +42,7 @@ import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.ArgumentMatchers.anyFloat
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
@@ -61,17 +64,25 @@ class ClockEventControllerTest : SysuiTestCase() {
@Mock private lateinit var configurationController: ConfigurationController
@Mock private lateinit var animations: ClockAnimations
@Mock private lateinit var events: ClockEvents
@Mock private lateinit var clock: Clock
@Mock private lateinit var clock: ClockController
@Mock private lateinit var mainExecutor: Executor
@Mock private lateinit var bgExecutor: Executor
@Mock private lateinit var featureFlags: FeatureFlags
@Mock private lateinit var smallClockController: ClockFaceController
@Mock private lateinit var largeClockController: ClockFaceController
@Mock private lateinit var smallClockEvents: ClockFaceEvents
@Mock private lateinit var largeClockEvents: ClockFaceEvents
private lateinit var clockEventController: ClockEventController
@Before
fun setUp() {
whenever(clock.smallClock).thenReturn(TextView(context))
whenever(clock.largeClock).thenReturn(TextView(context))
whenever(clock.smallClock).thenReturn(smallClockController)
whenever(clock.largeClock).thenReturn(largeClockController)
whenever(smallClockController.view).thenReturn(TextView(context))
whenever(largeClockController.view).thenReturn(TextView(context))
whenever(smallClockController.events).thenReturn(smallClockEvents)
whenever(largeClockController.events).thenReturn(largeClockEvents)
whenever(clock.events).thenReturn(events)
whenever(clock.animations).thenReturn(animations)
@@ -107,7 +118,8 @@ class ClockEventControllerTest : SysuiTestCase() {
@Test
fun themeChanged_verifyClockPaletteUpdated() {
clockEventController.clock = clock
verify(events).onColorPaletteChanged(any(), any(), any())
verify(smallClockEvents).onRegionDarknessChanged(anyBoolean())
verify(largeClockEvents).onRegionDarknessChanged(anyBoolean())
clockEventController.registerListeners()
@@ -115,13 +127,14 @@ class ClockEventControllerTest : SysuiTestCase() {
verify(configurationController).addCallback(capture(captor))
captor.value.onThemeChanged()
verify(events, times(2)).onColorPaletteChanged(any(), any(), any())
verify(events).onColorPaletteChanged(any())
}
@Test
fun fontChanged_verifyFontSizeUpdated() {
clockEventController.clock = clock
verify(events).onColorPaletteChanged(any(), any(), any())
verify(smallClockEvents).onRegionDarknessChanged(anyBoolean())
verify(largeClockEvents).onRegionDarknessChanged(anyBoolean())
clockEventController.registerListeners()

View File

@@ -45,7 +45,7 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.plugins.Clock;
import com.android.systemui.plugins.ClockController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shared.clocks.AnimatableClockView;
import com.android.systemui.shared.clocks.ClockRegistry;
@@ -87,7 +87,7 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase {
@Mock
KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
@Mock
private Clock mClock;
private ClockController mClock;
@Mock
DumpManager mDumpManager;
@Mock

View File

@@ -41,7 +41,8 @@ import android.widget.TextView;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.Clock;
import com.android.systemui.plugins.ClockController;
import com.android.systemui.plugins.ClockFaceController;
import com.android.systemui.statusbar.StatusBarState;
import org.junit.Before;
@@ -61,7 +62,13 @@ public class KeyguardClockSwitchTest extends SysuiTestCase {
ViewGroup mMockKeyguardSliceView;
@Mock
Clock mClock;
ClockController mClock;
@Mock
ClockFaceController mSmallClock;
@Mock
ClockFaceController mLargeClock;
private FrameLayout mSmallClockFrame;
private FrameLayout mLargeClockFrame;
@@ -75,8 +82,11 @@ public class KeyguardClockSwitchTest extends SysuiTestCase {
when(mMockKeyguardSliceView.findViewById(R.id.keyguard_status_area))
.thenReturn(mMockKeyguardSliceView);
when(mClock.getSmallClock()).thenReturn(new TextView(getContext()));
when(mClock.getLargeClock()).thenReturn(new TextView(getContext()));
when(mClock.getSmallClock()).thenReturn(mSmallClock);
when(mClock.getLargeClock()).thenReturn(mLargeClock);
when(mSmallClock.getView()).thenReturn(new TextView(getContext()));
when(mLargeClock.getView()).thenReturn(new TextView(getContext()));
LayoutInflater layoutInflater = LayoutInflater.from(getContext());
layoutInflater.setPrivateFactory(new LayoutInflater.Factory2() {
@@ -124,41 +134,49 @@ public class KeyguardClockSwitchTest extends SysuiTestCase {
public void onPluginConnected_showClock() {
mKeyguardClockSwitch.setClock(mClock, StatusBarState.KEYGUARD);
assertEquals(mClock.getSmallClock().getParent(), mSmallClockFrame);
assertEquals(mClock.getLargeClock().getParent(), mLargeClockFrame);
assertEquals(mClock.getSmallClock().getView().getParent(), mSmallClockFrame);
assertEquals(mClock.getLargeClock().getView().getParent(), mLargeClockFrame);
}
@Test
public void onPluginConnected_showSecondPluginClock() {
// GIVEN a plugin has already connected
Clock otherClock = mock(Clock.class);
when(otherClock.getSmallClock()).thenReturn(new TextView(getContext()));
when(otherClock.getLargeClock()).thenReturn(new TextView(getContext()));
ClockController otherClock = mock(ClockController.class);
ClockFaceController smallClock = mock(ClockFaceController.class);
ClockFaceController largeClock = mock(ClockFaceController.class);
when(otherClock.getSmallClock()).thenReturn(smallClock);
when(otherClock.getLargeClock()).thenReturn(largeClock);
when(smallClock.getView()).thenReturn(new TextView(getContext()));
when(largeClock.getView()).thenReturn(new TextView(getContext()));
mKeyguardClockSwitch.setClock(mClock, StatusBarState.KEYGUARD);
mKeyguardClockSwitch.setClock(otherClock, StatusBarState.KEYGUARD);
// THEN only the view from the second plugin should be a child of KeyguardClockSwitch.
assertThat(otherClock.getSmallClock().getParent()).isEqualTo(mSmallClockFrame);
assertThat(otherClock.getLargeClock().getParent()).isEqualTo(mLargeClockFrame);
assertThat(mClock.getSmallClock().getParent()).isNull();
assertThat(mClock.getLargeClock().getParent()).isNull();
assertThat(otherClock.getSmallClock().getView().getParent()).isEqualTo(mSmallClockFrame);
assertThat(otherClock.getLargeClock().getView().getParent()).isEqualTo(mLargeClockFrame);
assertThat(mClock.getSmallClock().getView().getParent()).isNull();
assertThat(mClock.getLargeClock().getView().getParent()).isNull();
}
@Test
public void onPluginDisconnected_secondOfTwoDisconnected() {
// GIVEN two plugins are connected
Clock otherClock = mock(Clock.class);
when(otherClock.getSmallClock()).thenReturn(new TextView(getContext()));
when(otherClock.getLargeClock()).thenReturn(new TextView(getContext()));
ClockController otherClock = mock(ClockController.class);
ClockFaceController smallClock = mock(ClockFaceController.class);
ClockFaceController largeClock = mock(ClockFaceController.class);
when(otherClock.getSmallClock()).thenReturn(smallClock);
when(otherClock.getLargeClock()).thenReturn(largeClock);
when(smallClock.getView()).thenReturn(new TextView(getContext()));
when(largeClock.getView()).thenReturn(new TextView(getContext()));
mKeyguardClockSwitch.setClock(otherClock, StatusBarState.KEYGUARD);
mKeyguardClockSwitch.setClock(mClock, StatusBarState.KEYGUARD);
// WHEN the second plugin is disconnected
mKeyguardClockSwitch.setClock(null, StatusBarState.KEYGUARD);
// THEN nothing should be shown
assertThat(otherClock.getSmallClock().getParent()).isNull();
assertThat(otherClock.getLargeClock().getParent()).isNull();
assertThat(mClock.getSmallClock().getParent()).isNull();
assertThat(mClock.getLargeClock().getParent()).isNull();
assertThat(otherClock.getSmallClock().getView().getParent()).isNull();
assertThat(otherClock.getLargeClock().getView().getParent()).isNull();
assertThat(mClock.getSmallClock().getView().getParent()).isNull();
assertThat(mClock.getLargeClock().getView().getParent()).isNull();
}
@Test

View File

@@ -22,7 +22,7 @@ import android.os.Handler
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.plugins.Clock
import com.android.systemui.plugins.ClockController
import com.android.systemui.plugins.ClockId
import com.android.systemui.plugins.ClockMetadata
import com.android.systemui.plugins.ClockProviderPlugin
@@ -48,8 +48,8 @@ class ClockRegistryTest : SysuiTestCase() {
@JvmField @Rule val mockito = MockitoJUnit.rule()
@Mock private lateinit var mockContext: Context
@Mock private lateinit var mockPluginManager: PluginManager
@Mock private lateinit var mockClock: Clock
@Mock private lateinit var mockDefaultClock: Clock
@Mock private lateinit var mockClock: ClockController
@Mock private lateinit var mockDefaultClock: ClockController
@Mock private lateinit var mockThumbnail: Drawable
@Mock private lateinit var mockHandler: Handler
@Mock private lateinit var mockContentResolver: ContentResolver
@@ -60,7 +60,7 @@ class ClockRegistryTest : SysuiTestCase() {
private var settingValue: String = ""
companion object {
private fun failFactory(): Clock {
private fun failFactory(): ClockController {
fail("Unexpected call to createClock")
return null!!
}
@@ -73,17 +73,17 @@ class ClockRegistryTest : SysuiTestCase() {
private class FakeClockPlugin : ClockProviderPlugin {
private val metadata = mutableListOf<ClockMetadata>()
private val createCallbacks = mutableMapOf<ClockId, () -> Clock>()
private val createCallbacks = mutableMapOf<ClockId, () -> ClockController>()
private val thumbnailCallbacks = mutableMapOf<ClockId, () -> Drawable?>()
override fun getClocks() = metadata
override fun createClock(id: ClockId): Clock = createCallbacks[id]!!()
override fun createClock(id: ClockId): ClockController = createCallbacks[id]!!()
override fun getClockThumbnail(id: ClockId): Drawable? = thumbnailCallbacks[id]!!()
fun addClock(
id: ClockId,
name: String,
create: () -> Clock = ::failFactory,
create: () -> ClockController = ::failFactory,
getThumbnail: () -> Drawable? = ::failThumbnail
): FakeClockPlugin {
metadata.add(ClockMetadata(id, name))

View File

@@ -17,6 +17,7 @@
package com.android.systemui.shared.clocks
import android.content.res.Resources
import android.graphics.Color
import android.graphics.drawable.Drawable
import android.testing.AndroidTestingRunner
import android.util.TypedValue
@@ -25,7 +26,7 @@ import android.widget.FrameLayout
import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.shared.clocks.DefaultClock.Companion.DOZE_COLOR
import com.android.systemui.shared.clocks.DefaultClockController.Companion.DOZE_COLOR
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
@@ -88,17 +89,20 @@ class DefaultClockProviderTest : SysuiTestCase() {
// Default clock provider must always provide the default clock
val clock = provider.createClock(DEFAULT_CLOCK_ID)
assertNotNull(clock)
assertEquals(clock.smallClock, mockSmallClockView)
assertEquals(clock.largeClock, mockLargeClockView)
assertEquals(mockSmallClockView, clock.smallClock.view)
assertEquals(mockLargeClockView, clock.largeClock.view)
}
@Test
fun defaultClock_initialize() {
val clock = provider.createClock(DEFAULT_CLOCK_ID)
verify(mockSmallClockView).setColors(Color.MAGENTA, Color.MAGENTA)
verify(mockLargeClockView).setColors(Color.MAGENTA, Color.MAGENTA)
clock.initialize(resources, 0f, 0f)
verify(mockSmallClockView, times(2)).setColors(eq(DOZE_COLOR), anyInt())
verify(mockLargeClockView, times(2)).setColors(eq(DOZE_COLOR), anyInt())
verify(mockSmallClockView).setColors(eq(DOZE_COLOR), anyInt())
verify(mockLargeClockView).setColors(eq(DOZE_COLOR), anyInt())
verify(mockSmallClockView).onTimeZoneChanged(notNull())
verify(mockLargeClockView).onTimeZoneChanged(notNull())
verify(mockSmallClockView).refreshTime()
@@ -147,10 +151,14 @@ class DefaultClockProviderTest : SysuiTestCase() {
@Test
fun defaultClock_events_onColorPaletteChanged() {
val clock = provider.createClock(DEFAULT_CLOCK_ID)
clock.events.onColorPaletteChanged(resources, true, true)
verify(mockSmallClockView, times(2)).setColors(eq(DOZE_COLOR), anyInt())
verify(mockLargeClockView, times(2)).setColors(eq(DOZE_COLOR), anyInt())
verify(mockSmallClockView).setColors(Color.MAGENTA, Color.MAGENTA)
verify(mockLargeClockView).setColors(Color.MAGENTA, Color.MAGENTA)
clock.events.onColorPaletteChanged(resources)
verify(mockSmallClockView).setColors(eq(DOZE_COLOR), anyInt())
verify(mockLargeClockView).setColors(eq(DOZE_COLOR), anyInt())
}
@Test