Merge changes from topic "clock_cherrypick_fixes" into tm-qpr-dev

* changes:
  [DO NOT MERGE] Initialize TimeZone during DefaultClockProvider init
  [DO NOT MERGE] Correct large clock position
  [DO NOT MERGE] Additional Tests for DefaultClock
  [DO NOT MERGE] Update DefaultClockProvider colors
  [DO NOT MERGE] Moving Text Protection to Clock Event Controller
  [DO NOT MERGE] Adding Region Sampler to Animatable Clocks
  [DO NOT MERGE] Revert "[DO NOT MERGE]Revert "Migrate KeyguardClockSwitch to new..."
This commit is contained in:
Hawkwood Glazier
2022-09-01 19:20:04 +00:00
committed by Android (Google) Code Review
26 changed files with 1042 additions and 783 deletions

View File

@@ -39,6 +39,7 @@
-packages/SystemUI/src-release/com/android/systemui/flags/FlagsModule.kt
-packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt
-packages/SystemUI/src/com/android/keyguard/BouncerPanelExpansionCalculator.kt
-packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
-packages/SystemUI/src/com/android/keyguard/KeyguardBiometricLockoutLogger.kt
-packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt
-packages/SystemUI/src/com/android/keyguard/KeyguardListenQueue.kt
@@ -156,6 +157,8 @@
-packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt
-packages/SystemUI/src/com/android/systemui/controls/ui/TouchBehavior.kt
-packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
-packages/SystemUI/src/com/android/systemui/decor/CutoutDecorProviderFactory.kt
-packages/SystemUI/src/com/android/systemui/decor/CutoutDecorProviderImpl.kt
-packages/SystemUI/src/com/android/systemui/decor/DecorProvider.kt
-packages/SystemUI/src/com/android/systemui/decor/DecorProviderFactory.kt
-packages/SystemUI/src/com/android/systemui/decor/FaceScanningProviderFactory.kt
@@ -246,6 +249,7 @@
-packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt
-packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
-packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
-packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipRootView.kt
-packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLogger.kt
-packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderUiEventLogger.kt
-packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanel.kt
@@ -575,6 +579,7 @@
-packages/SystemUI/src/com/android/systemui/volume/VolumePanelFactory.kt
-packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
-packages/SystemUI/tests/src/com/android/keyguard/BouncerPanelExpansionCalculatorTest.kt
-packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
-packages/SystemUI/tests/src/com/android/keyguard/KeyguardBiometricLockoutLoggerTest.kt
-packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt
-packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
@@ -633,6 +638,7 @@
-packages/SystemUI/tests/src/com/android/systemui/controls/management/TestControlsRequestDialog.kt
-packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlViewHolderTest.kt
-packages/SystemUI/tests/src/com/android/systemui/controls/ui/DetailDialogTest.kt
-packages/SystemUI/tests/src/com/android/systemui/decor/CutoutDecorProviderFactoryTest.kt
-packages/SystemUI/tests/src/com/android/systemui/decor/OverlayWindowTest.kt
-packages/SystemUI/tests/src/com/android/systemui/decor/PrivacyDotDecorProviderFactoryTest.kt
-packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerDecorProviderFactoryTest.kt
@@ -720,6 +726,7 @@
-packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordDialogTest.kt
-packages/SystemUI/tests/src/com/android/systemui/screenshot/ImageCaptureImplTest.kt
-packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt
-packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotPolicyImplTest.kt
-packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt
-packages/SystemUI/tests/src/com/android/systemui/settings/UserFileManagerImplTest.kt
-packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt

View File

