Merge "[DO NOT MERGE]Revert "Migrate KeyguardClockSwitch to new shared Clock library"" into tm-qpr-dev

This commit is contained in:
TreeHugger Robot
2022-08-04 09:39:51 +00:00
committed by Android (Google) Code Review
24 changed files with 740 additions and 708 deletions

View File

@@ -485,13 +485,7 @@ class TextInterpolator(
val out = mutableListOf<List<PositionedGlyphs>>()
for (lineNo in 0 until layout.lineCount) { // Shape all lines.
val lineStart = layout.getLineStart(lineNo)
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 count = layout.getLineEnd(lineNo) - lineStart
val runs = mutableListOf<PositionedGlyphs>()
TextShaper.shapeText(layout.text, lineStart, count, layout.textDirectionHeuristic,
paint) { _, _, glyphs, _ ->

View File

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

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
**
** Copyright 2020, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License")
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<!--
** AOD/LockScreen Clock font.
** Should include all numeric glyphs in all supported locales.
** Recommended: font with variable width to support AOD => LS animations
-->
<!-- TODO: Remove when clock migration complete -->
<font-family xmlns:android="http://schemas.android.com/apk/res/android">
<font android:typeface="monospace"/>
</font-family>

View File

@@ -31,14 +31,42 @@
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="match_parent"
android:layout_height="wrap_content"
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

View File

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

View File

@@ -664,7 +664,13 @@
<!-- 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_padding">100dp</dimen>
<dimen name="keyguard_large_clock_top_margin">-60dp</dimen>
<!-- TODO: Remove during migration -->
<!-- Default line spacing multiplier between hours and minutes of the keyguard clock -->
<item name="keyguard_clock_line_spacing_scale" type="dimen" format="float">.7</item>
<!-- Burmese line spacing multiplier between hours and minutes of the keyguard clock -->
<item name="keyguard_clock_line_spacing_scale_burmese" type="dimen" format="float">1</item>
<dimen name="notification_scrim_corner_radius">32dp</dimen>
@@ -884,6 +890,11 @@
burn-in on AOD. -->
<dimen name="burn_in_prevention_offset_y_clock">42dp</dimen>
<!-- Clock maximum font size (dp is intentional, to prevent any further scaling) -->
<!-- TODO: Remove when clock migration complete -->
<dimen name="large_clock_text_size">150dp</dimen>
<dimen name="clock_text_size">86dp</dimen>
<!-- The maximum offset in either direction that icons move to prevent burn-in on AOD. -->
<dimen name="default_burn_in_prevention_offset">15dp</dimen>

View File

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

View File

@@ -18,6 +18,7 @@
-->
<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"
@@ -25,7 +26,6 @@
android:textSize="@dimen/small_clock_text_size"
android:fontFamily="@*android:string/config_clockFontFamily"
android:elegantTextHeight="false"
android:ellipsize="none"
android:singleLine="true"
android:fontFeatureSettings="pnum"
chargeAnimationDelay="350"

View File

@@ -20,15 +20,12 @@ 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
@@ -78,12 +75,6 @@ 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
@@ -142,15 +133,6 @@ 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()
}
}

View File

@@ -35,6 +35,8 @@ 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,
@@ -49,11 +51,6 @@ open class ClockRegistry(
defaultClockProvider: DefaultClockProvider
) : this(context, pluginManager, handler, defaultClockProvider as ClockProvider) { }
// Usually this would be a typealias, but a SAM provides better java interop
fun interface ClockChangeListener {
fun onClockChanged()
}
var isEnabled: Boolean = false
private val gson = Gson()
@@ -61,7 +58,7 @@ open class ClockRegistry(
private val clockChangeListeners = mutableListOf<ClockChangeListener>()
private val settingObserver = object : ContentObserver(handler) {
override fun onChange(selfChange: Boolean, uris: Collection<Uri>, flags: Int, userId: Int) =
clockChangeListeners.forEach { it.onClockChanged() }
clockChangeListeners.forEach { it() }
}
private val pluginListener = object : PluginListener<ClockProviderPlugin> {
@@ -120,11 +117,8 @@ 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
}
@@ -133,7 +127,7 @@ open class ClockRegistry(
if (DEBUG) {
Log.i(TAG, "Current clock ($currentId) was connected")
}
clockChangeListeners.forEach { it.onClockChanged() }
clockChangeListeners.forEach { it() }
}
}
}
@@ -145,7 +139,7 @@ open class ClockRegistry(
if (currentId == clock.clockId) {
Log.w(TAG, "Current clock ($currentId) was disconnected")
clockChangeListeners.forEach { it.onClockChanged() }
clockChangeListeners.forEach { it() }
}
}
}

