Merge "Seperate clockface specific callbacks" into tm-qpr-dev

This commit is contained in:
Hawkwood Glazier
2022-10-11 15:05:48 +00:00
committed by Android (Google) Code Review
12 changed files with 427 additions and 322 deletions

View File

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

View File

@@ -22,7 +22,7 @@ import android.os.UserHandle
import android.provider.Settings import android.provider.Settings
import android.util.Log import android.util.Log
import com.android.systemui.dagger.qualifiers.Main 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.ClockId
import com.android.systemui.plugins.ClockMetadata import com.android.systemui.plugins.ClockMetadata
import com.android.systemui.plugins.ClockProvider import com.android.systemui.plugins.ClockProvider
@@ -33,7 +33,7 @@ import com.google.gson.Gson
import javax.inject.Inject import javax.inject.Inject
private val TAG = ClockRegistry::class.simpleName private val TAG = ClockRegistry::class.simpleName
private val DEBUG = true private const val DEBUG = true
/** ClockRegistry aggregates providers and plugins */ /** ClockRegistry aggregates providers and plugins */
open class ClockRegistry( open class ClockRegistry(
@@ -130,6 +130,10 @@ open class ClockRegistry(
} }
availableClocks[id] = ClockInfo(clock, provider) availableClocks[id] = ClockInfo(clock, provider)
if (DEBUG) {
Log.i(TAG, "Added ${clock.clockId}")
}
if (currentId == id) { if (currentId == id) {
if (DEBUG) { if (DEBUG) {
Log.i(TAG, "Current clock ($currentId) was connected") Log.i(TAG, "Current clock ($currentId) was connected")
@@ -143,6 +147,9 @@ open class ClockRegistry(
val currentId = currentClockId val currentId = currentClockId
for (clock in provider.getClocks()) { for (clock in provider.getClocks()) {
availableClocks.remove(clock.clockId) availableClocks.remove(clock.clockId)
if (DEBUG) {
Log.i(TAG, "Removed ${clock.clockId}")
}
if (currentId == clock.clockId) { if (currentId == clock.clockId) {
Log.w(TAG, "Current clock ($currentId) was disconnected") Log.w(TAG, "Current clock ($currentId) was disconnected")
@@ -161,7 +168,7 @@ open class ClockRegistry(
fun getClockThumbnail(clockId: ClockId): Drawable? = fun getClockThumbnail(clockId: ClockId): Drawable? =
availableClocks[clockId]?.provider?.getClockThumbnail(clockId) availableClocks[clockId]?.provider?.getClockThumbnail(clockId)
fun createExampleClock(clockId: ClockId): Clock? = createClock(clockId) fun createExampleClock(clockId: ClockId): ClockController? = createClock(clockId)
fun registerClockChangeListener(listener: ClockChangeListener) = fun registerClockChangeListener(listener: ClockChangeListener) =
clockChangeListeners.add(listener) clockChangeListeners.add(listener)
@@ -169,11 +176,14 @@ open class ClockRegistry(
fun unregisterClockChangeListener(listener: ClockChangeListener) = fun unregisterClockChangeListener(listener: ClockChangeListener) =
clockChangeListeners.remove(listener) clockChangeListeners.remove(listener)
fun createCurrentClock(): Clock { fun createCurrentClock(): ClockController {
val clockId = currentClockId val clockId = currentClockId
if (isEnabled && clockId.isNotEmpty()) { if (isEnabled && clockId.isNotEmpty()) {
val clock = createClock(clockId) val clock = createClock(clockId)
if (clock != null) { if (clock != null) {
if (DEBUG) {
Log.i(TAG, "Rendering clock $clockId")
}
return clock return clock
} else { } else {
Log.e(TAG, "Clock $clockId not found; using default") Log.e(TAG, "Clock $clockId not found; using default")
@@ -183,7 +193,7 @@ open class ClockRegistry(
return createClock(DEFAULT_CLOCK_ID)!! return createClock(DEFAULT_CLOCK_ID)!!
} }
private fun createClock(clockId: ClockId): Clock? = private fun createClock(clockId: ClockId): ClockController? =
availableClocks[clockId]?.provider?.createClock(clockId) availableClocks[clockId]?.provider?.createClock(clockId)
private data class ClockInfo( 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.Context
import android.content.res.Resources import android.content.res.Resources
import android.graphics.Color
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.icu.text.NumberFormat
import android.util.TypedValue
import android.view.LayoutInflater 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.dagger.qualifiers.Main
import com.android.systemui.plugins.Clock import com.android.systemui.plugins.ClockController
import com.android.systemui.plugins.ClockAnimations
import com.android.systemui.plugins.ClockEvents
import com.android.systemui.plugins.ClockId import com.android.systemui.plugins.ClockId
import com.android.systemui.plugins.ClockMetadata import com.android.systemui.plugins.ClockMetadata
import com.android.systemui.plugins.ClockProvider import com.android.systemui.plugins.ClockProvider
import com.android.systemui.shared.R import com.android.systemui.shared.R
import java.io.PrintWriter
import java.util.Locale
import java.util.TimeZone
import javax.inject.Inject import javax.inject.Inject
private val TAG = DefaultClockProvider::class.simpleName private val TAG = DefaultClockProvider::class.simpleName
@@ -48,11 +38,12 @@ class DefaultClockProvider @Inject constructor(
override fun getClocks(): List<ClockMetadata> = override fun getClocks(): List<ClockMetadata> =
listOf(ClockMetadata(DEFAULT_CLOCK_ID, DEFAULT_CLOCK_NAME)) 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) { if (id != DEFAULT_CLOCK_ID) {
throw IllegalArgumentException("$id is unsupported by $TAG") throw IllegalArgumentException("$id is unsupported by $TAG")
} }
return DefaultClock(ctx, layoutInflater, resources)
return DefaultClockController(ctx, layoutInflater, resources)
} }
override fun getClockThumbnail(id: ClockId): Drawable? { override fun getClockThumbnail(id: ClockId): Drawable? {
@@ -64,190 +55,3 @@ class DefaultClockProvider @Inject constructor(
return resources.getDrawable(R.drawable.clock_default_thumbnail, null) 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.Background
import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlags 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.plugins.statusbar.StatusBarStateController
import com.android.systemui.shared.regionsampling.RegionSamplingInstance import com.android.systemui.shared.regionsampling.RegionSamplingInstance
import com.android.systemui.statusbar.policy.BatteryController import com.android.systemui.statusbar.policy.BatteryController
@@ -53,9 +53,9 @@ open class ClockEventController @Inject constructor(
private val context: Context, private val context: Context,
@Main private val mainExecutor: Executor, @Main private val mainExecutor: Executor,
@Background private val bgExecutor: Executor, @Background private val bgExecutor: Executor,
private val featureFlags: FeatureFlags private val featureFlags: FeatureFlags,
) { ) {
var clock: Clock? = null var clock: ClockController? = null
set(value) { set(value) {
field = value field = value
if (value != null) { if (value != null) {
@@ -74,42 +74,45 @@ open class ClockEventController @Inject constructor(
private val regionSamplingEnabled = private val regionSamplingEnabled =
featureFlags.isEnabled(com.android.systemui.flags.Flags.REGION_SAMPLING) featureFlags.isEnabled(com.android.systemui.flags.Flags.REGION_SAMPLING)
private val updateFun = object : RegionSamplingInstance.UpdateColorCallback { private fun updateColors() {
override fun updateColors() { if (regionSamplingEnabled && smallRegionSampler != null && largeRegionSampler != null) {
if (regionSamplingEnabled) { smallClockIsDark = smallRegionSampler!!.currentRegionDarkness().isDark
smallClockIsDark = smallRegionSamplingInstance.currentRegionDarkness().isDark largeClockIsDark = largeRegionSampler!!.currentRegionDarkness().isDark
largeClockIsDark = largeRegionSamplingInstance.currentRegionDarkness().isDark
} else { } else {
val isLightTheme = TypedValue() val isLightTheme = TypedValue()
context.theme.resolveAttribute(android.R.attr.isLightTheme, isLightTheme, true) context.theme.resolveAttribute(android.R.attr.isLightTheme, isLightTheme, true)
smallClockIsDark = isLightTheme.data == 0 smallClockIsDark = isLightTheme.data == 0
largeClockIsDark = isLightTheme.data == 0 largeClockIsDark = isLightTheme.data == 0
} }
clock?.events?.onColorPaletteChanged(resources, smallClockIsDark, largeClockIsDark)
} clock?.smallClock?.events?.onRegionDarknessChanged(smallClockIsDark)
clock?.largeClock?.events?.onRegionDarknessChanged(largeClockIsDark)
} }
fun updateRegionSamplers(currentClock: Clock?) { private fun updateRegionSamplers(currentClock: ClockController?) {
smallRegionSamplingInstance = createRegionSampler( smallRegionSampler?.stopRegionSampler()
currentClock?.smallClock, largeRegionSampler?.stopRegionSampler()
smallRegionSampler = createRegionSampler(
currentClock?.smallClock?.view,
mainExecutor, mainExecutor,
bgExecutor, bgExecutor,
regionSamplingEnabled, regionSamplingEnabled,
updateFun ::updateColors
) )
largeRegionSamplingInstance = createRegionSampler( largeRegionSampler = createRegionSampler(
currentClock?.largeClock, currentClock?.largeClock?.view,
mainExecutor, mainExecutor,
bgExecutor, bgExecutor,
regionSamplingEnabled, regionSamplingEnabled,
updateFun ::updateColors
) )
smallRegionSamplingInstance.startRegionSampler() smallRegionSampler!!.startRegionSampler()
largeRegionSamplingInstance.startRegionSampler() largeRegionSampler!!.startRegionSampler()
updateFun.updateColors() updateColors()
} }
protected open fun createRegionSampler( protected open fun createRegionSampler(
@@ -117,25 +120,29 @@ open class ClockEventController @Inject constructor(
mainExecutor: Executor?, mainExecutor: Executor?,
bgExecutor: Executor?, bgExecutor: Executor?,
regionSamplingEnabled: Boolean, regionSamplingEnabled: Boolean,
updateFun: RegionSamplingInstance.UpdateColorCallback updateColors: () -> Unit
): RegionSamplingInstance { ): RegionSamplingInstance {
return RegionSamplingInstance( return RegionSamplingInstance(
sampledView, sampledView,
mainExecutor, mainExecutor,
bgExecutor, bgExecutor,
regionSamplingEnabled, regionSamplingEnabled,
updateFun) object : RegionSamplingInstance.UpdateColorCallback {
override fun updateColors() {
updateColors()
}
})
} }
lateinit var smallRegionSamplingInstance: RegionSamplingInstance var smallRegionSampler: RegionSamplingInstance? = null
lateinit var largeRegionSamplingInstance: RegionSamplingInstance var largeRegionSampler: RegionSamplingInstance? = null
private var smallClockIsDark = true private var smallClockIsDark = true
private var largeClockIsDark = true private var largeClockIsDark = true
private val configListener = object : ConfigurationController.ConfigurationListener { private val configListener = object : ConfigurationController.ConfigurationListener {
override fun onThemeChanged() { override fun onThemeChanged() {
updateFun.updateColors() clock?.events?.onColorPaletteChanged(resources)
} }
override fun onDensityOrFontScaleChanged() { override fun onDensityOrFontScaleChanged() {
@@ -204,8 +211,8 @@ open class ClockEventController @Inject constructor(
batteryController.addCallback(batteryCallback) batteryController.addCallback(batteryCallback)
keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback) keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback)
statusBarStateController.addCallback(statusBarStateListener) statusBarStateController.addCallback(statusBarStateListener)
smallRegionSamplingInstance.startRegionSampler() smallRegionSampler?.startRegionSampler()
largeRegionSamplingInstance.startRegionSampler() largeRegionSampler?.startRegionSampler()
} }
fun unregisterListeners() { fun unregisterListeners() {
@@ -214,8 +221,8 @@ open class ClockEventController @Inject constructor(
batteryController.removeCallback(batteryCallback) batteryController.removeCallback(batteryCallback)
keyguardUpdateMonitor.removeCallback(keyguardUpdateMonitorCallback) keyguardUpdateMonitor.removeCallback(keyguardUpdateMonitorCallback)
statusBarStateController.removeCallback(statusBarStateListener) statusBarStateController.removeCallback(statusBarStateListener)
smallRegionSamplingInstance.stopRegionSampler() smallRegionSampler?.stopRegionSampler()
largeRegionSamplingInstance.stopRegionSampler() largeRegionSampler?.stopRegionSampler()
} }
/** /**
@@ -224,8 +231,8 @@ open class ClockEventController @Inject constructor(
fun dump(pw: PrintWriter) { fun dump(pw: PrintWriter) {
pw.println(this) pw.println(this)
clock?.dump(pw) clock?.dump(pw)
smallRegionSamplingInstance.dump(pw) smallRegionSampler?.dump(pw)
largeRegionSamplingInstance.dump(pw) largeRegionSampler?.dump(pw)
} }
companion object { companion object {

View File

@@ -17,7 +17,7 @@ import androidx.annotation.VisibleForTesting;
import com.android.keyguard.dagger.KeyguardStatusViewScope; import com.android.keyguard.dagger.KeyguardStatusViewScope;
import com.android.systemui.R; import com.android.systemui.R;
import com.android.systemui.animation.Interpolators; import com.android.systemui.animation.Interpolators;
import com.android.systemui.plugins.Clock; import com.android.systemui.plugins.ClockController;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
@@ -94,7 +94,7 @@ public class KeyguardClockSwitch extends RelativeLayout {
onDensityOrFontScaleChanged(); onDensityOrFontScaleChanged();
} }
void setClock(Clock clock, int statusBarState) { void setClock(ClockController clock, int statusBarState) {
// Disconnect from existing plugin. // Disconnect from existing plugin.
mSmallClockFrame.removeAllViews(); mSmallClockFrame.removeAllViews();
mLargeClockFrame.removeAllViews(); mLargeClockFrame.removeAllViews();
@@ -105,8 +105,8 @@ public class KeyguardClockSwitch extends RelativeLayout {
} }
// Attach small and big clock views to hierarchy. // Attach small and big clock views to hierarchy.
mSmallClockFrame.addView(clock.getSmallClock()); mSmallClockFrame.addView(clock.getSmallClock().getView());
mLargeClockFrame.addView(clock.getLargeClock()); mLargeClockFrame.addView(clock.getLargeClock().getView());
} }
private void updateClockViews(boolean useLargeClock, boolean animate) { 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.FeatureFlags;
import com.android.systemui.flags.Flags; import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController; 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.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shared.clocks.ClockRegistry; import com.android.systemui.shared.clocks.ClockRegistry;
import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController; import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController;
@@ -262,7 +262,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
mCurrentClockSize = clockSize; mCurrentClockSize = clockSize;
Clock clock = getClock(); ClockController clock = getClock();
boolean appeared = mView.switchToClock(clockSize, animate); boolean appeared = mView.switchToClock(clockSize, animate);
if (clock != null && animate && appeared && clockSize == LARGE) { if (clock != null && animate && appeared && clockSize == LARGE) {
clock.getAnimations().enter(); clock.getAnimations().enter();
@@ -273,7 +273,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
* Animates the clock view between folded and unfolded states * Animates the clock view between folded and unfolded states
*/ */
public void animateFoldToAod(float foldFraction) { public void animateFoldToAod(float foldFraction) {
Clock clock = getClock(); ClockController clock = getClock();
if (clock != null) { if (clock != null) {
clock.getAnimations().fold(foldFraction); clock.getAnimations().fold(foldFraction);
} }
@@ -286,7 +286,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
if (mSmartspaceController != null) { if (mSmartspaceController != null) {
mSmartspaceController.requestSmartspaceUpdate(); mSmartspaceController.requestSmartspaceUpdate();
} }
Clock clock = getClock(); ClockController clock = getClock();
if (clock != null) { if (clock != null) {
clock.getEvents().onTimeTick(); 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 * We can't directly getBottom() because clock changes positions in AOD for burn-in
*/ */
int getClockBottom(int statusBarHeaderHeight) { int getClockBottom(int statusBarHeaderHeight) {
Clock clock = getClock(); ClockController clock = getClock();
if (clock == null) { if (clock == null) {
return 0; return 0;
} }
if (mLargeClockFrame.getVisibility() == View.VISIBLE) { if (mLargeClockFrame.getVisibility() == View.VISIBLE) {
int frameHeight = mLargeClockFrame.getHeight(); int frameHeight = mLargeClockFrame.getHeight();
int clockHeight = clock.getLargeClock().getHeight(); int clockHeight = clock.getLargeClock().getView().getHeight();
return frameHeight / 2 + clockHeight / 2; return frameHeight / 2 + clockHeight / 2;
} else { } else {
int clockHeight = clock.getSmallClock().getHeight(); int clockHeight = clock.getSmallClock().getView().getHeight();
return clockHeight + statusBarHeaderHeight + mKeyguardClockTopMargin; 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. * Get the height of the currently visible clock on the keyguard.
*/ */
int getClockHeight() { int getClockHeight() {
Clock clock = getClock(); ClockController clock = getClock();
if (clock == null) { if (clock == null) {
return 0; return 0;
} }
if (mLargeClockFrame.getVisibility() == View.VISIBLE) { if (mLargeClockFrame.getVisibility() == View.VISIBLE) {
return clock.getLargeClock().getHeight(); return clock.getLargeClock().getView().getHeight();
} else { } else {
return clock.getSmallClock().getHeight(); return clock.getSmallClock().getView().getHeight();
} }
} }
@@ -361,12 +361,12 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
mNotificationIconAreaController.setupAodIcons(nic); mNotificationIconAreaController.setupAodIcons(nic);
} }
private void setClock(Clock clock) { private void setClock(ClockController clock) {
mClockEventController.setClock(clock); mClockEventController.setClock(clock);
mView.setClock(clock, mStatusBarStateController.getState()); mView.setClock(clock, mStatusBarStateController.getState());
} }
private Clock getClock() { private ClockController getClock() {
return mClockEventController.getClock(); return mClockEventController.getClock();
} }
@@ -398,7 +398,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
public void dump(@NonNull PrintWriter pw, @NonNull String[] args) { public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
pw.println("currentClockSizeLarge=" + (mCurrentClockSize == LARGE)); pw.println("currentClockSizeLarge=" + (mCurrentClockSize == LARGE));
pw.println("mCanShowDoubleLineClock=" + mCanShowDoubleLineClock); pw.println("mCanShowDoubleLineClock=" + mCanShowDoubleLineClock);
Clock clock = getClock(); ClockController clock = getClock();
if (clock != null) { if (clock != null) {
clock.dump(pw); clock.dump(pw);
} }

View File

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

View File

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

View File

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

View File

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

View File

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