@@ -13,9 +13,9 @@
*/
package com.android.systemui.plugins
import android.content.res.Resources
import android.graphics.drawable.Drawable
import android.view.View
import com.android.internal.colorextraction.ColorExtractor
import com.android.systemui.plugins.annotations.ProvidesInterface
import java.io.PrintWriter
import java.util.Locale
@@ -57,7 +57,15 @@ interface Clock {
val events: ClockEvents
/** Triggers for various animations */
val animation: ClockAnimation
val animations: ClockAnimations
/** Initializes various rendering parameters. If never called, provides reasonable defaults. */
fun initialize(resources: Resources, dozeFraction: Float, foldFraction: Float) {
events.onColorPaletteChanged(resources, true, true)
animations.doze(dozeFraction)
animations.fold(foldFraction)
events.onTimeTick()
}
/** Optional method for dumping debug information */
fun dump(pw: PrintWriter) { }
@@ -80,15 +88,16 @@ interface ClockEvents {
/** Call whenever font settings change */
fun onFontSettingChanged() { }
/** Call whenever the color pallete should update */
fun onColorPaletteChanged(palette: ColorExtractor.GradientColors) { }
/** Call whenever the color palette should update */
fun onColorPaletteChanged(
resources: Resources,
smallClockIsDark: Boolean,
largeClockIsDark: Boolean
) { }
}
/** Methods which trigger various clock animations */
interface ClockAnimation {
/** Initializes the doze & fold animation positions. Defaults to neither folded nor dozing. */
fun initialize(dozeFraction: Float, foldFraction: Float) { }
interface ClockAnimations {
/** Runs an enter animation (if any) */
fun enter() { }

View File

@@ -1,28 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
**
** Copyright 2020, 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.
*/
-->
<!--
** AOD/LockScreen Clock font.
** Should include all numeric glyphs in all supported locales.
** Recommended: font with variable width to support AOD => LS animations
-->
<!-- TODO: Remove when clock migration complete -->
<font-family xmlns:android="http://schemas.android.com/apk/res/android">
<font android:typeface="monospace"/>
</font-family>

View File

@@ -23,6 +23,7 @@
android:id="@+id/keyguard_clock_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipChildren="false"
android:layout_gravity="center_horizontal|top">
<FrameLayout
android:id="@+id/lockscreen_clock_view"
@@ -30,44 +31,13 @@
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:paddingStart="@dimen/clock_padding_start">
<com.android.systemui.shared.clocks.AnimatableClockView
android:id="@+id/animatable_clock_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:gravity="start"
android:textSize="@dimen/clock_text_size"
android:fontFamily="@font/clock"
android:elegantTextHeight="false"
android:singleLine="true"
android:fontFeatureSettings="pnum"
chargeAnimationDelay="350"
dozeWeight="200"
lockScreenWeight="400"
/>
</FrameLayout>
android:paddingStart="@dimen/clock_padding_start" />
<FrameLayout
android:id="@+id/lockscreen_clock_view_large"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/keyguard_slice_view"
android:visibility="gone">
<com.android.systemui.shared.clocks.AnimatableClockView
android:id="@+id/animatable_clock_view_large"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center_horizontal"
android:textSize="@dimen/large_clock_text_size"
android:fontFamily="@font/clock"
android:typeface="monospace"
android:elegantTextHeight="false"
chargeAnimationDelay="200"
dozeWeight="200"
lockScreenWeight="400"
/>
</FrameLayout>
android:layout_height="match_parent"
android:layout_marginTop="@dimen/keyguard_large_clock_top_margin"
android:visibility="gone" />
<!-- Not quite optimal but needed to translate these items as a group. The
NotificationIconContainer has its own logic for translation. -->

View File

@@ -19,7 +19,7 @@
android:id="@+id/time_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/clock"
android:fontFamily="@*android:string/config_clockFontFamily"
android:textColor="@android:color/white"
android:format12Hour="@string/dream_time_complication_12_hr_time_format"
android:format24Hour="@string/dream_time_complication_24_hr_time_format"

View File

@@ -671,12 +671,6 @@
<!-- With the large clock, move up slightly from the center -->
<dimen name="keyguard_large_clock_top_margin">-60dp</dimen>
<!-- TODO: Remove during migration -->
<!-- Default line spacing multiplier between hours and minutes of the keyguard clock -->
<item name="keyguard_clock_line_spacing_scale" type="dimen" format="float">.7</item>
<!-- Burmese line spacing multiplier between hours and minutes of the keyguard clock -->
<item name="keyguard_clock_line_spacing_scale_burmese" type="dimen" format="float">1</item>
<dimen name="notification_scrim_corner_radius">32dp</dimen>
<!-- The minimum amount the user needs to swipe to go to the camera / phone. -->
@@ -895,11 +889,6 @@
burn-in on AOD. -->
<dimen name="burn_in_prevention_offset_y_clock">42dp</dimen>
<!-- Clock maximum font size (dp is intentional, to prevent any further scaling) -->
<!-- TODO: Remove when clock migration complete -->
<dimen name="large_clock_text_size">150dp</dimen>
<dimen name="clock_text_size">86dp</dimen>
<!-- The maximum offset in either direction that icons move to prevent burn-in on AOD. -->
<dimen name="default_burn_in_prevention_offset">15dp</dimen>

View File

@@ -18,7 +18,6 @@
-->
<com.android.systemui.shared.clocks.AnimatableClockView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/animatable_clock_view_large"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"

View File

@@ -18,14 +18,14 @@
-->
<com.android.systemui.shared.clocks.AnimatableClockView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/animatable_clock_view"
android:layout_width="wrap_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:gravity="start"
android:textSize="@dimen/small_clock_text_size"
android:fontFamily="@*android:string/config_clockFontFamily"
android:elegantTextHeight="false"
android:ellipsize="none"
android:singleLine="true"
android:fontFeatureSettings="pnum"
chargeAnimationDelay="350"

View File

@@ -20,12 +20,16 @@ import android.annotation.ColorInt
import android.annotation.FloatRange
import android.annotation.IntRange
import android.annotation.SuppressLint
import android.app.compat.ChangeIdStateCache.invalidate
import android.content.Context
import android.graphics.Canvas
import android.text.TextUtils
import android.text.format.DateFormat
import android.util.AttributeSet
import android.widget.TextView
import com.android.internal.R.attr.contentDescription
import com.android.internal.R.attr.format
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.animation.GlyphCallback
import com.android.systemui.animation.Interpolators
import com.android.systemui.animation.TextAnimator
@@ -74,7 +78,8 @@ class AnimatableClockView @JvmOverloads constructor(
private var textAnimator: TextAnimator? = null
private var onTextAnimatorInitialized: Runnable? = null
var timeOverrideInMillis: Long? = null
@VisibleForTesting var isAnimationEnabled: Boolean = true
@VisibleForTesting var timeOverrideInMillis: Long? = null
val dozingWeight: Int
get() = if (useBoldedVersion()) dozingWeightInternal + 100 else dozingWeightInternal
@@ -82,6 +87,12 @@ class AnimatableClockView @JvmOverloads constructor(
val lockScreenWeight: Int
get() = if (useBoldedVersion()) lockScreenWeightInternal + 100 else lockScreenWeightInternal
/**
* The number of pixels below the baseline. For fonts that support languages such as
* Burmese, this space can be significant and should be accounted for when computing layout.
*/
val bottom get() = paint?.fontMetrics?.bottom ?: 0f
init {
val animatableClockViewAttributes = context.obtainStyledAttributes(
attrs, R.styleable.AnimatableClockView, defStyleAttr, defStyleRes
@@ -141,6 +152,15 @@ class AnimatableClockView @JvmOverloads constructor(
if (!TextUtils.equals(text, formattedText)) {
text = formattedText
lastTextUpdate = getTimestamp()
// Because the TextLayout may mutate under the hood as a result of the new text, we
// notify the TextAnimator that it may have changed and request a measure/layout. A
// crash will occur on the next invocation of setTextStyle if the layout is mutated
// without being notified TextInterpolator being notified.
if (layout != null) {
textAnimator?.updateLayout(layout)
}
requestLayout()
}
}
@@ -200,7 +220,7 @@ class AnimatableClockView @JvmOverloads constructor(
}
fun animateAppearOnLockscreen() {
if (textAnimator == null) {
if (isAnimationEnabled && textAnimator == null) {
return
}
setTextStyle(
@@ -216,7 +236,7 @@ class AnimatableClockView @JvmOverloads constructor(
weight = lockScreenWeight,
textSize = -1f,
color = lockScreenColor,
animate = true,
animate = isAnimationEnabled,
duration = APPEAR_ANIM_DURATION,
delay = 0,
onAnimationEnd = null
@@ -224,7 +244,7 @@ class AnimatableClockView @JvmOverloads constructor(
}
fun animateFoldAppear(animate: Boolean = true) {
if (textAnimator == null) {
if (isAnimationEnabled && textAnimator == null) {
return
}
setTextStyle(
@@ -240,7 +260,7 @@ class AnimatableClockView @JvmOverloads constructor(
weight = dozingWeightInternal,
textSize = -1f,
color = dozingColor,
animate = animate,
animate = animate && isAnimationEnabled,
interpolator = Interpolators.EMPHASIZED_DECELERATE,
duration = ANIMATION_DURATION_FOLD_TO_AOD.toLong(),
delay = 0,
@@ -258,7 +278,7 @@ class AnimatableClockView @JvmOverloads constructor(
weight = if (isDozing()) dozingWeight else lockScreenWeight,
textSize = -1f,
color = null,
animate = true,
animate = isAnimationEnabled,
duration = CHARGE_ANIM_DURATION_PHASE_1,
delay = 0,
onAnimationEnd = null
@@ -268,7 +288,7 @@ class AnimatableClockView @JvmOverloads constructor(
weight = if (isDozing()) lockScreenWeight else dozingWeight,
textSize = -1f,
color = null,
animate = true,
animate = isAnimationEnabled,
duration = CHARGE_ANIM_DURATION_PHASE_0,
delay = chargeAnimationDelay.toLong(),
onAnimationEnd = startAnimPhase2
@@ -280,7 +300,7 @@ class AnimatableClockView @JvmOverloads constructor(
weight = if (isDozing) dozingWeight else lockScreenWeight,
textSize = -1f,
color = if (isDozing) dozingColor else lockScreenColor,
animate = animate,
animate = animate && isAnimationEnabled,
duration = DOZE_ANIM_DURATION,
delay = 0,
onAnimationEnd = null
@@ -314,7 +334,7 @@ class AnimatableClockView @JvmOverloads constructor(
weight = weight,
textSize = textSize,
color = color,
animate = animate,
animate = animate && isAnimationEnabled,
duration = duration,
interpolator = interpolator,
delay = delay,
@@ -352,7 +372,7 @@ class AnimatableClockView @JvmOverloads constructor(
weight = weight,
textSize = textSize,
color = color,
animate = animate,
animate = animate && isAnimationEnabled,
interpolator = null,
duration = duration,
delay = delay,

View File

@@ -35,8 +35,6 @@ import javax.inject.Inject
private val TAG = ClockRegistry::class.simpleName
private val DEBUG = true
typealias ClockChangeListener = () -> Unit
/** ClockRegistry aggregates providers and plugins */
open class ClockRegistry(
val context: Context,
@@ -51,6 +49,11 @@ open class ClockRegistry(
defaultClockProvider: DefaultClockProvider
) : this(context, pluginManager, handler, defaultClockProvider as ClockProvider) { }
// Usually this would be a typealias, but a SAM provides better java interop
fun interface ClockChangeListener {
fun onClockChanged()
}
var isEnabled: Boolean = false
private val gson = Gson()
@@ -58,7 +61,7 @@ open class ClockRegistry(
private val clockChangeListeners = mutableListOf<ClockChangeListener>()
private val settingObserver = object : ContentObserver(handler) {
override fun onChange(selfChange: Boolean, uris: Collection<Uri>, flags: Int, userId: Int) =
clockChangeListeners.forEach { it() }
clockChangeListeners.forEach { it.onClockChanged() }
}
private val pluginListener = object : PluginListener<ClockProviderPlugin> {
@@ -117,8 +120,11 @@ open class ClockRegistry(
val id = clock.clockId
val current = availableClocks[id]
if (current != null) {
Log.e(TAG, "Clock Id conflict: $id is registered by both " +
"${provider::class.simpleName} and ${current.provider::class.simpleName}")
Log.e(
TAG,
"Clock Id conflict: $id is registered by both " +
"${provider::class.simpleName} and ${current.provider::class.simpleName}"
)
return
}
@@ -127,7 +133,7 @@ open class ClockRegistry(
if (DEBUG) {
Log.i(TAG, "Current clock ($currentId) was connected")
}
clockChangeListeners.forEach { it() }
clockChangeListeners.forEach { it.onClockChanged() }
}
}
}
@@ -139,7 +145,7 @@ open class ClockRegistry(
if (currentId == clock.clockId) {
Log.w(TAG, "Current clock ($currentId) was disconnected")
clockChangeListeners.forEach { it() }
clockChangeListeners.forEach { it.onClockChanged() }
}
}
}

View File

@@ -13,16 +13,18 @@
*/
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 com.android.internal.colorextraction.ColorExtractor
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.ClockAnimation
import com.android.systemui.plugins.ClockAnimations
import com.android.systemui.plugins.ClockEvents
import com.android.systemui.plugins.ClockId
import com.android.systemui.plugins.ClockMetadata
@@ -39,6 +41,7 @@ const val DEFAULT_CLOCK_ID = "DEFAULT"
/** Provides the default system clock */
class DefaultClockProvider @Inject constructor(
val ctx: Context,
val layoutInflater: LayoutInflater,
@Main val resources: Resources
) : ClockProvider {
@@ -49,7 +52,7 @@ class DefaultClockProvider @Inject constructor(
if (id != DEFAULT_CLOCK_ID) {
throw IllegalArgumentException("$id is unsupported by $TAG")
}
return DefaultClock(layoutInflater, resources)
return DefaultClock(ctx, layoutInflater, resources)
}
override fun getClockThumbnail(id: ClockId): Drawable? {
@@ -69,14 +72,13 @@ class DefaultClockProvider @Inject constructor(
* AnimatableClockView used by the existing lockscreen clock.
*/
class DefaultClock(
private val layoutInflater: LayoutInflater,
private val resources: Resources
ctx: Context,
private val layoutInflater: LayoutInflater,
private val resources: Resources
) : Clock {
override val smallClock =
layoutInflater.inflate(R.layout.clock_default_small, null) as AnimatableClockView
override val largeClock =
layoutInflater.inflate(R.layout.clock_default_large, null) as AnimatableClockView
private val clocks = listOf(smallClock, largeClock)
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())
@@ -84,7 +86,43 @@ class DefaultClock(
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 = object : ClockEvents {
override val events: ClockEvents
override lateinit var animations: ClockAnimations
private set
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) =
@@ -102,10 +140,17 @@ class DefaultClock(
TypedValue.COMPLEX_UNIT_PX,
resources.getDimensionPixelSize(R.dimen.large_clock_text_size).toFloat()
)
recomputePadding()
}
override fun onColorPaletteChanged(palette: ColorExtractor.GradientColors) =
clocks.forEach { it.setColors(DOZE_COLOR, palette.mainColor) }
override fun onColorPaletteChanged(
resources: Resources,
smallClockIsDark: Boolean,
largeClockIsDark: Boolean
) {
updateClockColor(smallClock, smallClockIsDark)
updateClockColor(largeClock, largeClockIsDark)
}
override fun onLocaleChanged(locale: Locale) {
val nf = NumberFormat.getInstance(locale)
@@ -119,8 +164,14 @@ class DefaultClock(
}
}
override val animation = object : ClockAnimation {
override fun initialize(dozeFraction: Float, foldFraction: Float) {
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)
@@ -132,14 +183,13 @@ class DefaultClock(
}
override fun enter() {
if (dozeState.isActive) {
if (!dozeState.isActive) {
clocks.forEach { it.animateAppearOnLockscreen() }
}
}
override fun charge() = clocks.forEach { it.animateCharge { dozeState.isActive } }
private var foldState = AnimationState(0f)
override fun fold(fraction: Float) {
val (hasChanged, hasJumped) = foldState.update(fraction)
if (hasChanged) {
@@ -147,7 +197,6 @@ class DefaultClock(
}
}
private var dozeState = AnimationState(0f)
override fun doze(fraction: Float) {
val (hasChanged, hasJumped) = dozeState.update(fraction)
if (hasChanged) {
@@ -170,14 +219,26 @@ class DefaultClock(
}
}
init {
events.onLocaleChanged(Locale.getDefault())
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 {
private const val DOZE_COLOR = Color.WHITE
@VisibleForTesting const val DOZE_COLOR = Color.WHITE
private const val FORMAT_NUMBER = 1234567890
}
}

View File

@@ -22,7 +22,9 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Rect;
import android.icu.text.NumberFormat;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
@@ -30,16 +32,24 @@ import androidx.annotation.VisibleForTesting;
import com.android.settingslib.Utils;
import com.android.systemui.R;
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.flags.Flags;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shared.clocks.AnimatableClockView;
import com.android.systemui.shared.navigationbar.RegionSamplingHelper;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.util.ViewController;
import java.io.PrintWriter;
import java.util.Locale;
import java.util.Objects;
import java.util.Optional;
import java.util.TimeZone;
import java.util.concurrent.Executor;
import javax.inject.Inject;
/**
* Controller for an AnimatableClockView on the keyguard. Instantiated by
@@ -54,7 +64,10 @@ public class AnimatableClockController extends ViewController<AnimatableClockVie
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final BatteryController mBatteryController;
private final int mDozingColor = Color.WHITE;
private Optional<RegionSamplingHelper> mRegionSamplingHelper = Optional.empty();
private Rect mSamplingBounds = new Rect();
private int mLockScreenColor;
private final boolean mRegionSamplingEnabled;
private boolean mIsDozing;
private boolean mIsCharging;
@@ -67,13 +80,17 @@ public class AnimatableClockController extends ViewController<AnimatableClockVie
private final float mBurmeseLineSpacing;
private final float mDefaultLineSpacing;
@Inject
public AnimatableClockController(
AnimatableClockView view,
StatusBarStateController statusBarStateController,
BroadcastDispatcher broadcastDispatcher,
BatteryController batteryController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
@Main Resources resources
@Main Resources resources,
@Main Executor mainExecutor,
@Background Executor bgExecutor,
FeatureFlags featureFlags
) {
super(view);
mStatusBarStateController = statusBarStateController;
@@ -86,6 +103,40 @@ public class AnimatableClockController extends ViewController<AnimatableClockVie
R.dimen.keyguard_clock_line_spacing_scale_burmese);
mDefaultLineSpacing = resources.getFloat(
R.dimen.keyguard_clock_line_spacing_scale);
mRegionSamplingEnabled = featureFlags.isEnabled(Flags.REGION_SAMPLING);
if (!mRegionSamplingEnabled) {
return;
}
mRegionSamplingHelper = Optional.of(new RegionSamplingHelper(mView,
new RegionSamplingHelper.SamplingCallback() {
@Override
public void onRegionDarknessChanged(boolean isRegionDark) {
if (isRegionDark) {
mLockScreenColor = Color.WHITE;
} else {
mLockScreenColor = Color.BLACK;
}
initColors();
}
@Override
public Rect getSampledRegion(View sampledView) {
mSamplingBounds = new Rect(sampledView.getLeft(), sampledView.getTop(),
sampledView.getRight(), sampledView.getBottom());
return mSamplingBounds;
}
@Override
public boolean isSamplingEnabled() {
return mRegionSamplingEnabled;
}
}, mainExecutor, bgExecutor)
);
mRegionSamplingHelper.ifPresent((regionSamplingHelper) -> {
regionSamplingHelper.setWindowVisible(true);
});
}
private void reset() {
@@ -169,6 +220,10 @@ public class AnimatableClockController extends ViewController<AnimatableClockVie
mStatusBarStateController.addCallback(mStatusBarStateListener);
mRegionSamplingHelper.ifPresent((regionSamplingHelper) -> {
regionSamplingHelper.start(mSamplingBounds);
});
mView.onTimeZoneChanged(TimeZone.getDefault());
initColors();
mView.animateDoze(mIsDozing, false);
@@ -180,18 +235,9 @@ public class AnimatableClockController extends ViewController<AnimatableClockVie
mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateMonitorCallback);
mBatteryController.removeCallback(mBatteryCallback);
mStatusBarStateController.removeCallback(mStatusBarStateListener);
}
/**
* @return the number of pixels below the baseline. For fonts that support languages such as
* Burmese, this space can be significant.
*/
public float getBottom() {
if (mView.getPaint() != null && mView.getPaint().getFontMetrics() != null) {
return mView.getPaint().getFontMetrics().bottom;
}
return 0f;
mRegionSamplingHelper.ifPresent((regionSamplingHelper) -> {
regionSamplingHelper.stop();
});
}
/** Animate the clock appearance */
@@ -235,8 +281,10 @@ public class AnimatableClockController extends ViewController<AnimatableClockVie
}
private void initColors() {
mLockScreenColor = Utils.getColorAttrDefaultColor(getContext(),
com.android.systemui.R.attr.wallpaperTextColorAccent);
if (!mRegionSamplingEnabled) {
mLockScreenColor = Utils.getColorAttrDefaultColor(getContext(),
com.android.systemui.R.attr.wallpaperTextColorAccent);
}
mView.setColors(mDozingColor, mLockScreenColor);
mView.animateDoze(mIsDozing, false);
}
@@ -247,5 +295,8 @@ public class AnimatableClockController extends ViewController<AnimatableClockVie
public void dump(@NonNull PrintWriter pw) {
pw.println(this);
mView.dump(pw);
mRegionSamplingHelper.ifPresent((regionSamplingHelper) -> {
regionSamplingHelper.dump(pw);
});
}
}

View File

@@ -0,0 +1,231 @@
/*
* 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.keyguard
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.res.Resources
import android.text.format.DateFormat
import android.util.TypedValue
import android.view.View
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.statusbar.StatusBarStateController
import com.android.systemui.shared.regionsampling.RegionSamplingInstance
import com.android.systemui.statusbar.policy.BatteryController
import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback
import com.android.systemui.statusbar.policy.ConfigurationController
import java.io.PrintWriter
import java.util.Locale
import java.util.TimeZone
import java.util.concurrent.Executor
import javax.inject.Inject
/**
* Controller for a Clock provided by the registry and used on the keyguard. Instantiated by
* [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
) {
var clock: Clock? = null
set(value) {
field = value
if (value != null) {
value.initialize(resources, dozeAmount, 0f)
updateRegionSamplers(value)
}
}
private var isDozing = false
private set
private var isCharging = false
private var dozeAmount = 0f
private var isKeyguardShowing = false
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)
}
}
fun updateRegionSamplers(currentClock: Clock?) {
smallRegionSamplingInstance = createRegionSampler(
currentClock?.smallClock,
mainExecutor,
bgExecutor,
regionSamplingEnabled,
updateFun
)
largeRegionSamplingInstance = createRegionSampler(
currentClock?.largeClock,
mainExecutor,
bgExecutor,
regionSamplingEnabled,
updateFun
)
smallRegionSamplingInstance.startRegionSampler()
largeRegionSamplingInstance.startRegionSampler()
updateFun.updateColors()
}
protected open fun createRegionSampler(
sampledView: View?,
mainExecutor: Executor?,
bgExecutor: Executor?,
regionSamplingEnabled: Boolean,
updateFun: RegionSamplingInstance.UpdateColorCallback
): RegionSamplingInstance {
return RegionSamplingInstance(
sampledView,
mainExecutor,
bgExecutor,
regionSamplingEnabled,
updateFun)
}
lateinit var smallRegionSamplingInstance: RegionSamplingInstance
lateinit var largeRegionSamplingInstance: RegionSamplingInstance
private var smallClockIsDark = true
private var largeClockIsDark = true
private val configListener = object : ConfigurationController.ConfigurationListener {
override fun onThemeChanged() {
updateFun.updateColors()
}
}
private val batteryCallback = object : BatteryStateChangeCallback {
override fun onBatteryLevelChanged(level: Int, pluggedIn: Boolean, charging: Boolean) {
if (isKeyguardShowing && !isCharging && charging) {
clock?.animations?.charge()
}
isCharging = charging
}
}
private val localeBroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
clock?.events?.onLocaleChanged(Locale.getDefault())
}
}
private val statusBarStateListener = object : StatusBarStateController.StateListener {
override fun onDozeAmountChanged(linear: Float, eased: Float) {
clock?.animations?.doze(linear)
isDozing = linear > dozeAmount
dozeAmount = linear
}
}
private val keyguardUpdateMonitorCallback = object : KeyguardUpdateMonitorCallback() {
override fun onKeyguardVisibilityChanged(showing: Boolean) {
isKeyguardShowing = showing
if (!isKeyguardShowing) {
clock?.animations?.doze(if (isDozing) 1f else 0f)
}
}
override fun onTimeFormatChanged(timeFormat: String) {
clock?.events?.onTimeFormatChanged(DateFormat.is24HourFormat(context))
}
override fun onTimeZoneChanged(timeZone: TimeZone) {
clock?.events?.onTimeZoneChanged(timeZone)
}
override fun onUserSwitchComplete(userId: Int) {
clock?.events?.onTimeFormatChanged(DateFormat.is24HourFormat(context))
}
}
init {
isDozing = statusBarStateController.isDozing
}
fun registerListeners() {
dozeAmount = statusBarStateController.dozeAmount
isDozing = statusBarStateController.isDozing || dozeAmount != 0f
broadcastDispatcher.registerReceiver(
localeBroadcastReceiver,
IntentFilter(Intent.ACTION_LOCALE_CHANGED)
)
configurationController.addCallback(configListener)
batteryController.addCallback(batteryCallback)
keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback)
statusBarStateController.addCallback(statusBarStateListener)
smallRegionSamplingInstance.startRegionSampler()
largeRegionSamplingInstance.startRegionSampler()
}
fun unregisterListeners() {
broadcastDispatcher.unregisterReceiver(localeBroadcastReceiver)
configurationController.removeCallback(configListener)
batteryController.removeCallback(batteryCallback)
keyguardUpdateMonitor.removeCallback(keyguardUpdateMonitorCallback)
statusBarStateController.removeCallback(statusBarStateListener)
smallRegionSamplingInstance.stopRegionSampler()
largeRegionSamplingInstance.stopRegionSampler()
}
/**
* Dump information for debugging
*/
fun dump(pw: PrintWriter) {
pw.println(this)
clock?.dump(pw)
smallRegionSamplingInstance.dump(pw)
largeRegionSamplingInstance.dump(pw)
}
companion object {
private val TAG = ClockEventController::class.simpleName
private const val FORMAT_NUMBER = 1234567890
}
}

View File

@@ -5,31 +5,23 @@ import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
import androidx.annotation.IntDef;
import androidx.annotation.VisibleForTesting;
import com.android.internal.colorextraction.ColorExtractor;
import com.android.keyguard.dagger.KeyguardStatusViewScope;
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
import com.android.systemui.plugins.ClockPlugin;
import com.android.systemui.shared.clocks.AnimatableClockView;
import com.android.systemui.plugins.Clock;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import java.util.TimeZone;
/**
* Switch to show plugin clock when plugin is connected, otherwise it will show default clock.
*/
@@ -49,18 +41,11 @@ public class KeyguardClockSwitch extends RelativeLayout {
public static final int LARGE = 0;
public static final int SMALL = 1;
/**
* Optional/alternative clock injected via plugin.
*/
private ClockPlugin mClockPlugin;
/**
* Frame for small/large clocks
*/
private FrameLayout mClockFrame;
private FrameLayout mSmallClockFrame;
private FrameLayout mLargeClockFrame;
private AnimatableClockView mClockView;
private AnimatableClockView mLargeClockView;
private View mStatusArea;
private int mSmartspaceTopOffset;
@@ -80,12 +65,6 @@ public class KeyguardClockSwitch extends RelativeLayout {
@VisibleForTesting AnimatorSet mClockOutAnim = null;
private ObjectAnimator mStatusAreaAnim = null;
/**
* If the Keyguard Slice has a header (big center-aligned text.)
*/
private boolean mSupportsDarkText;
private int[] mColorPalette;
private int mClockSwitchYAmount;
@VisibleForTesting boolean mChildrenAreLaidOut = false;
@VisibleForTesting boolean mAnimateOnLayout = true;
@@ -98,97 +77,36 @@ public class KeyguardClockSwitch extends RelativeLayout {
* Apply dp changes on font/scale change
*/
public void onDensityOrFontScaleChanged() {
mLargeClockView.setTextSize(TypedValue.COMPLEX_UNIT_PX, mContext.getResources()
.getDimensionPixelSize(R.dimen.large_clock_text_size));
mClockView.setTextSize(TypedValue.COMPLEX_UNIT_PX, mContext.getResources()
.getDimensionPixelSize(R.dimen.clock_text_size));
mClockSwitchYAmount = mContext.getResources().getDimensionPixelSize(
R.dimen.keyguard_clock_switch_y_shift);
mSmartspaceTopOffset = mContext.getResources().getDimensionPixelSize(
R.dimen.keyguard_smartspace_top_offset);
}
/**
* Returns if this view is presenting a custom clock, or the default implementation.
*/
public boolean hasCustomClock() {
return mClockPlugin != null;
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mClockFrame = findViewById(R.id.lockscreen_clock_view);
mClockView = findViewById(R.id.animatable_clock_view);
mSmallClockFrame = findViewById(R.id.lockscreen_clock_view);
mLargeClockFrame = findViewById(R.id.lockscreen_clock_view_large);
mLargeClockView = findViewById(R.id.animatable_clock_view_large);
mStatusArea = findViewById(R.id.keyguard_status_area);
onDensityOrFontScaleChanged();
}
void setClockPlugin(ClockPlugin plugin, int statusBarState) {
void setClock(Clock clock, int statusBarState) {
// Disconnect from existing plugin.
if (mClockPlugin != null) {
View smallClockView = mClockPlugin.getView();
if (smallClockView != null && smallClockView.getParent() == mClockFrame) {
mClockFrame.removeView(smallClockView);
}
View bigClockView = mClockPlugin.getBigClockView();
if (bigClockView != null && bigClockView.getParent() == mLargeClockFrame) {
mLargeClockFrame.removeView(bigClockView);
}
mClockPlugin.onDestroyView();
mClockPlugin = null;
}
if (plugin == null) {
mClockView.setVisibility(View.VISIBLE);
mLargeClockView.setVisibility(View.VISIBLE);
mSmallClockFrame.removeAllViews();
mLargeClockFrame.removeAllViews();
if (clock == null) {
Log.e(TAG, "No clock being shown");
return;
}
// Attach small and big clock views to hierarchy.
View smallClockView = plugin.getView();
if (smallClockView != null) {
mClockFrame.addView(smallClockView, -1,
new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
mClockView.setVisibility(View.GONE);
}
View bigClockView = plugin.getBigClockView();
if (bigClockView != null) {
mLargeClockFrame.addView(bigClockView);
mLargeClockView.setVisibility(View.GONE);
}
// Initialize plugin parameters.
mClockPlugin = plugin;
mClockPlugin.setStyle(getPaint().getStyle());
mClockPlugin.setTextColor(getCurrentTextColor());
mClockPlugin.setDarkAmount(mDarkAmount);
if (mColorPalette != null) {
mClockPlugin.setColorPalette(mSupportsDarkText, mColorPalette);
}
}
/**
* It will also update plugin setStyle if plugin is connected.
*/
public void setStyle(Style style) {
if (mClockPlugin != null) {
mClockPlugin.setStyle(style);
}
}
/**
* It will also update plugin setTextColor if plugin is connected.
*/
public void setTextColor(int color) {
if (mClockPlugin != null) {
mClockPlugin.setTextColor(color);
}
mSmallClockFrame.addView(clock.getSmallClock());
mLargeClockFrame.addView(clock.getLargeClock());
}
private void updateClockViews(boolean useLargeClock, boolean animate) {
@@ -204,14 +122,14 @@ public class KeyguardClockSwitch extends RelativeLayout {
int direction = 1;
float statusAreaYTranslation;
if (useLargeClock) {
out = mClockFrame;
out = mSmallClockFrame;
in = mLargeClockFrame;
if (indexOfChild(in) == -1) addView(in);
direction = -1;
statusAreaYTranslation = mClockFrame.getTop() - mStatusArea.getTop()
statusAreaYTranslation = mSmallClockFrame.getTop() - mStatusArea.getTop()
+ mSmartspaceTopOffset;
} else {
in = mClockFrame;
in = mSmallClockFrame;
out = mLargeClockFrame;
statusAreaYTranslation = 0f;
@@ -269,18 +187,6 @@ public class KeyguardClockSwitch extends RelativeLayout {
mStatusAreaAnim.start();
}
/**
* Set the amount (ratio) that the device has transitioned to doze.
*
* @param darkAmount Amount of transition to doze: 1f for doze and 0f for awake.
*/
public void setDarkAmount(float darkAmount) {
mDarkAmount = darkAmount;
if (mClockPlugin != null) {
mClockPlugin.setDarkAmount(darkAmount);
}
}
/**
* Display the desired clock and hide the other one
*
@@ -312,64 +218,11 @@ public class KeyguardClockSwitch extends RelativeLayout {
mChildrenAreLaidOut = true;
}
public Paint getPaint() {
return mClockView.getPaint();
}
public int getCurrentTextColor() {
return mClockView.getCurrentTextColor();
}
public float getTextSize() {
return mClockView.getTextSize();
}
/**
* Refresh the time of the clock, due to either time tick broadcast or doze time tick alarm.
*/
public void refresh() {
if (mClockPlugin != null) {
mClockPlugin.onTimeTick();
}
}
/**
* Notifies that the time zone has changed.
*/
public void onTimeZoneChanged(TimeZone timeZone) {
if (mClockPlugin != null) {
mClockPlugin.onTimeZoneChanged(timeZone);
}
}
/**
* Notifies that the time format has changed.
*
* @param timeFormat "12" for 12-hour format, "24" for 24-hour format
*/
public void onTimeFormatChanged(String timeFormat) {
if (mClockPlugin != null) {
mClockPlugin.onTimeFormatChanged(timeFormat);
}
}
void updateColors(ColorExtractor.GradientColors colors) {
mSupportsDarkText = colors.supportsDarkText();
mColorPalette = colors.getColorPalette();
if (mClockPlugin != null) {
mClockPlugin.setColorPalette(mSupportsDarkText, mColorPalette);
}
}
public void dump(PrintWriter pw, String[] args) {
pw.println("KeyguardClockSwitch:");
pw.println(" mClockPlugin: " + mClockPlugin);
pw.println(" mClockFrame: " + mClockFrame);
pw.println(" mSmallClockFrame: " + mSmallClockFrame);
pw.println(" mLargeClockFrame: " + mLargeClockFrame);
pw.println(" mStatusArea: " + mStatusArea);
pw.println(" mDarkAmount: " + mDarkAmount);
pw.println(" mSupportsDarkText: " + mSupportsDarkText);
pw.println(" mColorPalette: " + Arrays.toString(mColorPalette));
pw.println(" mDisplayedClockSize: " + mDisplayedClockSize);
}
}

View File

@@ -22,8 +22,6 @@ import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static com.android.keyguard.KeyguardClockSwitch.LARGE;
import static com.android.keyguard.KeyguardClockSwitch.SMALL;
import android.app.WallpaperManager;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.os.UserHandle;
import android.provider.Settings;
@@ -32,36 +30,30 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import androidx.annotation.NonNull;
import com.android.internal.colorextraction.ColorExtractor;
import com.android.keyguard.clock.ClockManager;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dagger.qualifiers.Main;
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.ClockPlugin;
import com.android.systemui.plugins.Clock;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shared.clocks.ClockRegistry;
import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController;
import com.android.systemui.statusbar.notification.AnimatableProperty;
import com.android.systemui.statusbar.notification.PropertyAnimator;
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
import com.android.systemui.statusbar.phone.NotificationIconContainer;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.util.ViewController;
import com.android.systemui.util.settings.SecureSettings;
import java.io.PrintWriter;
import java.util.Locale;
import java.util.TimeZone;
import java.util.concurrent.Executor;
import javax.inject.Inject;
@@ -71,48 +63,23 @@ import javax.inject.Inject;
*/
public class KeyguardClockSwitchController extends ViewController<KeyguardClockSwitch>
implements Dumpable {
private static final boolean CUSTOM_CLOCKS_ENABLED = true;
private final StatusBarStateController mStatusBarStateController;
private final SysuiColorExtractor mColorExtractor;
private final ClockManager mClockManager;
private final ClockRegistry mClockRegistry;
private final KeyguardSliceViewController mKeyguardSliceViewController;
private final NotificationIconAreaController mNotificationIconAreaController;
private final BroadcastDispatcher mBroadcastDispatcher;
private final BatteryController mBatteryController;
private final LockscreenSmartspaceController mSmartspaceController;
private final Resources mResources;
private final SecureSettings mSecureSettings;
private final DumpManager mDumpManager;
private final ClockEventController mClockEventController;
/**
* Clock for both small and large sizes
*/
private AnimatableClockController mClockViewController;
private FrameLayout mClockFrame; // top aligned clock
private AnimatableClockController mLargeClockViewController;
private FrameLayout mSmallClockFrame; // top aligned clock
private FrameLayout mLargeClockFrame; // centered clock
@KeyguardClockSwitch.ClockSize
private int mCurrentClockSize = SMALL;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private int mKeyguardClockTopMargin = 0;
/**
* Listener for changes to the color palette.
*
* The color palette changes when the wallpaper is changed.
*/
private final ColorExtractor.OnColorsChangedListener mColorsListener =
(extractor, which) -> {
if ((which & WallpaperManager.FLAG_LOCK) != 0) {
mView.updateColors(getGradientColors());
}
};
private final ClockManager.ClockChangedListener mClockChangedListener = this::setClockPlugin;
private final ClockRegistry.ClockChangeListener mClockChangedListener;
private ViewGroup mStatusArea;
// If set will replace keyguard_slice_view
@@ -121,9 +88,9 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
private boolean mOnlyClock = false;
private Executor mUiExecutor;
private final Executor mUiExecutor;
private boolean mCanShowDoubleLineClock = true;
private ContentObserver mDoubleLineClockObserver = new ContentObserver(null) {
private final ContentObserver mDoubleLineClockObserver = new ContentObserver(null) {
@Override
public void onChange(boolean change) {
updateDoubleLineClock();
@@ -144,34 +111,32 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
public KeyguardClockSwitchController(
KeyguardClockSwitch keyguardClockSwitch,
StatusBarStateController statusBarStateController,
SysuiColorExtractor colorExtractor,
ClockManager clockManager,
ClockRegistry clockRegistry,
KeyguardSliceViewController keyguardSliceViewController,
NotificationIconAreaController notificationIconAreaController,
BroadcastDispatcher broadcastDispatcher,
BatteryController batteryController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
LockscreenSmartspaceController smartspaceController,
KeyguardUnlockAnimationController keyguardUnlockAnimationController,
SecureSettings secureSettings,
@Main Executor uiExecutor,
@Main Resources resources,
DumpManager dumpManager) {
DumpManager dumpManager,
ClockEventController clockEventController,
FeatureFlags featureFlags) {
super(keyguardClockSwitch);
mStatusBarStateController = statusBarStateController;
mColorExtractor = colorExtractor;
mClockManager = clockManager;
mClockRegistry = clockRegistry;
mKeyguardSliceViewController = keyguardSliceViewController;
mNotificationIconAreaController = notificationIconAreaController;
mBroadcastDispatcher = broadcastDispatcher;
mBatteryController = batteryController;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mSmartspaceController = smartspaceController;
mResources = resources;
mSecureSettings = secureSettings;
mUiExecutor = uiExecutor;
mKeyguardUnlockAnimationController = keyguardUnlockAnimationController;
mDumpManager = dumpManager;
mClockEventController = clockEventController;
mClockRegistry.setEnabled(featureFlags.isEnabled(Flags.LOCKSCREEN_CUSTOM_CLOCKS));
mClockChangedListener = () -> {
setClock(mClockRegistry.createCurrentClock());
};
}
/**
@@ -185,43 +150,21 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
* Attach the controller to the view it relates to.
*/
@Override
public void onInit() {
protected void onInit() {
mKeyguardSliceViewController.init();
mClockFrame = mView.findViewById(R.id.lockscreen_clock_view);
mSmallClockFrame = mView.findViewById(R.id.lockscreen_clock_view);
mLargeClockFrame = mView.findViewById(R.id.lockscreen_clock_view_large);
mClockViewController =
new AnimatableClockController(
mView.findViewById(R.id.animatable_clock_view),
mStatusBarStateController,
mBroadcastDispatcher,
mBatteryController,
mKeyguardUpdateMonitor,
mResources);
mClockViewController.init();
mLargeClockViewController =
new AnimatableClockController(
mView.findViewById(R.id.animatable_clock_view_large),
mStatusBarStateController,
mBroadcastDispatcher,
mBatteryController,
mKeyguardUpdateMonitor,
mResources);
mLargeClockViewController.init();
mDumpManager.unregisterDumpable(getClass().toString()); // unregister previous clocks
mDumpManager.registerDumpable(getClass().toString(), this);
}
@Override
protected void onViewAttached() {
if (CUSTOM_CLOCKS_ENABLED) {
mClockManager.addOnClockChangedListener(mClockChangedListener);
}
mColorExtractor.addOnColorsChangedListener(mColorsListener);
mView.updateColors(getGradientColors());
mClockRegistry.registerClockChangeListener(mClockChangedListener);
setClock(mClockRegistry.createCurrentClock());
mClockEventController.registerListeners();
mKeyguardClockTopMargin =
mView.getResources().getDimensionPixelSize(R.dimen.keyguard_clock_top_margin);
@@ -244,7 +187,6 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
ksv.setVisibility(View.GONE);
addSmartspaceView(ksvIndex);
updateClockLayout();
}
mSecureSettings.registerContentObserverForUser(
@@ -266,11 +208,9 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
@Override
protected void onViewDetached() {
if (CUSTOM_CLOCKS_ENABLED) {
mClockManager.removeOnClockChangedListener(mClockChangedListener);
}
mColorExtractor.removeOnColorsChangedListener(mColorsListener);
mView.setClockPlugin(null, mStatusBarStateController.getState());
mClockRegistry.unregisterClockChangeListener(mClockChangedListener);
mClockEventController.unregisterListeners();
setClock(null);
mSecureSettings.unregisterContentObserver(mDoubleLineClockObserver);
@@ -309,18 +249,6 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
mView.onDensityOrFontScaleChanged();
mKeyguardClockTopMargin =
mView.getResources().getDimensionPixelSize(R.dimen.keyguard_clock_top_margin);
updateClockLayout();
}
private void updateClockLayout() {
int largeClockTopMargin = getContext().getResources().getDimensionPixelSize(
R.dimen.keyguard_large_clock_top_margin)
- (int) mLargeClockViewController.getBottom();
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(MATCH_PARENT,
MATCH_PARENT);
lp.topMargin = largeClockTopMargin;
mLargeClockFrame.setLayoutParams(lp);
}
/**
@@ -334,46 +262,34 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
mCurrentClockSize = clockSize;
Clock clock = getClock();
boolean appeared = mView.switchToClock(clockSize, animate);
if (animate && appeared && clockSize == LARGE) {
mLargeClockViewController.animateAppear();
}
}
public void animateFoldToAod() {
if (mClockViewController != null) {
mClockViewController.animateFoldAppear();
mLargeClockViewController.animateFoldAppear();
if (clock != null && animate && appeared && clockSize == LARGE) {
clock.getAnimations().enter();
}
}
/**
* If we're presenting a custom clock of just the default one.
* Animates the clock view between folded and unfolded states
*/
public boolean hasCustomClock() {
return mView.hasCustomClock();
}
/**
* Get the clock text size.
*/
public float getClockTextSize() {
return mView.getTextSize();
public void animateFoldToAod(float foldFraction) {
Clock clock = getClock();
if (clock != null) {
clock.getAnimations().fold(foldFraction);
}
}
/**
* Refresh clock. Called in response to TIME_TICK broadcasts.
*/
void refresh() {
if (mClockViewController != null) {
mClockViewController.refreshTime();
mLargeClockViewController.refreshTime();
}
if (mSmartspaceController != null) {
mSmartspaceController.requestSmartspaceUpdate();
}
mView.refresh();
Clock clock = getClock();
if (clock != null) {
clock.getEvents().onTimeTick();
}
}
/**
@@ -385,7 +301,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
void updatePosition(int x, float scale, AnimationProperties props, boolean animate) {
x = getCurrentLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? -x : x;
PropertyAnimator.setProperty(mClockFrame, AnimatableProperty.TRANSLATION_X,
PropertyAnimator.setProperty(mSmallClockFrame, AnimatableProperty.TRANSLATION_X,
x, props, animate);
PropertyAnimator.setProperty(mLargeClockFrame, AnimatableProperty.SCALE_X,
scale, props, animate);
@@ -398,25 +314,39 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
}
}
void updateTimeZone(TimeZone timeZone) {
mView.onTimeZoneChanged(timeZone);
}
/**
* Get y-bottom position of the currently visible clock on the keyguard.
* We can't directly getBottom() because clock changes positions in AOD for burn-in
*/
int getClockBottom(int statusBarHeaderHeight) {
Clock clock = getClock();
if (clock == null) {
return 0;
}
if (mLargeClockFrame.getVisibility() == View.VISIBLE) {
View clock = mLargeClockFrame.findViewById(
com.android.systemui.R.id.animatable_clock_view_large);
int frameHeight = mLargeClockFrame.getHeight();
int clockHeight = clock.getHeight();
int clockHeight = clock.getLargeClock().getHeight();
return frameHeight / 2 + clockHeight / 2;
} else {
return mClockFrame.findViewById(
com.android.systemui.R.id.animatable_clock_view).getHeight()
+ statusBarHeaderHeight + mKeyguardClockTopMargin;
int clockHeight = clock.getSmallClock().getHeight();
return clockHeight + statusBarHeaderHeight + mKeyguardClockTopMargin;
}
}
/**
* Get the height of the currently visible clock on the keyguard.
*/
int getClockHeight() {
Clock clock = getClock();
if (clock == null) {
return 0;
}
if (mLargeClockFrame.getVisibility() == View.VISIBLE) {
return clock.getLargeClock().getHeight();
} else {
return clock.getSmallClock().getHeight();
}
}
@@ -431,12 +361,13 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
mNotificationIconAreaController.setupAodIcons(nic);
}
private void setClockPlugin(ClockPlugin plugin) {
mView.setClockPlugin(plugin, mStatusBarStateController.getState());
private void setClock(Clock clock) {
mClockEventController.setClock(clock);
mView.setClock(clock, mStatusBarStateController.getState());
}
private ColorExtractor.GradientColors getGradientColors() {
return mColorExtractor.getColors(WallpaperManager.FLAG_LOCK);
private Clock getClock() {
return mClockEventController.getClock();
}
private int getCurrentLayoutDirection() {
@@ -458,8 +389,6 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
* bounds during the unlock transition.
*/
private void setClipChildrenForUnlock(boolean clip) {
mView.setClipChildren(clip);
if (mStatusArea != null) {
mStatusArea.setClipChildren(clip);
}
@@ -469,8 +398,10 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
pw.println("currentClockSizeLarge=" + (mCurrentClockSize == LARGE));
pw.println("mCanShowDoubleLineClock=" + mCanShowDoubleLineClock);
mClockViewController.dump(pw);
mLargeClockViewController.dump(pw);
Clock clock = getClock();
if (clock != null) {
clock.dump(pw);
}
}
}

View File

@@ -17,14 +17,11 @@
package com.android.keyguard;
import android.content.Context;
import android.graphics.Color;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.GridLayout;
import androidx.core.graphics.ColorUtils;
import com.android.systemui.R;
import com.android.systemui.statusbar.CrossFadeHelper;
@@ -46,7 +43,6 @@ public class KeyguardStatusView extends GridLayout {
private View mMediaHostContainer;
private float mDarkAmount = 0;
private int mTextColor;
public KeyguardStatusView(Context context) {
this(context, null, 0);
@@ -71,7 +67,6 @@ public class KeyguardStatusView extends GridLayout {
}
mKeyguardSlice = findViewById(R.id.keyguard_slice_view);
mTextColor = mClockView.getCurrentTextColor();
mMediaHostContainer = findViewById(R.id.status_view_media_container);
@@ -83,15 +78,12 @@ public class KeyguardStatusView extends GridLayout {
return;
}
mDarkAmount = darkAmount;
mClockView.setDarkAmount(darkAmount);
CrossFadeHelper.fadeOut(mMediaHostContainer, darkAmount);
updateDark();
}
void updateDark() {
final int blendedTextColor = ColorUtils.blendARGB(mTextColor, Color.WHITE, mDarkAmount);
mKeyguardSlice.setDarkAmount(mDarkAmount);
mClockView.setTextColor(blendedTextColor);
}
/** Sets a translationY value on every child view except for the media view. */
@@ -113,7 +105,6 @@ public class KeyguardStatusView extends GridLayout {
public void dump(PrintWriter pw, String[] args) {
pw.println("KeyguardStatusView:");
pw.println(" mDarkAmount: " + mDarkAmount);
pw.println(" mTextColor: " + Integer.toHexString(mTextColor));
if (mClockView != null) {
mClockView.dump(pw, args);
}

View File

@@ -30,8 +30,6 @@ import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.ViewController;
import java.util.TimeZone;
import javax.inject.Inject;
/**
@@ -95,13 +93,6 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
mKeyguardSliceViewController.refresh();
}
/**
* The amount we're in doze.
*/
public void setDarkAmount(float darkAmount) {
mView.setDarkAmount(darkAmount);
}
/**
* Set which clock should be displayed on the keyguard. The other one will be automatically
* hidden.
@@ -114,16 +105,10 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
* Performs fold to aod animation of the clocks (changes font weight from bold to thin).
* This animation is played when AOD is enabled and foldable device is fully folded, it is
* displayed on the outer screen
* @param foldFraction current fraction of fold animation complete
*/
public void animateFoldToAod() {
mKeyguardClockSwitchController.animateFoldToAod();
}
/**
* If we're presenting a custom clock of just the default one.
*/
public boolean hasCustomClock() {
return mKeyguardClockSwitchController.hasCustomClock();
public void animateFoldToAod(float foldFraction) {
mKeyguardClockSwitchController.animateFoldToAod(foldFraction);
}
/**
@@ -143,24 +128,11 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
}
/**
* Set pivot x.
* Update the pivot position based on the parent view
*/
public void setPivotX(float pivot) {
mView.setPivotX(pivot);
}
/**
* Set pivot y.
*/
public void setPivotY(float pivot) {
mView.setPivotY(pivot);
}
/**
* Get the clock text size.
*/
public float getClockTextSize() {
return mKeyguardClockSwitchController.getClockTextSize();
public void updatePivot(float parentWidth, float parentHeight) {
mView.setPivotX(parentWidth / 2f);
mView.setPivotY(mKeyguardClockSwitchController.getClockHeight() / 2f);
}
/**
@@ -239,11 +211,6 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
refreshTime();
}
@Override
public void onTimeZoneChanged(TimeZone timeZone) {
mKeyguardClockSwitchController.updateTimeZone(timeZone);
}
@Override
public void onKeyguardVisibilityChanged(boolean showing) {
if (showing) {

View File

@@ -290,11 +290,6 @@ public final class NotificationPanelViewController extends PanelViewController {
private final NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
private final NotificationIconAreaController mNotificationIconAreaController;
// Cap and total height of Roboto font. Needs to be adjusted when font for the big clock is
// changed.
private static final int CAP_HEIGHT = 1456;
private static final int FONT_HEIGHT = 2163;
/**
* Maximum time before which we will expand the panel even for slow motions when getting a
* touch passed over from launcher.
@@ -1112,13 +1107,6 @@ public final class NotificationPanelViewController extends PanelViewController {
}
}
/**
* Returns if there's a custom clock being presented.
*/
public boolean hasCustomClock() {
return mKeyguardStatusViewController.hasCustomClock();
}
private void setCentralSurfaces(CentralSurfaces centralSurfaces) {
// TODO: this can be injected.
mCentralSurfaces = centralSurfaces;
@@ -3974,9 +3962,9 @@ public final class NotificationPanelViewController extends PanelViewController {
public void onAnimationEnd(Animator animation) {
endAction.run();
}
}).setUpdateListener(anim -> {
mKeyguardStatusViewController.animateFoldToAod(anim.getAnimatedFraction());
}).start();
mKeyguardStatusViewController.animateFoldToAod();
}
/**
@@ -4686,7 +4674,6 @@ public final class NotificationPanelViewController extends PanelViewController {
public void onDozeAmountChanged(float linearAmount, float amount) {
mInterpolatedDarkAmount = amount;
mLinearDarkAmount = linearAmount;
mKeyguardStatusViewController.setDarkAmount(mInterpolatedDarkAmount);
mKeyguardBottomArea.setDarkAmount(mInterpolatedDarkAmount);
positionClockAndNotifications();
}
@@ -4796,11 +4783,8 @@ public final class NotificationPanelViewController extends PanelViewController {
updateMaxDisplayedNotifications(!shouldAvoidChangingNotificationsCount());
setIsFullWidth(mNotificationStackScrollLayoutController.getWidth() == mView.getWidth());
// Update Clock Pivot
mKeyguardStatusViewController.setPivotX(((float) mView.getWidth()) / 2f);
mKeyguardStatusViewController.setPivotY(
(FONT_HEIGHT - CAP_HEIGHT) / 2048f
* mKeyguardStatusViewController.getClockTextSize());
// Update Clock Pivot (used by anti-burnin transformations)
mKeyguardStatusViewController.updatePivot(mView.getWidth(), mView.getHeight());
// Calculate quick setting heights.
int oldMaxHeight = mQsMaxExpansionHeight;

View File

@@ -442,7 +442,6 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
state |= DISABLE_CLOCK;
}
if (mNetworkController != null && EncryptionHelper.IS_DATA_ENCRYPTED) {
if (mNetworkController.hasEmergencyCryptKeeperText()) {
state |= DISABLE_NOTIFICATION_ICONS;
@@ -452,13 +451,6 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
}
}
// The shelf will be hidden when dozing with a custom clock, we must show notification
// icons in this occasion.
if (mStatusBarStateController.isDozing()
&& mNotificationPanelViewController.hasCustomClock()) {
state |= DISABLE_CLOCK | DISABLE_SYSTEM_INFO;
}
if (mOngoingCallController.hasOngoingCall()) {
state &= ~DISABLE_ONGOING_CALL_CHIP;
} else {

View File

@@ -0,0 +1,270 @@
/*
* 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.keyguard
import android.content.BroadcastReceiver
import android.testing.AndroidTestingRunner
import android.widget.TextView
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.ClockEvents
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.policy.BatteryController
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
import java.util.TimeZone
import java.util.concurrent.Executor
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyFloat
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
import org.mockito.Mockito.never
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
import org.mockito.junit.MockitoJUnit
@RunWith(AndroidTestingRunner::class)
@SmallTest
class ClockEventControllerTest : SysuiTestCase() {
@JvmField @Rule val mockito = MockitoJUnit.rule()
@Mock private lateinit var statusBarStateController: StatusBarStateController
@Mock private lateinit var broadcastDispatcher: BroadcastDispatcher
@Mock private lateinit var batteryController: BatteryController
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@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 mainExecutor: Executor
@Mock private lateinit var bgExecutor: Executor
@Mock private lateinit var featureFlags: FeatureFlags
private lateinit var clockEventController: ClockEventController
@Before
fun setUp() {
whenever(clock.smallClock).thenReturn(TextView(context))
whenever(clock.largeClock).thenReturn(TextView(context))
whenever(clock.events).thenReturn(events)
whenever(clock.animations).thenReturn(animations)
clockEventController = ClockEventController(
statusBarStateController,
broadcastDispatcher,
batteryController,
keyguardUpdateMonitor,
configurationController,
context.resources,
context,
mainExecutor,
bgExecutor,
featureFlags
)
}
@Test
fun clockSet_validateInitialization() {
clockEventController.clock = clock
verify(clock).initialize(any(), anyFloat(), anyFloat())
}
@Test
fun clockUnset_validateState() {
clockEventController.clock = clock
clockEventController.clock = null
assertEquals(clockEventController.clock, null)
}
@Test
fun themeChanged_verifyClockPaletteUpdated() {
clockEventController.clock = clock
verify(events).onColorPaletteChanged(any(), any(), any())
clockEventController.registerListeners()
val captor = argumentCaptor<ConfigurationController.ConfigurationListener>()
verify(configurationController).addCallback(capture(captor))
captor.value.onThemeChanged()
verify(events, times(2)).onColorPaletteChanged(any(), any(), any())
}
@Test
fun batteryCallback_keyguardShowingCharging_verifyChargeAnimation() {
clockEventController.clock = clock
clockEventController.registerListeners()
val batteryCaptor = argumentCaptor<BatteryController.BatteryStateChangeCallback>()
verify(batteryController).addCallback(capture(batteryCaptor))
val keyguardCaptor = argumentCaptor<KeyguardUpdateMonitorCallback>()
verify(keyguardUpdateMonitor).registerCallback(capture(keyguardCaptor))
keyguardCaptor.value.onKeyguardVisibilityChanged(true)
batteryCaptor.value.onBatteryLevelChanged(10, false, true)
verify(animations).charge()
}
@Test
fun batteryCallback_keyguardShowingCharging_Duplicate_verifyChargeAnimation() {
clockEventController.clock = clock
clockEventController.registerListeners()
val batteryCaptor = argumentCaptor<BatteryController.BatteryStateChangeCallback>()
verify(batteryController).addCallback(capture(batteryCaptor))
val keyguardCaptor = argumentCaptor<KeyguardUpdateMonitorCallback>()
verify(keyguardUpdateMonitor).registerCallback(capture(keyguardCaptor))
keyguardCaptor.value.onKeyguardVisibilityChanged(true)
batteryCaptor.value.onBatteryLevelChanged(10, false, true)
batteryCaptor.value.onBatteryLevelChanged(10, false, true)
verify(animations, times(1)).charge()
}
@Test
fun batteryCallback_keyguardHiddenCharging_verifyChargeAnimation() {
clockEventController.clock = clock
clockEventController.registerListeners()
val batteryCaptor = argumentCaptor<BatteryController.BatteryStateChangeCallback>()
verify(batteryController).addCallback(capture(batteryCaptor))
val keyguardCaptor = argumentCaptor<KeyguardUpdateMonitorCallback>()
verify(keyguardUpdateMonitor).registerCallback(capture(keyguardCaptor))
keyguardCaptor.value.onKeyguardVisibilityChanged(false)
batteryCaptor.value.onBatteryLevelChanged(10, false, true)
verify(animations, never()).charge()
}
@Test
fun batteryCallback_keyguardShowingNotCharging_verifyChargeAnimation() {
clockEventController.clock = clock
clockEventController.registerListeners()
val batteryCaptor = argumentCaptor<BatteryController.BatteryStateChangeCallback>()
verify(batteryController).addCallback(capture(batteryCaptor))
val keyguardCaptor = argumentCaptor<KeyguardUpdateMonitorCallback>()
verify(keyguardUpdateMonitor).registerCallback(capture(keyguardCaptor))
keyguardCaptor.value.onKeyguardVisibilityChanged(true)
batteryCaptor.value.onBatteryLevelChanged(10, false, false)
verify(animations, never()).charge()
}
@Test
fun localeCallback_verifyClockNotified() {
clockEventController.clock = clock
clockEventController.registerListeners()
val captor = argumentCaptor<BroadcastReceiver>()
verify(broadcastDispatcher).registerReceiver(
capture(captor), any(), eq(null), eq(null), anyInt(), eq(null)
)
captor.value.onReceive(context, mock())
verify(events).onLocaleChanged(any())
}
@Test
fun keyguardCallback_visibilityChanged_clockDozeCalled() {
clockEventController.clock = clock
clockEventController.registerListeners()
val captor = argumentCaptor<KeyguardUpdateMonitorCallback>()
verify(keyguardUpdateMonitor).registerCallback(capture(captor))
captor.value.onKeyguardVisibilityChanged(true)
verify(animations, never()).doze(0f)
captor.value.onKeyguardVisibilityChanged(false)
verify(animations, times(1)).doze(0f)
}
@Test
fun keyguardCallback_timeFormat_clockNotified() {
clockEventController.clock = clock
clockEventController.registerListeners()
val captor = argumentCaptor<KeyguardUpdateMonitorCallback>()
verify(keyguardUpdateMonitor).registerCallback(capture(captor))
captor.value.onTimeFormatChanged("12h")
verify(events).onTimeFormatChanged(false)
}
@Test
fun keyguardCallback_timezoneChanged_clockNotified() {
val mockTimeZone = mock<TimeZone>()
clockEventController.clock = clock
clockEventController.registerListeners()
val captor = argumentCaptor<KeyguardUpdateMonitorCallback>()
verify(keyguardUpdateMonitor).registerCallback(capture(captor))
captor.value.onTimeZoneChanged(mockTimeZone)
verify(events).onTimeZoneChanged(mockTimeZone)
}
@Test
fun keyguardCallback_userSwitched_clockNotified() {
clockEventController.clock = clock
clockEventController.registerListeners()
val captor = argumentCaptor<KeyguardUpdateMonitorCallback>()
verify(keyguardUpdateMonitor).registerCallback(capture(captor))
captor.value.onUserSwitchComplete(10)
verify(events).onTimeFormatChanged(false)
}
@Test
fun keyguardCallback_verifyKeyguardChanged() {
clockEventController.clock = clock
clockEventController.registerListeners()
val captor = argumentCaptor<StatusBarStateController.StateListener>()
verify(statusBarStateController).addCallback(capture(captor))
captor.value.onDozeAmountChanged(0.4f, 0.6f)
verify(animations).doze(0.4f)
}
@Test
fun unregisterListeners_validate() {
clockEventController.clock = clock
clockEventController.unregisterListeners()
verify(broadcastDispatcher).unregisterReceiver(any())
verify(configurationController).removeCallback(any())
verify(batteryController).removeCallback(any())
verify(keyguardUpdateMonitor).removeCallback(any())
verify(statusBarStateController).removeCallback(any())
}
}

View File

@@ -19,7 +19,6 @@ package com.android.keyguard;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -41,24 +40,19 @@ import android.widget.RelativeLayout;
import androidx.test.filters.SmallTest;
import com.android.internal.colorextraction.ColorExtractor;
import com.android.keyguard.clock.ClockManager;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.plugins.ClockPlugin;
import com.android.systemui.plugins.Clock;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shared.clocks.AnimatableClockView;
import com.android.systemui.shared.clocks.ClockRegistry;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
import com.android.systemui.statusbar.phone.NotificationIconContainer;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.util.time.FakeSystemClock;
@@ -80,22 +74,12 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase {
@Mock
private StatusBarStateController mStatusBarStateController;
@Mock
private SysuiColorExtractor mColorExtractor;
@Mock
private ClockManager mClockManager;
private ClockRegistry mClockRegistry;
@Mock
KeyguardSliceViewController mKeyguardSliceViewController;
@Mock
NotificationIconAreaController mNotificationIconAreaController;
@Mock
BroadcastDispatcher mBroadcastDispatcher;
@Mock
BatteryController mBatteryController;
@Mock
KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock
KeyguardBypassController mBypassController;
@Mock
LockscreenSmartspaceController mSmartspaceController;
@Mock
@@ -103,11 +87,11 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase {
@Mock
KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
@Mock
private ClockPlugin mClockPlugin;
@Mock
ColorExtractor.GradientColors mGradientColors;
private Clock mClock;
@Mock
DumpManager mDumpManager;
@Mock
ClockEventController mClockEventController;
@Mock
private NotificationIconContainer mNotificationIcons;
@@ -139,8 +123,6 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase {
when(mView.getContext()).thenReturn(getContext());
when(mView.getResources()).thenReturn(mResources);
when(mView.findViewById(R.id.animatable_clock_view)).thenReturn(mClockView);
when(mView.findViewById(R.id.animatable_clock_view_large)).thenReturn(mLargeClockView);
when(mView.findViewById(R.id.lockscreen_clock_view_large)).thenReturn(mLargeClockFrame);
when(mClockView.getContext()).thenReturn(getContext());
when(mLargeClockView.getContext()).thenReturn(getContext());
@@ -151,23 +133,20 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase {
mController = new KeyguardClockSwitchController(
mView,
mStatusBarStateController,
mColorExtractor,
mClockManager,
mClockRegistry,
mKeyguardSliceViewController,
mNotificationIconAreaController,
mBroadcastDispatcher,
mBatteryController,
mKeyguardUpdateMonitor,
mSmartspaceController,
mKeyguardUnlockAnimationController,
mSecureSettings,
mExecutor,
mResources,
mDumpManager
mDumpManager,
mClockEventController,
mFeatureFlags
);
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
when(mColorExtractor.getColors(anyInt())).thenReturn(mGradientColors);
when(mClockRegistry.createCurrentClock()).thenReturn(mClock);
mSliceView = new View(getContext());
when(mView.findViewById(R.id.keyguard_slice_view)).thenReturn(mSliceView);
@@ -214,20 +193,20 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase {
verifyAttachment(times(1));
listenerArgumentCaptor.getValue().onViewDetachedFromWindow(mView);
verify(mColorExtractor).removeOnColorsChangedListener(
any(ColorExtractor.OnColorsChangedListener.class));
verify(mClockEventController).unregisterListeners();
}
@Test
public void testPluginPassesStatusBarState() {
ArgumentCaptor<ClockManager.ClockChangedListener> listenerArgumentCaptor =
ArgumentCaptor.forClass(ClockManager.ClockChangedListener.class);
ArgumentCaptor<ClockRegistry.ClockChangeListener> listenerArgumentCaptor =
ArgumentCaptor.forClass(ClockRegistry.ClockChangeListener.class);
mController.init();
verify(mClockManager).addOnClockChangedListener(listenerArgumentCaptor.capture());
verify(mClockRegistry).registerClockChangeListener(listenerArgumentCaptor.capture());
listenerArgumentCaptor.getValue().onClockChanged(mClockPlugin);
verify(mView).setClockPlugin(mClockPlugin, StatusBarState.SHADE);
listenerArgumentCaptor.getValue().onClockChanged();
verify(mView, times(2)).setClock(mClock, StatusBarState.SHADE);
verify(mClockEventController, times(2)).setClock(mClock);
}
@Test
@@ -284,10 +263,8 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase {
}
private void verifyAttachment(VerificationMode times) {
verify(mClockManager, times).addOnClockChangedListener(
any(ClockManager.ClockChangedListener.class));
verify(mColorExtractor, times).addOnColorsChangedListener(
any(ColorExtractor.OnColorsChangedListener.class));
verify(mView, times).updateColors(mGradientColors);
verify(mClockRegistry, times).registerClockChangeListener(
any(ClockRegistry.ClockChangeListener.class));
verify(mClockEventController, times).registerListeners();
}
}

View File

@@ -16,7 +16,6 @@
package com.android.keyguard;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
import static com.android.keyguard.KeyguardClockSwitch.LARGE;
@@ -24,56 +23,61 @@ import static com.android.keyguard.KeyguardClockSwitch.SMALL;
import static com.google.common.truth.Truth.assertThat;
import static junit.framework.TestCase.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.graphics.Color;
import android.graphics.Paint.Style;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.TextClock;
import android.widget.TextView;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.ClockPlugin;
import com.android.systemui.shared.clocks.AnimatableClockView;
import com.android.systemui.plugins.Clock;
import com.android.systemui.statusbar.StatusBarState;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidTestingRunner.class)
// Need to run on the main thread because KeyguardSliceView$Row init checks for
// the main thread before acquiring a wake lock. This class is constructed when
// the keyguard_clcok_switch layout is inflated.
// the keyguard_clock_switch layout is inflated.
@RunWithLooper(setAsMainLooper = true)
public class KeyguardClockSwitchTest extends SysuiTestCase {
private FrameLayout mClockFrame;
private FrameLayout mLargeClockFrame;
private TextClock mBigClock;
@Mock
ViewGroup mMockKeyguardSliceView;
@Mock
Clock mClock;
private FrameLayout mSmallClockFrame;
private FrameLayout mLargeClockFrame;
private AnimatableClockView mClockView;
private AnimatableClockView mLargeClockView;
View mMockKeyguardSliceView;
KeyguardClockSwitch mKeyguardClockSwitch;
@Before
public void setUp() {
mMockKeyguardSliceView = mock(KeyguardSliceView.class);
MockitoAnnotations.initMocks(this);
when(mMockKeyguardSliceView.getContext()).thenReturn(mContext);
when(mMockKeyguardSliceView.findViewById(R.id.keyguard_status_area))
.thenReturn(mMockKeyguardSliceView);
when(mClock.getSmallClock()).thenReturn(new TextView(getContext()));
when(mClock.getLargeClock()).thenReturn(new TextView(getContext()));
LayoutInflater layoutInflater = LayoutInflater.from(getContext());
layoutInflater.setPrivateFactory(new LayoutInflater.Factory2() {
@@ -93,164 +97,68 @@ public class KeyguardClockSwitchTest extends SysuiTestCase {
});
mKeyguardClockSwitch =
(KeyguardClockSwitch) layoutInflater.inflate(R.layout.keyguard_clock_switch, null);
mClockFrame = mKeyguardClockSwitch.findViewById(R.id.lockscreen_clock_view);
mClockView = mKeyguardClockSwitch.findViewById(R.id.animatable_clock_view);
mSmallClockFrame = mKeyguardClockSwitch.findViewById(R.id.lockscreen_clock_view);
mLargeClockFrame = mKeyguardClockSwitch.findViewById(R.id.lockscreen_clock_view_large);
mLargeClockView = mKeyguardClockSwitch.findViewById(R.id.animatable_clock_view_large);
mBigClock = new TextClock(getContext());
mKeyguardClockSwitch.mChildrenAreLaidOut = true;
MockitoAnnotations.initMocks(this);
}
@Test
public void onPluginConnected_showPluginClock() {
ClockPlugin plugin = mock(ClockPlugin.class);
TextClock pluginView = new TextClock(getContext());
when(plugin.getView()).thenReturn(pluginView);
mKeyguardClockSwitch.setClockPlugin(plugin, StatusBarState.KEYGUARD);
assertThat(mClockView.getVisibility()).isEqualTo(GONE);
assertThat(plugin.getView().getParent()).isEqualTo(mClockFrame);
public void noPluginConnected_showNothing() {
mKeyguardClockSwitch.setClock(null, StatusBarState.KEYGUARD);
assertEquals(mLargeClockFrame.getChildCount(), 0);
assertEquals(mSmallClockFrame.getChildCount(), 0);
}
@Test
public void onPluginConnected_showPluginBigClock() {
// GIVEN the plugin returns a view for the big clock
ClockPlugin plugin = mock(ClockPlugin.class);
when(plugin.getBigClockView()).thenReturn(mBigClock);
// WHEN the plugin is connected
mKeyguardClockSwitch.setClockPlugin(plugin, StatusBarState.KEYGUARD);
// THEN the big clock container is visible and it is the parent of the
// big clock view.
assertThat(mLargeClockView.getVisibility()).isEqualTo(View.GONE);
assertThat(mBigClock.getParent()).isEqualTo(mLargeClockFrame);
public void pluginConnectedThenDisconnected_showNothing() {
mKeyguardClockSwitch.setClock(mClock, StatusBarState.KEYGUARD);
assertEquals(mLargeClockFrame.getChildCount(), 1);
assertEquals(mSmallClockFrame.getChildCount(), 1);
mKeyguardClockSwitch.setClock(null, StatusBarState.KEYGUARD);
assertEquals(mLargeClockFrame.getChildCount(), 0);
assertEquals(mSmallClockFrame.getChildCount(), 0);
}
@Test
public void onPluginConnected_nullView() {
ClockPlugin plugin = mock(ClockPlugin.class);
mKeyguardClockSwitch.setClockPlugin(plugin, StatusBarState.KEYGUARD);
assertThat(mClockView.getVisibility()).isEqualTo(VISIBLE);
public void onPluginConnected_showClock() {
mKeyguardClockSwitch.setClock(mClock, StatusBarState.KEYGUARD);
assertEquals(mClock.getSmallClock().getParent(), mSmallClockFrame);
assertEquals(mClock.getLargeClock().getParent(), mLargeClockFrame);
}
@Test
public void onPluginConnected_showSecondPluginClock() {
// GIVEN a plugin has already connected
ClockPlugin plugin1 = mock(ClockPlugin.class);
when(plugin1.getView()).thenReturn(new TextClock(getContext()));
mKeyguardClockSwitch.setClockPlugin(plugin1, StatusBarState.KEYGUARD);
// WHEN a second plugin is connected
ClockPlugin plugin2 = mock(ClockPlugin.class);
when(plugin2.getView()).thenReturn(new TextClock(getContext()));
mKeyguardClockSwitch.setClockPlugin(plugin2, StatusBarState.KEYGUARD);
Clock otherClock = mock(Clock.class);
when(otherClock.getSmallClock()).thenReturn(new TextView(getContext()));
when(otherClock.getLargeClock()).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(plugin2.getView().getParent()).isEqualTo(mClockFrame);
assertThat(plugin1.getView().getParent()).isNull();
}
@Test
public void onPluginConnected_darkAmountInitialized() {
// GIVEN that the dark amount has already been set
mKeyguardClockSwitch.setDarkAmount(0.5f);
// WHEN a plugin is connected
ClockPlugin plugin = mock(ClockPlugin.class);
mKeyguardClockSwitch.setClockPlugin(plugin, StatusBarState.KEYGUARD);
// THEN dark amount should be initalized on the plugin.
verify(plugin).setDarkAmount(0.5f);
}
@Test
public void onPluginDisconnected_showDefaultClock() {
ClockPlugin plugin = mock(ClockPlugin.class);
TextClock pluginView = new TextClock(getContext());
when(plugin.getView()).thenReturn(pluginView);
mKeyguardClockSwitch.setClockPlugin(plugin, StatusBarState.KEYGUARD);
assertThat(mClockView.getVisibility()).isEqualTo(GONE);
mKeyguardClockSwitch.setClockPlugin(null, StatusBarState.KEYGUARD);
assertThat(mClockView.getVisibility()).isEqualTo(VISIBLE);
assertThat(plugin.getView().getParent()).isNull();
}
@Test
public void onPluginDisconnected_hidePluginBigClock() {
// GIVEN the plugin returns a view for the big clock
ClockPlugin plugin = mock(ClockPlugin.class);
TextClock pluginView = new TextClock(getContext());
when(plugin.getBigClockView()).thenReturn(pluginView);
// WHEN the plugin is connected and then disconnected
mKeyguardClockSwitch.setClockPlugin(plugin, StatusBarState.KEYGUARD);
mKeyguardClockSwitch.setClockPlugin(null, StatusBarState.KEYGUARD);
// THEN the big lock container is GONE and the big clock view doesn't have
// a parent.
assertThat(mLargeClockView.getVisibility()).isEqualTo(VISIBLE);
assertThat(pluginView.getParent()).isNull();
}
@Test
public void onPluginDisconnected_nullView() {
ClockPlugin plugin = mock(ClockPlugin.class);
mKeyguardClockSwitch.setClockPlugin(plugin, StatusBarState.KEYGUARD);
mKeyguardClockSwitch.setClockPlugin(null, StatusBarState.KEYGUARD);
assertThat(mClockView.getVisibility()).isEqualTo(VISIBLE);
assertThat(otherClock.getSmallClock().getParent()).isEqualTo(mSmallClockFrame);
assertThat(otherClock.getLargeClock().getParent()).isEqualTo(mLargeClockFrame);
assertThat(mClock.getSmallClock().getParent()).isNull();
assertThat(mClock.getLargeClock().getParent()).isNull();
}
@Test
public void onPluginDisconnected_secondOfTwoDisconnected() {
// GIVEN two plugins are connected
ClockPlugin plugin1 = mock(ClockPlugin.class);
when(plugin1.getView()).thenReturn(new TextClock(getContext()));
mKeyguardClockSwitch.setClockPlugin(plugin1, StatusBarState.KEYGUARD);
ClockPlugin plugin2 = mock(ClockPlugin.class);
when(plugin2.getView()).thenReturn(new TextClock(getContext()));
mKeyguardClockSwitch.setClockPlugin(plugin2, StatusBarState.KEYGUARD);
Clock otherClock = mock(Clock.class);
when(otherClock.getSmallClock()).thenReturn(new TextView(getContext()));
when(otherClock.getLargeClock()).thenReturn(new TextView(getContext()));
mKeyguardClockSwitch.setClock(otherClock, StatusBarState.KEYGUARD);
mKeyguardClockSwitch.setClock(mClock, StatusBarState.KEYGUARD);
// WHEN the second plugin is disconnected
mKeyguardClockSwitch.setClockPlugin(null, StatusBarState.KEYGUARD);
// THEN the default clock should be shown.
assertThat(mClockView.getVisibility()).isEqualTo(VISIBLE);
assertThat(plugin1.getView().getParent()).isNull();
assertThat(plugin2.getView().getParent()).isNull();
}
@Test
public void onPluginDisconnected_onDestroyView() {
// GIVEN a plugin is connected
ClockPlugin clockPlugin = mock(ClockPlugin.class);
when(clockPlugin.getView()).thenReturn(new TextClock(getContext()));
mKeyguardClockSwitch.setClockPlugin(clockPlugin, StatusBarState.KEYGUARD);
// WHEN the plugin is disconnected
mKeyguardClockSwitch.setClockPlugin(null, StatusBarState.KEYGUARD);
// THEN onDestroyView is called on the plugin
verify(clockPlugin).onDestroyView();
}
@Test
public void setTextColor_pluginClockSetTextColor() {
ClockPlugin plugin = mock(ClockPlugin.class);
TextClock pluginView = new TextClock(getContext());
when(plugin.getView()).thenReturn(pluginView);
mKeyguardClockSwitch.setClockPlugin(plugin, StatusBarState.KEYGUARD);
mKeyguardClockSwitch.setTextColor(Color.WHITE);
verify(plugin).setTextColor(Color.WHITE);
}
@Test
public void setStyle_pluginClockSetStyle() {
ClockPlugin plugin = mock(ClockPlugin.class);
TextClock pluginView = new TextClock(getContext());
when(plugin.getView()).thenReturn(pluginView);
Style style = mock(Style.class);
mKeyguardClockSwitch.setClockPlugin(plugin, StatusBarState.KEYGUARD);
mKeyguardClockSwitch.setStyle(style);
verify(plugin).setStyle(style);
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();
}
@Test
@@ -262,7 +170,7 @@ public class KeyguardClockSwitchTest extends SysuiTestCase {
assertThat(mLargeClockFrame.getAlpha()).isEqualTo(1);
assertThat(mLargeClockFrame.getVisibility()).isEqualTo(VISIBLE);
assertThat(mClockFrame.getAlpha()).isEqualTo(0);
assertThat(mSmallClockFrame.getAlpha()).isEqualTo(0);
}
@Test
@@ -271,7 +179,7 @@ public class KeyguardClockSwitchTest extends SysuiTestCase {
assertThat(mLargeClockFrame.getAlpha()).isEqualTo(1);
assertThat(mLargeClockFrame.getVisibility()).isEqualTo(VISIBLE);
assertThat(mClockFrame.getAlpha()).isEqualTo(0);
assertThat(mSmallClockFrame.getAlpha()).isEqualTo(0);
}
@Test
@@ -281,8 +189,8 @@ public class KeyguardClockSwitchTest extends SysuiTestCase {
mKeyguardClockSwitch.mClockInAnim.end();
mKeyguardClockSwitch.mClockOutAnim.end();
assertThat(mClockFrame.getAlpha()).isEqualTo(1);
assertThat(mClockFrame.getVisibility()).isEqualTo(VISIBLE);
assertThat(mSmallClockFrame.getAlpha()).isEqualTo(1);
assertThat(mSmallClockFrame.getVisibility()).isEqualTo(VISIBLE);
// only big clock is removed at switch
assertThat(mLargeClockFrame.getParent()).isNull();
assertThat(mLargeClockFrame.getAlpha()).isEqualTo(0);
@@ -292,8 +200,8 @@ public class KeyguardClockSwitchTest extends SysuiTestCase {
public void switchingToSmallClockNoAnimation_makesBigClockDisappear() {
mKeyguardClockSwitch.switchToClock(SMALL, false);
assertThat(mClockFrame.getAlpha()).isEqualTo(1);
assertThat(mClockFrame.getVisibility()).isEqualTo(VISIBLE);
assertThat(mSmallClockFrame.getAlpha()).isEqualTo(1);
assertThat(mSmallClockFrame.getVisibility()).isEqualTo(VISIBLE);
// only big clock is removed at switch
assertThat(mLargeClockFrame.getParent()).isNull();
assertThat(mLargeClockFrame.getAlpha()).isEqualTo(0);

View File

@@ -38,6 +38,7 @@ import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.settingslib.Utils;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shared.clocks.AnimatableClockView;
import com.android.systemui.statusbar.policy.BatteryController;
@@ -53,6 +54,8 @@ import org.mockito.MockitoAnnotations;
import org.mockito.MockitoSession;
import org.mockito.quality.Strictness;
import java.util.concurrent.Executor;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@@ -69,6 +72,12 @@ public class AnimatableClockControllerTest extends SysuiTestCase {
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock
private Resources mResources;
@Mock
private Executor mMainExecutor;
@Mock
private Executor mBgExecutor;
@Mock
private FeatureFlags mFeatureFlags;
private MockitoSession mStaticMockSession;
private AnimatableClockController mAnimatableClockController;
@@ -96,7 +105,10 @@ public class AnimatableClockControllerTest extends SysuiTestCase {
mBroadcastDispatcher,
mBatteryController,
mKeyguardUpdateMonitor,
mResources
mResources,
mMainExecutor,
mBgExecutor,
mFeatureFlags
);
mAnimatableClockController.init();
captureAttachListener();

View File

@@ -130,13 +130,16 @@ class ClockRegistryTest : SysuiTestCase() {
pluginListener.onPluginConnected(plugin1, mockContext)
pluginListener.onPluginConnected(plugin2, mockContext)
val list = registry.getClocks()
assertEquals(list, listOf(
ClockMetadata(DEFAULT_CLOCK_ID, DEFAULT_CLOCK_NAME),
ClockMetadata("clock_1", "clock 1"),
ClockMetadata("clock_2", "clock 2"),
ClockMetadata("clock_3", "clock 3"),
ClockMetadata("clock_4", "clock 4")
))
assertEquals(
list,
listOf(
ClockMetadata(DEFAULT_CLOCK_ID, DEFAULT_CLOCK_NAME),
ClockMetadata("clock_1", "clock 1"),
ClockMetadata("clock_2", "clock 2"),
ClockMetadata("clock_3", "clock 3"),
ClockMetadata("clock_4", "clock 4")
)
)
}
@Test
@@ -158,11 +161,14 @@ class ClockRegistryTest : SysuiTestCase() {
pluginListener.onPluginConnected(plugin1, mockContext)
pluginListener.onPluginConnected(plugin2, mockContext)
val list = registry.getClocks()
assertEquals(list, listOf(
ClockMetadata(DEFAULT_CLOCK_ID, DEFAULT_CLOCK_NAME),
ClockMetadata("clock_1", "clock 1"),
ClockMetadata("clock_2", "clock 2")
))
assertEquals(
list,
listOf(
ClockMetadata(DEFAULT_CLOCK_ID, DEFAULT_CLOCK_NAME),
ClockMetadata("clock_1", "clock 1"),
ClockMetadata("clock_2", "clock 2")
)
)
assertEquals(registry.createExampleClock("clock_1"), mockClock)
assertEquals(registry.createExampleClock("clock_2"), mockClock)
@@ -222,7 +228,7 @@ class ClockRegistryTest : SysuiTestCase() {
pluginListener.onPluginConnected(plugin2, mockContext)
var changeCallCount = 0
registry.registerClockChangeListener({ changeCallCount++ })
registry.registerClockChangeListener { changeCallCount++ }
pluginListener.onPluginDisconnected(plugin1)
assertEquals(0, changeCallCount)

View File

@@ -19,17 +19,31 @@ package com.android.systemui.shared.clocks
import android.content.res.Resources
import android.graphics.drawable.Drawable
import android.testing.AndroidTestingRunner
import android.util.TypedValue
import android.view.LayoutInflater
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.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
import java.util.Locale
import java.util.TimeZone
import junit.framework.Assert.assertEquals
import junit.framework.Assert.assertNotNull
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.ArgumentMatchers.notNull
import org.mockito.Mock
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
import org.mockito.junit.MockitoJUnit
@@ -39,7 +53,8 @@ class DefaultClockProviderTest : SysuiTestCase() {
@JvmField @Rule val mockito = MockitoJUnit.rule()
@Mock private lateinit var mockClockView: AnimatableClockView
@Mock private lateinit var mockSmallClockView: AnimatableClockView
@Mock private lateinit var mockLargeClockView: AnimatableClockView
@Mock private lateinit var layoutInflater: LayoutInflater
@Mock private lateinit var mockClockThumbnail: Drawable
@Mock private lateinit var resources: Resources
@@ -47,14 +62,16 @@ class DefaultClockProviderTest : SysuiTestCase() {
@Before
fun setUp() {
whenever(layoutInflater.inflate(R.layout.clock_default_small, null))
.thenReturn(mockClockView)
whenever(layoutInflater.inflate(R.layout.clock_default_large, null))
.thenReturn(mockClockView)
whenever(layoutInflater.inflate(eq(R.layout.clock_default_small), any(), anyBoolean()))
.thenReturn(mockSmallClockView)
whenever(layoutInflater.inflate(eq(R.layout.clock_default_large), any(), anyBoolean()))
.thenReturn(mockLargeClockView)
whenever(resources.getDrawable(R.drawable.clock_default_thumbnail, null))
.thenReturn(mockClockThumbnail)
whenever(mockSmallClockView.getLayoutParams()).thenReturn(FrameLayout.LayoutParams(10, 10))
whenever(mockLargeClockView.getLayoutParams()).thenReturn(FrameLayout.LayoutParams(10, 10))
provider = DefaultClockProvider(layoutInflater, resources)
provider = DefaultClockProvider(context, layoutInflater, resources)
}
@Test
@@ -71,7 +88,79 @@ class DefaultClockProviderTest : SysuiTestCase() {
// Default clock provider must always provide the default clock
val clock = provider.createClock(DEFAULT_CLOCK_ID)
assertNotNull(clock)
assertEquals(clock.smallClock, mockClockView)
assertEquals(clock.largeClock, mockClockView)
assertEquals(clock.smallClock, mockSmallClockView)
assertEquals(clock.largeClock, mockLargeClockView)
}
@Test
fun defaultClock_initialize() {
val clock = provider.createClock(DEFAULT_CLOCK_ID)
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).onTimeZoneChanged(notNull())
verify(mockLargeClockView).onTimeZoneChanged(notNull())
verify(mockSmallClockView).refreshTime()
verify(mockLargeClockView).refreshTime()
verify(mockLargeClockView).setLayoutParams(any())
}
@Test
fun defaultClock_events_onTimeTick() {
val clock = provider.createClock(DEFAULT_CLOCK_ID)
clock.events.onTimeTick()
verify(mockSmallClockView).refreshTime()
verify(mockLargeClockView).refreshTime()
}
@Test
fun defaultClock_events_onTimeFormatChanged() {
val clock = provider.createClock(DEFAULT_CLOCK_ID)
clock.events.onTimeFormatChanged(true)
verify(mockSmallClockView).refreshFormat(true)
verify(mockLargeClockView).refreshFormat(true)
}
@Test
fun defaultClock_events_onTimeZoneChanged() {
val timeZone = mock<TimeZone>()
val clock = provider.createClock(DEFAULT_CLOCK_ID)
clock.events.onTimeZoneChanged(timeZone)
verify(mockSmallClockView).onTimeZoneChanged(timeZone)
verify(mockLargeClockView).onTimeZoneChanged(timeZone)
}
@Test
fun defaultClock_events_onFontSettingChanged() {
val clock = provider.createClock(DEFAULT_CLOCK_ID)
clock.events.onFontSettingChanged()
verify(mockSmallClockView).setTextSize(eq(TypedValue.COMPLEX_UNIT_PX), anyFloat())
verify(mockLargeClockView).setTextSize(eq(TypedValue.COMPLEX_UNIT_PX), anyFloat())
verify(mockLargeClockView).setLayoutParams(any())
}
@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())
}
@Test
fun defaultClock_events_onLocaleChanged() {
val clock = provider.createClock(DEFAULT_CLOCK_ID)
clock.events.onLocaleChanged(Locale.getDefault())
verify(mockSmallClockView, times(2)).setLineSpacingScale(anyFloat())
verify(mockLargeClockView, times(2)).setLineSpacingScale(anyFloat())
verify(mockSmallClockView, times(2)).refreshFormat()
verify(mockLargeClockView, times(2)).refreshFormat()
}
}

View File

@@ -333,10 +333,9 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
}
@Test
public void disable_isDozingButNoCustomClock_clockAndSystemInfoVisible() {
public void disable_isDozing_clockAndSystemInfoVisible() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
when(mStatusBarStateController.isDozing()).thenReturn(true);
when(mNotificationPanelViewController.hasCustomClock()).thenReturn(false);
fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
@@ -345,10 +344,9 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
}
@Test
public void disable_customClockButNotDozing_clockAndSystemInfoVisible() {
public void disable_NotDozing_clockAndSystemInfoVisible() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
when(mStatusBarStateController.isDozing()).thenReturn(false);
when(mNotificationPanelViewController.hasCustomClock()).thenReturn(true);
fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
@@ -356,40 +354,6 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
assertEquals(View.VISIBLE, getClockView().getVisibility());
}
@Test
public void disable_dozingAndCustomClock_clockAndSystemInfoHidden() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
when(mStatusBarStateController.isDozing()).thenReturn(true);
when(mNotificationPanelViewController.hasCustomClock()).thenReturn(true);
// Make sure they start out as visible
assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
assertEquals(View.VISIBLE, getClockView().getVisibility());
fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
assertEquals(View.GONE, getClockView().getVisibility());
}
@Test
public void onDozingChanged_clockAndSystemInfoVisibilitiesUpdated() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
when(mStatusBarStateController.isDozing()).thenReturn(true);
when(mNotificationPanelViewController.hasCustomClock()).thenReturn(true);
// Make sure they start out as visible
assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
assertEquals(View.VISIBLE, getClockView().getVisibility());
fragment.onDozingChanged(true);
// When this callback is triggered, we want to make sure the clock and system info
// visibilities are recalculated. Since dozing=true, they shouldn't be visible.
assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
assertEquals(View.GONE, getClockView().getVisibility());
}
@Test
public void disable_headsUpShouldBeVisibleTrue_clockDisabled() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();