View File

@@ -19,9 +19,10 @@ 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.ClockAnimations
import com.android.systemui.plugins.ClockAnimation
import com.android.systemui.plugins.ClockEvents
import com.android.systemui.plugins.ClockId
import com.android.systemui.plugins.ClockMetadata
@@ -101,13 +102,10 @@ class DefaultClock(
TypedValue.COMPLEX_UNIT_PX,
resources.getDimensionPixelSize(R.dimen.large_clock_text_size).toFloat()
)
recomputePadding()
}
override fun onColorPaletteChanged(resources: Resources) {
val color = resources.getColor(android.R.color.system_accent1_100)
clocks.forEach { it.setColors(DOZE_COLOR, color) }
}
override fun onColorPaletteChanged(palette: ColorExtractor.GradientColors) =
clocks.forEach { it.setColors(DOZE_COLOR, palette.mainColor) }
override fun onLocaleChanged(locale: Locale) {
val nf = NumberFormat.getInstance(locale)
@@ -121,17 +119,8 @@ class DefaultClock(
}
}
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 {
override val animation = object : ClockAnimation {
override fun initialize(dozeFraction: Float, foldFraction: Float) {
dozeState = AnimationState(dozeFraction)
foldState = AnimationState(foldFraction)
@@ -143,13 +132,14 @@ 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) {
@@ -157,6 +147,7 @@ class DefaultClock(
}
}
private var dozeState = AnimationState(0f)
override fun doze(fraction: Float) {
val (hasChanged, hasJumped) = dozeState.update(fraction)
if (hasChanged) {
@@ -181,19 +172,6 @@ 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) }

View File

@@ -182,6 +182,18 @@ 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();

View File

@@ -1,153 +0,0 @@
/*
* 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
}
}

View File

@@ -5,8 +5,10 @@ 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.Log;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
@@ -15,14 +17,19 @@ 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.Clock;
import com.android.systemui.plugins.ClockPlugin;
import com.android.systemui.shared.clocks.AnimatableClockView;
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.
*/
@@ -42,11 +49,18 @@ public class KeyguardClockSwitch extends RelativeLayout {
public static final int LARGE = 0;
public static final int SMALL = 1;
/**
* Optional/alternative clock injected via plugin.
*/
private ClockPlugin mClockPlugin;
/**
* Frame for small/large clocks
*/
private FrameLayout mSmallClockFrame;
private FrameLayout mClockFrame;
private FrameLayout mLargeClockFrame;
private AnimatableClockView mClockView;
private AnimatableClockView mLargeClockView;
private View mStatusArea;
private int mSmartspaceTopOffset;
@@ -66,6 +80,12 @@ 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;
@@ -77,38 +97,97 @@ 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();
mSmallClockFrame = findViewById(R.id.lockscreen_clock_view);
mClockFrame = findViewById(R.id.lockscreen_clock_view);
mClockView = findViewById(R.id.animatable_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 setClock(Clock clock, int statusBarState) {
void setClockPlugin(ClockPlugin plugin, int statusBarState) {
// Disconnect from existing plugin.
mSmallClockFrame.removeAllViews();
mLargeClockFrame.removeAllViews();
if (clock == null) {
Log.e(TAG, "No clock being shown");
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.
mSmallClockFrame.addView(clock.getSmallClock(), -1,
new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
mLargeClockFrame.addView(clock.getLargeClock());
View smallClockView = plugin.getView();
if (smallClockView != null) {
mClockFrame.addView(smallClockView, -1,
new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
mClockView.setVisibility(View.GONE);
}
View bigClockView = plugin.getBigClockView();
if (bigClockView != null) {
mLargeClockFrame.addView(bigClockView);
mLargeClockView.setVisibility(View.GONE);
}
// Initialize plugin parameters.
mClockPlugin = plugin;
mClockPlugin.setStyle(getPaint().getStyle());
mClockPlugin.setTextColor(getCurrentTextColor());
mClockPlugin.setDarkAmount(mDarkAmount);
if (mColorPalette != null) {
mClockPlugin.setColorPalette(mSupportsDarkText, mColorPalette);
}
}
/**
* It will also update plugin setStyle if plugin is connected.
*/
public void setStyle(Style style) {
if (mClockPlugin != null) {
mClockPlugin.setStyle(style);
}
}
/**
* It will also update plugin setTextColor if plugin is connected.
*/
public void setTextColor(int color) {
if (mClockPlugin != null) {
mClockPlugin.setTextColor(color);
}
}
private void updateClockViews(boolean useLargeClock, boolean animate) {
@@ -124,14 +203,14 @@ public class KeyguardClockSwitch extends RelativeLayout {
int direction = 1;
float statusAreaYTranslation;
if (useLargeClock) {
out = mSmallClockFrame;
out = mClockFrame;
in = mLargeClockFrame;
if (indexOfChild(in) == -1) addView(in);
direction = -1;
statusAreaYTranslation = mSmallClockFrame.getTop() - mStatusArea.getTop()
statusAreaYTranslation = mClockFrame.getTop() - mStatusArea.getTop()
+ mSmartspaceTopOffset;
} else {
in = mSmallClockFrame;
in = mClockFrame;
out = mLargeClockFrame;
statusAreaYTranslation = 0f;
@@ -189,6 +268,18 @@ public class KeyguardClockSwitch extends RelativeLayout {
mStatusAreaAnim.start();
}
/**
* Set the amount (ratio) that the device has transitioned to doze.
*
* @param darkAmount Amount of transition to doze: 1f for doze and 0f for awake.
*/
public void setDarkAmount(float darkAmount) {
mDarkAmount = darkAmount;
if (mClockPlugin != null) {
mClockPlugin.setDarkAmount(darkAmount);
}
}
/**
* Display the desired clock and hide the other one
*
@@ -220,11 +311,64 @@ 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(" mClockFrame: " + mSmallClockFrame);
pw.println(" mClockPlugin: " + mClockPlugin);
pw.println(" mClockFrame: " + mClockFrame);
pw.println(" mLargeClockFrame: " + mLargeClockFrame);
pw.println(" mStatusArea: " + mStatusArea);
pw.println(" mDarkAmount: " + mDarkAmount);
pw.println(" mSupportsDarkText: " + mSupportsDarkText);
pw.println(" mColorPalette: " + Arrays.toString(mColorPalette));
pw.println(" mDisplayedClockSize: " + mDisplayedClockSize);
}
}

View File

@@ -22,6 +22,8 @@ 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;
@@ -30,30 +32,36 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import androidx.annotation.NonNull;
import com.android.internal.colorextraction.ColorExtractor;
import com.android.keyguard.clock.ClockManager;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.plugins.Clock;
import com.android.systemui.plugins.ClockPlugin;
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;
@@ -63,24 +71,48 @@ 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 ClockRegistry mClockRegistry;
private final SysuiColorExtractor mColorExtractor;
private final ClockManager mClockManager;
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 frames for both small and large sizes */
private FrameLayout mSmallClockFrame; // top aligned clock
/**
* Clock for both small and large sizes
*/
private AnimatableClockController mClockViewController;
private FrameLayout mClockFrame; // top aligned clock
private AnimatableClockController mLargeClockViewController;
private FrameLayout mLargeClockFrame; // centered clock
@KeyguardClockSwitch.ClockSize
private int mCurrentClockSize = SMALL;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private int mKeyguardClockTopMargin = 0;
private final ClockRegistry.ClockChangeListener mClockChangedListener;
/**
* 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 ViewGroup mStatusArea;
// If set will replace keyguard_slice_view
@@ -89,9 +121,9 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
private boolean mOnlyClock = false;
private final Executor mUiExecutor;
private Executor mUiExecutor;
private boolean mCanShowDoubleLineClock = true;
private final ContentObserver mDoubleLineClockObserver = new ContentObserver(null) {
private ContentObserver mDoubleLineClockObserver = new ContentObserver(null) {
@Override
public void onChange(boolean change) {
updateDoubleLineClock();
@@ -112,32 +144,34 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
public KeyguardClockSwitchController(
KeyguardClockSwitch keyguardClockSwitch,
StatusBarStateController statusBarStateController,
ClockRegistry clockRegistry,
SysuiColorExtractor colorExtractor,
ClockManager clockManager,
KeyguardSliceViewController keyguardSliceViewController,
NotificationIconAreaController notificationIconAreaController,
BroadcastDispatcher broadcastDispatcher,
BatteryController batteryController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
LockscreenSmartspaceController smartspaceController,
KeyguardUnlockAnimationController keyguardUnlockAnimationController,
SecureSettings secureSettings,
@Main Executor uiExecutor,
DumpManager dumpManager,
ClockEventController clockEventController,
FeatureFlags featureFlags) {
@Main Resources resources,
DumpManager dumpManager) {
super(keyguardClockSwitch);
mStatusBarStateController = statusBarStateController;
mClockRegistry = clockRegistry;
mColorExtractor = colorExtractor;
mClockManager = clockManager;
mKeyguardSliceViewController = keyguardSliceViewController;
mNotificationIconAreaController = notificationIconAreaController;
mBroadcastDispatcher = broadcastDispatcher;
mBatteryController = batteryController;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mSmartspaceController = smartspaceController;
mResources = resources;
mSecureSettings = secureSettings;
mUiExecutor = uiExecutor;
mKeyguardUnlockAnimationController = keyguardUnlockAnimationController;
mDumpManager = dumpManager;
mClockEventController = clockEventController;
mClockRegistry.setEnabled(featureFlags.isEnabled(Flags.LOCKSCREEN_CUSTOM_CLOCKS));
mClockChangedListener = () -> {
setClock(mClockRegistry.createCurrentClock());
};
}
/**
@@ -154,18 +188,40 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
public void onInit() {
mKeyguardSliceViewController.init();
mSmallClockFrame = mView.findViewById(R.id.lockscreen_clock_view);
mClockFrame = 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() {
mClockRegistry.registerClockChangeListener(mClockChangedListener);
setClock(mClockRegistry.createCurrentClock());
mClockEventController.registerListeners();
if (CUSTOM_CLOCKS_ENABLED) {
mClockManager.addOnClockChangedListener(mClockChangedListener);
}
mColorExtractor.addOnColorsChangedListener(mColorsListener);
mView.updateColors(getGradientColors());
mKeyguardClockTopMargin =
mView.getResources().getDimensionPixelSize(R.dimen.keyguard_clock_top_margin);
@@ -188,6 +244,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
ksv.setVisibility(View.GONE);
addSmartspaceView(ksvIndex);
updateClockLayout();
}
mSecureSettings.registerContentObserverForUser(
@@ -209,9 +266,11 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
@Override
protected void onViewDetached() {
mClockRegistry.unregisterClockChangeListener(mClockChangedListener);
mClockEventController.unregisterListeners();
setClock(null);
if (CUSTOM_CLOCKS_ENABLED) {
mClockManager.removeOnClockChangedListener(mClockChangedListener);
}
mColorExtractor.removeOnColorsChangedListener(mColorsListener);
mView.setClockPlugin(null, mStatusBarStateController.getState());
mSecureSettings.unregisterContentObserver(mDoubleLineClockObserver);
@@ -250,6 +309,18 @@ 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);
}
/**
@@ -263,34 +334,46 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
mCurrentClockSize = clockSize;
Clock clock = getClock();
boolean appeared = mView.switchToClock(clockSize, animate);
if (clock != null && animate && appeared && clockSize == LARGE) {
clock.getAnimations().enter();
if (animate && appeared && clockSize == LARGE) {
mLargeClockViewController.animateAppear();
}
}
public void animateFoldToAod() {
if (mClockViewController != null) {
mClockViewController.animateFoldAppear();
mLargeClockViewController.animateFoldAppear();
}
}
/**
* Animates the clock view between folded and unfolded states
* If we're presenting a custom clock of just the default one.
*/
public void animateFoldToAod(float foldFraction) {
Clock clock = getClock();
if (clock != null) {
clock.getAnimations().fold(foldFraction);
}
public boolean hasCustomClock() {
return mView.hasCustomClock();
}
/**
* Get the clock text size.
*/
public float getClockTextSize() {
return mView.getTextSize();
}
/**
* Refresh clock. Called in response to TIME_TICK broadcasts.
*/
void refresh() {
if (mClockViewController != null) {
mClockViewController.refreshTime();
mLargeClockViewController.refreshTime();
}
if (mSmartspaceController != null) {
mSmartspaceController.requestSmartspaceUpdate();
}
Clock clock = getClock();
if (clock != null) {
clock.getEvents().onTimeTick();
}
mView.refresh();
}
/**
@@ -302,7 +385,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(mSmallClockFrame, AnimatableProperty.TRANSLATION_X,
PropertyAnimator.setProperty(mClockFrame, AnimatableProperty.TRANSLATION_X,
x, props, animate);
PropertyAnimator.setProperty(mLargeClockFrame, AnimatableProperty.SCALE_X,
scale, props, animate);
@@ -315,39 +398,25 @@ 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.getLargeClock().getHeight();
int clockHeight = clock.getHeight();
return frameHeight / 2 + clockHeight / 2;
} else {
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();
return mClockFrame.findViewById(
com.android.systemui.R.id.animatable_clock_view).getHeight()
+ statusBarHeaderHeight + mKeyguardClockTopMargin;
}
}
@@ -362,13 +431,12 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
mNotificationIconAreaController.setupAodIcons(nic);
}
private void setClock(Clock clock) {
mClockEventController.setClock(clock);
mView.setClock(clock, mStatusBarStateController.getState());
private void setClockPlugin(ClockPlugin plugin) {
mView.setClockPlugin(plugin, mStatusBarStateController.getState());
}
private Clock getClock() {
return mClockEventController.getClock();
private ColorExtractor.GradientColors getGradientColors() {
return mColorExtractor.getColors(WallpaperManager.FLAG_LOCK);
}
private int getCurrentLayoutDirection() {
@@ -401,10 +469,8 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
pw.println("currentClockSizeLarge=" + (mCurrentClockSize == LARGE));
pw.println("mCanShowDoubleLineClock=" + mCanShowDoubleLineClock);
Clock clock = getClock();
if (clock != null) {
clock.dump(pw);
}
mClockViewController.dump(pw);
mLargeClockViewController.dump(pw);
}
}

View File

@@ -17,11 +17,14 @@
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;
@@ -43,6 +46,7 @@ public class KeyguardStatusView extends GridLayout {
private View mMediaHostContainer;
private float mDarkAmount = 0;
private int mTextColor;
public KeyguardStatusView(Context context) {
this(context, null, 0);
@@ -67,6 +71,7 @@ public class KeyguardStatusView extends GridLayout {
}
mKeyguardSlice = findViewById(R.id.keyguard_slice_view);
mTextColor = mClockView.getCurrentTextColor();
mMediaHostContainer = findViewById(R.id.status_view_media_container);
@@ -78,12 +83,15 @@ 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. */
@@ -105,6 +113,7 @@ public class KeyguardStatusView extends GridLayout {
public void dump(PrintWriter pw, String[] args) {
pw.println("KeyguardStatusView:");
pw.println(" mDarkAmount: " + mDarkAmount);
pw.println(" mTextColor: " + Integer.toHexString(mTextColor));
if (mClockView != null) {
mClockView.dump(pw, args);
}

View File

@@ -30,6 +30,8 @@ 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;
/**
@@ -93,6 +95,13 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
mKeyguardSliceViewController.refresh();
}
/**
* The amount we're in doze.
*/
public void setDarkAmount(float darkAmount) {
mView.setDarkAmount(darkAmount);
}
/**
* Set which clock should be displayed on the keyguard. The other one will be automatically
* hidden.
@@ -105,10 +114,16 @@ 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(float foldFraction) {
mKeyguardClockSwitchController.animateFoldToAod(foldFraction);
public void animateFoldToAod() {
mKeyguardClockSwitchController.animateFoldToAod();
}
/**
* If we're presenting a custom clock of just the default one.
*/
public boolean hasCustomClock() {
return mKeyguardClockSwitchController.hasCustomClock();
}
/**
@@ -128,11 +143,24 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
}
/**
* Update the pivot position based on the parent view
* Set pivot x.
*/
public void updatePivot(float parentWidth, float parentHeight) {
mView.setPivotX(parentWidth / 2f);
mView.setPivotY(mKeyguardClockSwitchController.getClockHeight() / 2f);
public void setPivotX(float pivot) {
mView.setPivotX(pivot);
}
/**
* Set pivot y.
*/
public void setPivotY(float pivot) {
mView.setPivotY(pivot);
}
/**
* Get the clock text size.
*/
public float getClockTextSize() {
return mKeyguardClockSwitchController.getClockTextSize();
}
/**
@@ -211,6 +239,11 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
refreshTime();
}
@Override
public void onTimeZoneChanged(TimeZone timeZone) {
mKeyguardClockSwitchController.updateTimeZone(timeZone);
}
@Override
public void onKeyguardVisibilityChanged(boolean showing) {
if (showing) {

View File

@@ -292,6 +292,11 @@ 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.
@@ -1117,6 +1122,13 @@ 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;
@@ -3910,9 +3922,9 @@ public final class NotificationPanelViewController extends PanelViewController {
public void onAnimationEnd(Animator animation) {
endAction.run();
}
}).setUpdateListener(anim -> {
mKeyguardStatusViewController.animateFoldToAod(anim.getAnimatedFraction());
}).start();
mKeyguardStatusViewController.animateFoldToAod();
}
/**
@@ -4625,6 +4637,7 @@ public final class NotificationPanelViewController extends PanelViewController {
public void onDozeAmountChanged(float linearAmount, float amount) {
mInterpolatedDarkAmount = amount;
mLinearDarkAmount = linearAmount;
mKeyguardStatusViewController.setDarkAmount(mInterpolatedDarkAmount);
mKeyguardBottomArea.setDarkAmount(mInterpolatedDarkAmount);
positionClockAndNotifications();
}
@@ -4734,8 +4747,11 @@ public final class NotificationPanelViewController extends PanelViewController {
updateMaxDisplayedNotifications(!shouldAvoidChangingNotificationsCount());
setIsFullWidth(mNotificationStackScrollLayoutController.getWidth() == mView.getWidth());
// Update Clock Pivot (used by anti-burnin transformations)
mKeyguardStatusViewController.updatePivot(mView.getWidth(), mView.getHeight());
// Update Clock Pivot
mKeyguardStatusViewController.setPivotX(((float) mView.getWidth()) / 2f);
mKeyguardStatusViewController.setPivotY(
(FONT_HEIGHT - CAP_HEIGHT) / 2048f
* mKeyguardStatusViewController.getClockTextSize());
// Calculate quick setting heights.
int oldMaxHeight = mQsMaxExpansionHeight;

View File

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

View File

@@ -1,259 +0,0 @@
/*
* 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())
}
}

View File

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

View File

@@ -16,6 +16,7 @@
package com.android.keyguard;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
import static com.android.keyguard.KeyguardClockSwitch.LARGE;
@@ -23,61 +24,56 @@ 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.TextView;
import android.widget.TextClock;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.Clock;
import com.android.systemui.plugins.ClockPlugin;
import com.android.systemui.shared.clocks.AnimatableClockView;
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_clock_switch layout is inflated.
// the keyguard_clcok_switch layout is inflated.
@RunWithLooper(setAsMainLooper = true)
public class KeyguardClockSwitchTest extends SysuiTestCase {
@Mock
ViewGroup mMockKeyguardSliceView;
@Mock
Clock mClock;
private FrameLayout mSmallClockFrame;
private FrameLayout mClockFrame;
private FrameLayout mLargeClockFrame;
private TextClock mBigClock;
private AnimatableClockView mClockView;
private AnimatableClockView mLargeClockView;
View mMockKeyguardSliceView;
KeyguardClockSwitch mKeyguardClockSwitch;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mMockKeyguardSliceView = mock(KeyguardSliceView.class);
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() {
@@ -97,68 +93,164 @@ public class KeyguardClockSwitchTest extends SysuiTestCase {
});
mKeyguardClockSwitch =
(KeyguardClockSwitch) layoutInflater.inflate(R.layout.keyguard_clock_switch, null);
mSmallClockFrame = mKeyguardClockSwitch.findViewById(R.id.lockscreen_clock_view);
mClockFrame = mKeyguardClockSwitch.findViewById(R.id.lockscreen_clock_view);
mClockView = mKeyguardClockSwitch.findViewById(R.id.animatable_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 noPluginConnected_showNothing() {
mKeyguardClockSwitch.setClock(null, StatusBarState.KEYGUARD);
assertEquals(mLargeClockFrame.getChildCount(), 0);
assertEquals(mSmallClockFrame.getChildCount(), 0);
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 pluginConnectedThenDisconnected_showNothing() {
mKeyguardClockSwitch.setClock(mClock, StatusBarState.KEYGUARD);
assertEquals(mLargeClockFrame.getChildCount(), 1);
assertEquals(mSmallClockFrame.getChildCount(), 1);
mKeyguardClockSwitch.setClock(null, StatusBarState.KEYGUARD);
assertEquals(mLargeClockFrame.getChildCount(), 0);
assertEquals(mSmallClockFrame.getChildCount(), 0);
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);
}
@Test
public void onPluginConnected_showClock() {
mKeyguardClockSwitch.setClock(mClock, StatusBarState.KEYGUARD);
assertEquals(mClock.getSmallClock().getParent(), mSmallClockFrame);
assertEquals(mClock.getLargeClock().getParent(), mLargeClockFrame);
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
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);
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(otherClock.getSmallClock().getParent()).isEqualTo(mSmallClockFrame);
assertThat(otherClock.getLargeClock().getParent()).isEqualTo(mLargeClockFrame);
assertThat(mClock.getSmallClock().getParent()).isNull();
assertThat(mClock.getLargeClock().getParent()).isNull();
assertThat(plugin2.getView().getParent()).isEqualTo(mClockFrame);
assertThat(plugin1.getView().getParent()).isNull();
}
@Test
public void onPluginConnected_darkAmountInitialized() {
// GIVEN that the dark amount has already been set
mKeyguardClockSwitch.setDarkAmount(0.5f);
// WHEN a plugin is connected
ClockPlugin plugin = mock(ClockPlugin.class);
mKeyguardClockSwitch.setClockPlugin(plugin, StatusBarState.KEYGUARD);
// THEN dark amount should be initalized on the plugin.
verify(plugin).setDarkAmount(0.5f);
}
@Test
public void onPluginDisconnected_showDefaultClock() {
ClockPlugin plugin = mock(ClockPlugin.class);
TextClock pluginView = new TextClock(getContext());
when(plugin.getView()).thenReturn(pluginView);
mKeyguardClockSwitch.setClockPlugin(plugin, StatusBarState.KEYGUARD);
assertThat(mClockView.getVisibility()).isEqualTo(GONE);
mKeyguardClockSwitch.setClockPlugin(null, StatusBarState.KEYGUARD);
assertThat(mClockView.getVisibility()).isEqualTo(VISIBLE);
assertThat(plugin.getView().getParent()).isNull();
}
@Test
public void onPluginDisconnected_hidePluginBigClock() {
// GIVEN the plugin returns a view for the big clock
ClockPlugin plugin = mock(ClockPlugin.class);
TextClock pluginView = new TextClock(getContext());
when(plugin.getBigClockView()).thenReturn(pluginView);
// WHEN the plugin is connected and then disconnected
mKeyguardClockSwitch.setClockPlugin(plugin, StatusBarState.KEYGUARD);
mKeyguardClockSwitch.setClockPlugin(null, StatusBarState.KEYGUARD);
// THEN the big lock container is GONE and the big clock view doesn't have
// a parent.
assertThat(mLargeClockView.getVisibility()).isEqualTo(VISIBLE);
assertThat(pluginView.getParent()).isNull();
}
@Test
public void onPluginDisconnected_nullView() {
ClockPlugin plugin = mock(ClockPlugin.class);
mKeyguardClockSwitch.setClockPlugin(plugin, StatusBarState.KEYGUARD);
mKeyguardClockSwitch.setClockPlugin(null, StatusBarState.KEYGUARD);
assertThat(mClockView.getVisibility()).isEqualTo(VISIBLE);
}
@Test
public void onPluginDisconnected_secondOfTwoDisconnected() {
// GIVEN two plugins are connected
Clock otherClock = mock(Clock.class);
when(otherClock.getSmallClock()).thenReturn(new TextView(getContext()));
when(otherClock.getLargeClock()).thenReturn(new TextView(getContext()));
mKeyguardClockSwitch.setClock(otherClock, StatusBarState.KEYGUARD);
mKeyguardClockSwitch.setClock(mClock, StatusBarState.KEYGUARD);
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);
// WHEN the second plugin is disconnected
mKeyguardClockSwitch.setClock(null, StatusBarState.KEYGUARD);
// THEN nothing should be shown
assertThat(otherClock.getSmallClock().getParent()).isNull();
assertThat(otherClock.getLargeClock().getParent()).isNull();
assertThat(mClock.getSmallClock().getParent()).isNull();
assertThat(mClock.getLargeClock().getParent()).isNull();
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);
}
@Test
@@ -170,7 +262,7 @@ public class KeyguardClockSwitchTest extends SysuiTestCase {
assertThat(mLargeClockFrame.getAlpha()).isEqualTo(1);
assertThat(mLargeClockFrame.getVisibility()).isEqualTo(VISIBLE);
assertThat(mSmallClockFrame.getAlpha()).isEqualTo(0);
assertThat(mClockFrame.getAlpha()).isEqualTo(0);
}
@Test
@@ -179,7 +271,7 @@ public class KeyguardClockSwitchTest extends SysuiTestCase {
assertThat(mLargeClockFrame.getAlpha()).isEqualTo(1);
assertThat(mLargeClockFrame.getVisibility()).isEqualTo(VISIBLE);
assertThat(mSmallClockFrame.getAlpha()).isEqualTo(0);
assertThat(mClockFrame.getAlpha()).isEqualTo(0);
}
@Test
@@ -189,8 +281,8 @@ public class KeyguardClockSwitchTest extends SysuiTestCase {
mKeyguardClockSwitch.mClockInAnim.end();
mKeyguardClockSwitch.mClockOutAnim.end();
assertThat(mSmallClockFrame.getAlpha()).isEqualTo(1);
assertThat(mSmallClockFrame.getVisibility()).isEqualTo(VISIBLE);
assertThat(mClockFrame.getAlpha()).isEqualTo(1);
assertThat(mClockFrame.getVisibility()).isEqualTo(VISIBLE);
// only big clock is removed at switch
assertThat(mLargeClockFrame.getParent()).isNull();
assertThat(mLargeClockFrame.getAlpha()).isEqualTo(0);
@@ -200,8 +292,8 @@ public class KeyguardClockSwitchTest extends SysuiTestCase {
public void switchingToSmallClockNoAnimation_makesBigClockDisappear() {
mKeyguardClockSwitch.switchToClock(SMALL, false);
assertThat(mSmallClockFrame.getAlpha()).isEqualTo(1);
assertThat(mSmallClockFrame.getVisibility()).isEqualTo(VISIBLE);
assertThat(mClockFrame.getAlpha()).isEqualTo(1);
assertThat(mClockFrame.getVisibility()).isEqualTo(VISIBLE);
// only big clock is removed at switch
assertThat(mLargeClockFrame.getParent()).isNull();
assertThat(mLargeClockFrame.getAlpha()).isEqualTo(0);

View File

@@ -130,16 +130,13 @@ 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
@@ -161,14 +158,11 @@ 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)
@@ -228,7 +222,7 @@ class ClockRegistryTest : SysuiTestCase() {
pluginListener.onPluginConnected(plugin2, mockContext)
var changeCallCount = 0
registry.registerClockChangeListener { changeCallCount++ }
registry.registerClockChangeListener({ changeCallCount++ })
pluginListener.onPluginDisconnected(plugin1)
assertEquals(0, changeCallCount)

View File

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