diff options
24 files changed, 705 insertions, 740 deletions
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt index ff64c7891128..d427a57f3b87 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt @@ -485,7 +485,13 @@ class TextInterpolator( val out = mutableListOf<List<PositionedGlyphs>>() for (lineNo in 0 until layout.lineCount) { // Shape all lines. val lineStart = layout.getLineStart(lineNo) - val count = layout.getLineEnd(lineNo) - lineStart + var count = layout.getLineEnd(lineNo) - lineStart + // Do not render the last character in the line if it's a newline and unprintable + val last = lineStart + count - 1 + if (last > lineStart && last < layout.text.length && layout.text[last] == '\n') { + count-- + } + val runs = mutableListOf<PositionedGlyphs>() TextShaper.shapeText(layout.text, lineStart, count, layout.textDirectionHeuristic, paint) { _, _, glyphs, _ -> diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt index 0c1916074e0c..cafdc8676173 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt @@ -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) + animations.doze(dozeFraction) + animations.fold(foldFraction) + events.onTimeTick() + } /** Optional method for dumping debug information */ fun dump(pw: PrintWriter) { } @@ -80,15 +88,12 @@ 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) { } } /** 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() { } diff --git a/packages/SystemUI/res-keyguard/font/clock.xml b/packages/SystemUI/res-keyguard/font/clock.xml deleted file mode 100644 index 0137dc39921f..000000000000 --- a/packages/SystemUI/res-keyguard/font/clock.xml +++ /dev/null @@ -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>
\ No newline at end of file diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml index 6a38507b2ad7..8b8ebf00e190 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml @@ -31,42 +31,14 @@ 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> <FrameLayout android:id="@+id/lockscreen_clock_view_large" android:layout_width="match_parent" - android:layout_height="wrap_content" + android:layout_height="match_parent" android:layout_below="@id/keyguard_slice_view" + android:paddingTop="@dimen/keyguard_large_clock_top_padding" 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> <!-- Not quite optimal but needed to translate these items as a group. The diff --git a/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml b/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml index 7a57293f58bd..5f4e310f975c 100644 --- a/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml +++ b/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml @@ -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:includeFontPadding="false" android:textColor="@android:color/white" android:format12Hour="@string/dream_time_complication_12_hr_time_format" diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 3fb00a33e6b6..205b11784634 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -664,13 +664,7 @@ <!-- When large clock is showing, offset the smartspace by this amount --> <dimen name="keyguard_smartspace_top_offset">12dp</dimen> <!-- 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="keyguard_large_clock_top_padding">100dp</dimen> <dimen name="notification_scrim_corner_radius">32dp</dimen> @@ -890,11 +884,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> diff --git a/packages/SystemUI/shared/res/layout/clock_default_large.xml b/packages/SystemUI/shared/res/layout/clock_default_large.xml index 8510a0a8b550..0139d50dcfba 100644 --- a/packages/SystemUI/shared/res/layout/clock_default_large.xml +++ b/packages/SystemUI/shared/res/layout/clock_default_large.xml @@ -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" diff --git a/packages/SystemUI/shared/res/layout/clock_default_small.xml b/packages/SystemUI/shared/res/layout/clock_default_small.xml index ec0e427e6a4d..390ff5e3ff78 100644 --- a/packages/SystemUI/shared/res/layout/clock_default_small.xml +++ b/packages/SystemUI/shared/res/layout/clock_default_small.xml @@ -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" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="start" @@ -26,6 +25,7 @@ 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" diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/AnimatableClockView.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/AnimatableClockView.kt index 2739d59dbf00..8f1959e884cf 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/AnimatableClockView.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/AnimatableClockView.kt @@ -20,12 +20,15 @@ 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.systemui.animation.GlyphCallback import com.android.systemui.animation.Interpolators import com.android.systemui.animation.TextAnimator @@ -75,6 +78,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 @@ -133,6 +142,15 @@ class AnimatableClockView @JvmOverloads constructor( // relayout if the text didn't actually change. if (!TextUtils.equals(text, formattedText)) { text = formattedText + + // 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() } } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/ClockRegistry.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/ClockRegistry.kt index a4c03b0b57c8..4b8b46d54848 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/ClockRegistry.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/ClockRegistry.kt @@ -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,12 +49,17 @@ 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() + } + private val gson = Gson() private val availableClocks = mutableMapOf<ClockId, ClockInfo>() 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> { @@ -73,7 +76,7 @@ open class ClockRegistry( context.contentResolver, Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE ) - return gson.fromJson(json, ClockSetting::class.java).clockId + return gson.fromJson(json, ClockSetting::class.java)?.clockId ?: DEFAULT_CLOCK_ID } set(value) { val json = gson.toJson(ClockSetting(value, System.currentTimeMillis())) @@ -100,8 +103,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 } @@ -110,7 +116,7 @@ open class ClockRegistry( if (DEBUG) { Log.i(TAG, "Current clock ($currentId) was connected") } - clockChangeListeners.forEach { it() } + clockChangeListeners.forEach { it.onClockChanged() } } } } @@ -122,7 +128,7 @@ open class ClockRegistry( if (currentId == clock.clockId) { Log.w(TAG, "Current clock ($currentId) was disconnected") - clockChangeListeners.forEach { it() } + clockChangeListeners.forEach { it.onClockChanged() } } } } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt index 5d8da5985768..1d8abe3fdf42 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt @@ -19,10 +19,9 @@ 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 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 @@ -102,10 +101,13 @@ 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) { + val color = resources.getColor(android.R.color.system_accent1_100) + clocks.forEach { it.setColors(DOZE_COLOR, color) } + } override fun onLocaleChanged(locale: Locale) { val nf = NumberFormat.getInstance(locale) @@ -119,8 +121,17 @@ class DefaultClock( } } - override val animation = object : ClockAnimation { - override fun initialize(dozeFraction: Float, foldFraction: Float) { + override var animations = DefaultClockAnimations(0f, 0f) + private set + + 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 +143,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 +157,6 @@ class DefaultClock( } } - private var dozeState = AnimationState(0f) override fun doze(fraction: Float) { val (hasChanged, hasJumped) = dozeState.update(fraction) if (hasChanged) { @@ -172,6 +181,19 @@ class DefaultClock( init { events.onLocaleChanged(Locale.getDefault()) + 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) + events.onTimeTick() + } + + private fun recomputePadding() { + val topPadding = -1 * (largeClock.bottom.toInt() - 180) + largeClock.setPadding(0, topPadding, 0, 0) } override fun dump(pw: PrintWriter) = clocks.forEach { it.dump(pw) } diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java index c69ff7ee1cd8..e0b11d83bf75 100644 --- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java +++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java @@ -182,18 +182,6 @@ public class AnimatableClockController extends ViewController<AnimatableClockVie 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; - } - /** Animate the clock appearance */ public void animateAppear() { if (!mIsDozing) mView.animateAppearOnLockscreen(); diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt new file mode 100644 index 000000000000..efd7bcf10cd2 --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt @@ -0,0 +1,153 @@ +/* + * 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 com.android.systemui.broadcast.BroadcastDispatcher +import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.plugins.Clock +import com.android.systemui.plugins.statusbar.StatusBarStateController +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 javax.inject.Inject + +/** + * Controller for a Clock provided by the registry and used on the keyguard. Instantiated by + * [KeyguardClockSwitchController]. Functionality is forked from [AnimatableClockController]. + */ +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 +) { + var clock: Clock? = null + set(value) { + field = value + if (value != null) { + value.initialize(resources, dozeAmount, 0f) + } + } + + private var isDozing = false + private set + + private var isCharging = false + private var dozeAmount = 0f + private var isKeyguardShowing = false + + private val configListener = object : ConfigurationController.ConfigurationListener { + override fun onThemeChanged() { + clock?.events?.onColorPaletteChanged(resources) + } + } + + 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) + } + + fun unregisterListeners() { + broadcastDispatcher.unregisterReceiver(localeBroadcastReceiver) + configurationController.removeCallback(configListener) + batteryController.removeCallback(batteryCallback) + keyguardUpdateMonitor.removeCallback(keyguardUpdateMonitorCallback) + statusBarStateController.removeCallback(statusBarStateListener) + } + + /** + * Dump information for debugging + */ + fun dump(pw: PrintWriter) { + pw.println(this) + clock?.dump(pw) + } + + companion object { + private val TAG = ClockEventController::class.simpleName + private const val FORMAT_NUMBER = 1234567890 + } +} diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java index 206b8bee323c..e1fabdef3651 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java @@ -5,10 +5,8 @@ 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; @@ -17,19 +15,14 @@ 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. */ @@ -50,17 +43,10 @@ public class KeyguardClockSwitch extends RelativeLayout { 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 +66,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; @@ -97,97 +77,38 @@ 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); - 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); - } - } + mSmallClockFrame.removeAllViews(); + mLargeClockFrame.removeAllViews(); - /** - * It will also update plugin setStyle if plugin is connected. - */ - public void setStyle(Style style) { - if (mClockPlugin != null) { - mClockPlugin.setStyle(style); + if (clock == null) { + Log.e(TAG, "No clock being shown"); + return; } - } - /** - * It will also update plugin setTextColor if plugin is connected. - */ - public void setTextColor(int color) { - if (mClockPlugin != null) { - mClockPlugin.setTextColor(color); - } + // Attach small and big clock views to hierarchy. + mSmallClockFrame.addView(clock.getSmallClock(), -1, + new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT)); + mLargeClockFrame.addView(clock.getLargeClock()); } private void updateClockViews(boolean useLargeClock, boolean animate) { @@ -203,14 +124,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 +190,6 @@ public class KeyguardClockSwitch extends RelativeLayout { } /** - * 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 * * @return true if desired clock appeared and false if it was already visible @@ -311,64 +220,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(" mClockFrame: " + 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); } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java index 6c32a4910c56..d566f49c04ff 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java @@ -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,34 +30,28 @@ 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.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; @@ -69,48 +61,24 @@ 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; + /** Clock frames for both small and large sizes */ + 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 @@ -119,9 +87,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(); @@ -142,34 +110,30 @@ 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) { 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; + + mClockChangedListener = () -> { + setClock(mClockRegistry.createCurrentClock()); + }; } /** @@ -186,40 +150,18 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS public 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); @@ -242,7 +184,6 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS ksv.setVisibility(View.GONE); addSmartspaceView(ksvIndex); - updateClockLayout(); } mSecureSettings.registerContentObserverForUser( @@ -264,11 +205,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); @@ -307,18 +246,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,44 +261,31 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS boolean appeared = mView.switchToClock(clockSize, animate); if (animate && appeared && clockSize == LARGE) { - mLargeClockViewController.animateAppear(); + getClock().getAnimations().enter(); } } - public void animateFoldToAod() { - if (mClockViewController != null) { - mClockViewController.animateFoldAppear(); - mLargeClockViewController.animateFoldAppear(); - } - } - - /** - * If we're presenting a custom clock of just the default one. - */ - public boolean hasCustomClock() { - return mView.hasCustomClock(); - } - /** - * Get the clock text size. + * Animates the clock view between folded and unfolded states */ - 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(); + } } /** @@ -383,7 +297,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); @@ -396,25 +310,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(); } } @@ -429,12 +357,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() { @@ -467,8 +396,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); + } } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java index cb3172dabdb1..83e23bd52f19 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java @@ -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); } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java index 014d08288158..c715a4eaef2b 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java @@ -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; /** @@ -96,13 +94,6 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV } /** - * 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. - */ - public void setPivotX(float pivot) { - mView.setPivotX(pivot); - } - - /** - * Set pivot y. - */ - public void setPivotY(float pivot) { - mView.setPivotY(pivot); - } - - /** - * Get the clock text size. + * Update the pivot position based on the parent view */ - public float getClockTextSize() { - return mKeyguardClockSwitchController.getClockTextSize(); + public void updatePivot(float parentWidth, float parentHeight) { + mView.setPivotX(parentWidth / 2f); + mView.setPivotY(mKeyguardClockSwitchController.getClockHeight() / 2f); } /** @@ -240,11 +212,6 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV } @Override - public void onTimeZoneChanged(TimeZone timeZone) { - mKeyguardClockSwitchController.updateTimeZone(timeZone); - } - - @Override public void onKeyguardVisibilityChanged(boolean showing) { if (showing) { if (DEBUG) Slog.v(TAG, "refresh statusview showing:" + showing); diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index 99d0fe97971a..24448bb0ed2e 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -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. @@ -1107,13 +1102,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; @@ -3881,9 +3869,10 @@ public final class NotificationPanelViewController extends PanelViewController { endAction.run(); } }) + .setUpdateListener(anim -> { + mKeyguardStatusViewController.animateFoldToAod(anim.getAnimatedFraction()); + }) .start(); - - mKeyguardStatusViewController.animateFoldToAod(); } /** @@ -4596,7 +4585,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(); } @@ -4706,11 +4694,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; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java index 0848729781bd..fb26600f8256 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java @@ -439,7 +439,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; @@ -449,13 +448,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 { diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt new file mode 100644 index 000000000000..4f3995252b54 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt @@ -0,0 +1,259 @@ +/* + * 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.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 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 + + 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 + ) + } + + @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 + clockEventController.registerListeners() + + val captor = argumentCaptor<ConfigurationController.ConfigurationListener>() + verify(configurationController).addCallback(capture(captor)) + captor.value.onThemeChanged() + + verify(events).onColorPaletteChanged(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.unregisterListeners() + verify(broadcastDispatcher).unregisterReceiver(any()) + verify(configurationController).removeCallback(any()) + verify(batteryController).removeCallback(any()) + verify(keyguardUpdateMonitor).removeCallback(any()) + verify(statusBarStateController).removeCallback(any()) + } +} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java index 8b9a1e022d26..cd2a43fc6a44 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java @@ -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,23 +40,18 @@ 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.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; @@ -79,22 +73,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 @@ -102,11 +86,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; @@ -136,8 +120,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()); @@ -148,23 +130,19 @@ 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 ); 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); @@ -211,20 +189,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 @@ -281,10 +259,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(); } } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java index 6c6f0acd7085..a0295d09826f 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java @@ -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; + @Mock + ViewGroup mMockKeyguardSliceView; + + @Mock + Clock mClock; + + private FrameLayout mSmallClockFrame; private FrameLayout mLargeClockFrame; - private TextClock mBigClock; - 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); } @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 noPluginConnected_showNothing() { + 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); - } - - @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); - // 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(); - } + public void pluginConnectedThenDisconnected_showNothing() { + mKeyguardClockSwitch.setClock(mClock, StatusBarState.KEYGUARD); + assertEquals(mLargeClockFrame.getChildCount(), 1); + assertEquals(mSmallClockFrame.getChildCount(), 1); - @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); + mKeyguardClockSwitch.setClock(null, StatusBarState.KEYGUARD); + assertEquals(mLargeClockFrame.getChildCount(), 0); + assertEquals(mSmallClockFrame.getChildCount(), 0); } @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); + public void onPluginConnected_showClock() { + mKeyguardClockSwitch.setClock(mClock, StatusBarState.KEYGUARD); - mKeyguardClockSwitch.setClockPlugin(null, StatusBarState.KEYGUARD); - assertThat(mClockView.getVisibility()).isEqualTo(VISIBLE); - - assertThat(plugin.getView().getParent()).isNull(); + assertEquals(mClock.getSmallClock().getParent(), mSmallClockFrame); + assertEquals(mClock.getLargeClock().getParent(), mLargeClockFrame); } @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(); - } + public void onPluginConnected_showSecondPluginClock() { + // GIVEN a plugin has already connected + Clock otherClock = mock(Clock.class); + when(otherClock.getSmallClock()).thenReturn(new TextView(getContext())); + when(otherClock.getLargeClock()).thenReturn(new TextView(getContext())); + mKeyguardClockSwitch.setClock(mClock, StatusBarState.KEYGUARD); + mKeyguardClockSwitch.setClock(otherClock, StatusBarState.KEYGUARD); - @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); + // THEN only the view from the second plugin should be a child of KeyguardClockSwitch. + assertThat(otherClock.getSmallClock().getParent()).isEqualTo(mSmallClockFrame); + assertThat(otherClock.getLargeClock().getParent()).isEqualTo(mLargeClockFrame); + assertThat(mClock.getSmallClock().getParent()).isNull(); + assertThat(mClock.getLargeClock().getParent()).isNull(); } @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); diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt index d61989fc3128..136a395196e1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt @@ -129,13 +129,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 @@ -157,11 +160,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) @@ -221,7 +227,7 @@ class ClockRegistryTest : SysuiTestCase() { pluginListener.onPluginConnected(plugin2, mockContext) var changeCallCount = 0 - registry.registerClockChangeListener({ changeCallCount++ }) + registry.registerClockChangeListener { changeCallCount++ } pluginListener.onPluginDisconnected(plugin1) assertEquals(0, changeCallCount) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java index 56804d58d241..25348f3bc686 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java @@ -230,10 +230,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); @@ -242,10 +241,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); @@ -254,40 +252,6 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @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(); when(mHeadsUpAppearanceController.shouldBeVisible()).thenReturn(true); |