Bug: 130223602 Test: visual - AOD to lock screen while playing music Change-Id: Id03f93854bdb98a83664dc651cf1f30618f09176
517 lines
18 KiB
Java
517 lines
18 KiB
Java
package com.android.keyguard;
|
|
|
|
import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
|
|
|
|
import android.animation.Animator;
|
|
import android.animation.AnimatorSet;
|
|
import android.animation.ValueAnimator;
|
|
import android.app.WallpaperManager;
|
|
import android.content.Context;
|
|
import android.graphics.Paint;
|
|
import android.graphics.Paint.Style;
|
|
import android.transition.ChangeBounds;
|
|
import android.transition.Transition;
|
|
import android.transition.TransitionListenerAdapter;
|
|
import android.transition.TransitionManager;
|
|
import android.transition.TransitionValues;
|
|
import android.util.AttributeSet;
|
|
import android.util.MathUtils;
|
|
import android.util.TypedValue;
|
|
import android.view.View;
|
|
import android.view.ViewGroup;
|
|
import android.widget.FrameLayout;
|
|
import android.widget.RelativeLayout;
|
|
import android.widget.TextClock;
|
|
|
|
import androidx.annotation.VisibleForTesting;
|
|
|
|
import com.android.internal.colorextraction.ColorExtractor;
|
|
import com.android.internal.colorextraction.ColorExtractor.OnColorsChangedListener;
|
|
import com.android.keyguard.clock.ClockManager;
|
|
import com.android.systemui.Interpolators;
|
|
import com.android.systemui.colorextraction.SysuiColorExtractor;
|
|
import com.android.systemui.plugins.ClockPlugin;
|
|
import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
|
import com.android.systemui.statusbar.StatusBarState;
|
|
|
|
import java.io.FileDescriptor;
|
|
import java.io.PrintWriter;
|
|
import java.util.Arrays;
|
|
import java.util.TimeZone;
|
|
|
|
import javax.inject.Inject;
|
|
import javax.inject.Named;
|
|
|
|
/**
|
|
* Switch to show plugin clock when plugin is connected, otherwise it will show default clock.
|
|
*/
|
|
public class KeyguardClockSwitch extends RelativeLayout {
|
|
|
|
/**
|
|
* Controller used to track StatusBar state to know when to show the big_clock_container.
|
|
*/
|
|
private final StatusBarStateController mStatusBarStateController;
|
|
|
|
/**
|
|
* Color extractor used to apply colors from wallpaper to custom clock faces.
|
|
*/
|
|
private final SysuiColorExtractor mSysuiColorExtractor;
|
|
|
|
/**
|
|
* Manager used to know when to show a custom clock face.
|
|
*/
|
|
private final ClockManager mClockManager;
|
|
|
|
/**
|
|
* Layout transition that scales the default clock face.
|
|
*/
|
|
private final Transition mTransition;
|
|
|
|
/**
|
|
* Listener for layout transitions.
|
|
*/
|
|
private final Transition.TransitionListener mTransitionListener;
|
|
|
|
/**
|
|
* Optional/alternative clock injected via plugin.
|
|
*/
|
|
private ClockPlugin mClockPlugin;
|
|
|
|
/**
|
|
* Default clock.
|
|
*/
|
|
private TextClock mClockView;
|
|
|
|
/**
|
|
* Default clock, bold version.
|
|
* Used to transition to bold when shrinking the default clock.
|
|
*/
|
|
private TextClock mClockViewBold;
|
|
|
|
/**
|
|
* Frame for default and custom clock.
|
|
*/
|
|
private FrameLayout mSmallClockFrame;
|
|
|
|
/**
|
|
* Container for big custom clock.
|
|
*/
|
|
private ViewGroup mBigClockContainer;
|
|
|
|
/**
|
|
* Status area (date and other stuff) shown below the clock. Plugin can decide whether or not to
|
|
* show it below the alternate clock.
|
|
*/
|
|
private View mKeyguardStatusArea;
|
|
|
|
/**
|
|
* Maintain state so that a newly connected plugin can be initialized.
|
|
*/
|
|
private float mDarkAmount;
|
|
|
|
/**
|
|
* If the Keyguard Slice has a header (big center-aligned text.)
|
|
*/
|
|
private boolean mShowingHeader;
|
|
private boolean mSupportsDarkText;
|
|
private int[] mColorPalette;
|
|
|
|
/**
|
|
* Track the state of the status bar to know when to hide the big_clock_container.
|
|
*/
|
|
private int mStatusBarState;
|
|
|
|
private final StatusBarStateController.StateListener mStateListener =
|
|
new StatusBarStateController.StateListener() {
|
|
@Override
|
|
public void onStateChanged(int newState) {
|
|
mStatusBarState = newState;
|
|
updateBigClockVisibility();
|
|
}
|
|
};
|
|
|
|
private ClockManager.ClockChangedListener mClockChangedListener = this::setClockPlugin;
|
|
|
|
/**
|
|
* Listener for changes to the color palette.
|
|
*
|
|
* The color palette changes when the wallpaper is changed.
|
|
*/
|
|
private final OnColorsChangedListener mColorsListener = (extractor, which) -> {
|
|
if ((which & WallpaperManager.FLAG_LOCK) != 0) {
|
|
updateColors();
|
|
}
|
|
};
|
|
|
|
@Inject
|
|
public KeyguardClockSwitch(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
|
|
StatusBarStateController statusBarStateController, SysuiColorExtractor colorExtractor,
|
|
ClockManager clockManager) {
|
|
super(context, attrs);
|
|
mStatusBarStateController = statusBarStateController;
|
|
mStatusBarState = mStatusBarStateController.getState();
|
|
mSysuiColorExtractor = colorExtractor;
|
|
mClockManager = clockManager;
|
|
mTransition = new ClockBoundsTransition();
|
|
mTransitionListener = new ClockBoundsTransitionListener();
|
|
}
|
|
|
|
/**
|
|
* 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();
|
|
mClockView = findViewById(R.id.default_clock_view);
|
|
mClockViewBold = findViewById(R.id.default_clock_view_bold);
|
|
mSmallClockFrame = findViewById(R.id.clock_view);
|
|
mKeyguardStatusArea = findViewById(R.id.keyguard_status_area);
|
|
}
|
|
|
|
@Override
|
|
protected void onAttachedToWindow() {
|
|
super.onAttachedToWindow();
|
|
mClockManager.addOnClockChangedListener(mClockChangedListener);
|
|
mStatusBarStateController.addCallback(mStateListener);
|
|
mSysuiColorExtractor.addOnColorsChangedListener(mColorsListener);
|
|
mTransition.addListener(mTransitionListener);
|
|
updateColors();
|
|
}
|
|
|
|
@Override
|
|
protected void onDetachedFromWindow() {
|
|
super.onDetachedFromWindow();
|
|
mClockManager.removeOnClockChangedListener(mClockChangedListener);
|
|
mStatusBarStateController.removeCallback(mStateListener);
|
|
mSysuiColorExtractor.removeOnColorsChangedListener(mColorsListener);
|
|
mTransition.removeListener(mTransitionListener);
|
|
setClockPlugin(null);
|
|
}
|
|
|
|
private void setClockPlugin(ClockPlugin plugin) {
|
|
// Disconnect from existing plugin.
|
|
if (mClockPlugin != null) {
|
|
View smallClockView = mClockPlugin.getView();
|
|
if (smallClockView != null && smallClockView.getParent() == mSmallClockFrame) {
|
|
mSmallClockFrame.removeView(smallClockView);
|
|
}
|
|
if (mBigClockContainer != null) {
|
|
mBigClockContainer.removeAllViews();
|
|
updateBigClockVisibility();
|
|
}
|
|
mClockPlugin.onDestroyView();
|
|
mClockPlugin = null;
|
|
}
|
|
if (plugin == null) {
|
|
mClockView.setVisibility(View.VISIBLE);
|
|
mClockViewBold.setVisibility(View.INVISIBLE);
|
|
mKeyguardStatusArea.setVisibility(View.VISIBLE);
|
|
return;
|
|
}
|
|
// Attach small and big clock views to hierarchy.
|
|
View smallClockView = plugin.getView();
|
|
if (smallClockView != null) {
|
|
mSmallClockFrame.addView(smallClockView, -1,
|
|
new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
|
|
ViewGroup.LayoutParams.WRAP_CONTENT));
|
|
mClockView.setVisibility(View.GONE);
|
|
mClockViewBold.setVisibility(View.GONE);
|
|
}
|
|
View bigClockView = plugin.getBigClockView();
|
|
if (bigClockView != null && mBigClockContainer != null) {
|
|
mBigClockContainer.addView(bigClockView);
|
|
updateBigClockVisibility();
|
|
}
|
|
// Hide default clock.
|
|
if (!plugin.shouldShowStatusArea()) {
|
|
mKeyguardStatusArea.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);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set container for big clock face appearing behind NSSL and KeyguardStatusView.
|
|
*/
|
|
public void setBigClockContainer(ViewGroup container) {
|
|
if (mClockPlugin != null && container != null) {
|
|
View bigClockView = mClockPlugin.getBigClockView();
|
|
if (bigClockView != null) {
|
|
container.addView(bigClockView);
|
|
}
|
|
}
|
|
mBigClockContainer = container;
|
|
updateBigClockVisibility();
|
|
}
|
|
|
|
/**
|
|
* It will also update plugin setStyle if plugin is connected.
|
|
*/
|
|
public void setStyle(Style style) {
|
|
mClockView.getPaint().setStyle(style);
|
|
mClockViewBold.getPaint().setStyle(style);
|
|
if (mClockPlugin != null) {
|
|
mClockPlugin.setStyle(style);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* It will also update plugin setTextColor if plugin is connected.
|
|
*/
|
|
public void setTextColor(int color) {
|
|
mClockView.setTextColor(color);
|
|
mClockViewBold.setTextColor(color);
|
|
if (mClockPlugin != null) {
|
|
mClockPlugin.setTextColor(color);
|
|
}
|
|
}
|
|
|
|
public void setShowCurrentUserTime(boolean showCurrentUserTime) {
|
|
mClockView.setShowCurrentUserTime(showCurrentUserTime);
|
|
mClockViewBold.setShowCurrentUserTime(showCurrentUserTime);
|
|
}
|
|
|
|
public void setTextSize(int unit, float size) {
|
|
mClockView.setTextSize(unit, size);
|
|
mClockViewBold.setTextSize(unit, size);
|
|
}
|
|
|
|
public void setFormat12Hour(CharSequence format) {
|
|
mClockView.setFormat12Hour(format);
|
|
mClockViewBold.setFormat12Hour(format);
|
|
}
|
|
|
|
public void setFormat24Hour(CharSequence format) {
|
|
mClockView.setFormat24Hour(format);
|
|
mClockViewBold.setFormat24Hour(format);
|
|
}
|
|
|
|
/**
|
|
* 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);
|
|
}
|
|
}
|
|
|
|
public Paint getPaint() {
|
|
return mClockView.getPaint();
|
|
}
|
|
|
|
public int getCurrentTextColor() {
|
|
return mClockView.getCurrentTextColor();
|
|
}
|
|
|
|
public float getTextSize() {
|
|
return mClockView.getTextSize();
|
|
}
|
|
|
|
/**
|
|
* Returns the preferred Y position of the clock.
|
|
*
|
|
* @param totalHeight Height of the parent container.
|
|
* @return preferred Y position.
|
|
*/
|
|
int getPreferredY(int totalHeight) {
|
|
if (mClockPlugin != null) {
|
|
return mClockPlugin.getPreferredY(totalHeight);
|
|
} else {
|
|
return totalHeight / 2;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Refresh the time of the clock, due to either time tick broadcast or doze time tick alarm.
|
|
*/
|
|
public void refresh() {
|
|
mClockView.refresh();
|
|
mClockViewBold.refresh();
|
|
if (mClockPlugin != null) {
|
|
mClockPlugin.onTimeTick();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Notifies that the time zone has changed.
|
|
*/
|
|
public void onTimeZoneChanged(TimeZone timeZone) {
|
|
if (mClockPlugin != null) {
|
|
mClockPlugin.onTimeZoneChanged(timeZone);
|
|
}
|
|
}
|
|
|
|
private void updateColors() {
|
|
ColorExtractor.GradientColors colors = mSysuiColorExtractor.getColors(
|
|
WallpaperManager.FLAG_LOCK, true);
|
|
mSupportsDarkText = colors.supportsDarkText();
|
|
mColorPalette = colors.getColorPalette();
|
|
if (mClockPlugin != null) {
|
|
mClockPlugin.setColorPalette(mSupportsDarkText, mColorPalette);
|
|
}
|
|
}
|
|
|
|
private void updateBigClockVisibility() {
|
|
if (mBigClockContainer == null) {
|
|
return;
|
|
}
|
|
final boolean inDisplayState = mStatusBarState == StatusBarState.KEYGUARD
|
|
|| mStatusBarState == StatusBarState.SHADE_LOCKED;
|
|
final int visibility =
|
|
inDisplayState && mBigClockContainer.getChildCount() != 0 ? View.VISIBLE
|
|
: View.GONE;
|
|
if (mBigClockContainer.getVisibility() != visibility) {
|
|
mBigClockContainer.setVisibility(visibility);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets if the keyguard slice is showing a center-aligned header. We need a smaller clock in
|
|
* these cases.
|
|
*/
|
|
public void setKeyguardShowingHeader(boolean hasHeader) {
|
|
if (mShowingHeader == hasHeader || hasCustomClock()) {
|
|
return;
|
|
}
|
|
mShowingHeader = hasHeader;
|
|
|
|
TransitionManager.beginDelayedTransition((ViewGroup) mClockView.getParent(), mTransition);
|
|
int fontSize = mContext.getResources().getDimensionPixelSize(mShowingHeader
|
|
? R.dimen.widget_small_font_size : R.dimen.widget_big_font_size);
|
|
int paddingBottom = mContext.getResources().getDimensionPixelSize(mShowingHeader
|
|
? R.dimen.widget_vertical_padding_clock : R.dimen.header_subtitle_padding);
|
|
mClockView.setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize);
|
|
mClockViewBold.setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize);
|
|
mClockView.setPadding(mClockView.getPaddingLeft(), mClockView.getPaddingTop(),
|
|
mClockView.getPaddingRight(), paddingBottom);
|
|
mClockViewBold.setPadding(mClockViewBold.getPaddingLeft(), mClockViewBold.getPaddingTop(),
|
|
mClockViewBold.getPaddingRight(), paddingBottom);
|
|
}
|
|
|
|
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
|
|
ClockManager.ClockChangedListener getClockChangedListener() {
|
|
return mClockChangedListener;
|
|
}
|
|
|
|
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
|
|
StatusBarStateController.StateListener getStateListener() {
|
|
return mStateListener;
|
|
}
|
|
|
|
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
|
|
pw.println("KeyguardClockSwitch:");
|
|
pw.println(" mClockPlugin: " + mClockPlugin);
|
|
pw.println(" mClockView: " + mClockView);
|
|
pw.println(" mClockViewBold: " + mClockViewBold);
|
|
pw.println(" mSmallClockFrame: " + mSmallClockFrame);
|
|
pw.println(" mBigClockContainer: " + mBigClockContainer);
|
|
pw.println(" mKeyguardStatusArea: " + mKeyguardStatusArea);
|
|
pw.println(" mDarkAmount: " + mDarkAmount);
|
|
pw.println(" mShowingHeader: " + mShowingHeader);
|
|
pw.println(" mSupportsDarkText: " + mSupportsDarkText);
|
|
pw.println(" mColorPalette: " + Arrays.toString(mColorPalette));
|
|
}
|
|
|
|
/**
|
|
* Special layout transition that scales the clock view as its bounds change, to make it look
|
|
* like the text is shrinking.
|
|
*/
|
|
private class ClockBoundsTransition extends ChangeBounds {
|
|
|
|
/**
|
|
* Animation fraction when text is transitioned to/from bold.
|
|
*/
|
|
private static final float TO_BOLD_TRANSITION_FRACTION = 0.7f;
|
|
|
|
ClockBoundsTransition() {
|
|
setDuration(KeyguardSliceView.DEFAULT_ANIM_DURATION / 2);
|
|
setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
|
|
}
|
|
|
|
@Override
|
|
public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
|
|
TransitionValues endValues) {
|
|
Animator animator = super.createAnimator(sceneRoot, startValues, endValues);
|
|
if (animator == null || startValues.view != mClockView) {
|
|
return animator;
|
|
}
|
|
|
|
ValueAnimator boundsAnimator = null;
|
|
if (animator instanceof AnimatorSet) {
|
|
Animator first = ((AnimatorSet) animator).getChildAnimations().get(0);
|
|
if (first instanceof ValueAnimator) {
|
|
boundsAnimator = (ValueAnimator) first;
|
|
}
|
|
} else if (animator instanceof ValueAnimator) {
|
|
boundsAnimator = (ValueAnimator) animator;
|
|
}
|
|
|
|
if (boundsAnimator != null) {
|
|
float bigFontSize = mContext.getResources()
|
|
.getDimensionPixelSize(R.dimen.widget_big_font_size);
|
|
float smallFontSize = mContext.getResources()
|
|
.getDimensionPixelSize(R.dimen.widget_small_font_size);
|
|
float startScale = mShowingHeader
|
|
? bigFontSize / smallFontSize : smallFontSize / bigFontSize;
|
|
final int normalViewVisibility = mShowingHeader ? View.INVISIBLE : View.VISIBLE;
|
|
final int boldViewVisibility = mShowingHeader ? View.VISIBLE : View.INVISIBLE;
|
|
final float boldTransitionFraction = mShowingHeader ? TO_BOLD_TRANSITION_FRACTION :
|
|
1f - TO_BOLD_TRANSITION_FRACTION;
|
|
boundsAnimator.addUpdateListener(animation -> {
|
|
final float fraction = animation.getAnimatedFraction();
|
|
if (fraction > boldTransitionFraction) {
|
|
mClockView.setVisibility(normalViewVisibility);
|
|
mClockViewBold.setVisibility(boldViewVisibility);
|
|
}
|
|
float scale = MathUtils.lerp(startScale, 1f /* stop */,
|
|
animation.getAnimatedFraction());
|
|
mClockView.setPivotX(mClockView.getWidth() / 2f);
|
|
mClockViewBold.setPivotX(mClockViewBold.getWidth() / 2f);
|
|
mClockView.setPivotY(0);
|
|
mClockViewBold.setPivotY(0);
|
|
mClockView.setScaleX(scale);
|
|
mClockViewBold.setScaleX(scale);
|
|
mClockView.setScaleY(scale);
|
|
mClockViewBold.setScaleY(scale);
|
|
});
|
|
}
|
|
|
|
return animator;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Transition listener for layout transition that scales the clock view.
|
|
*/
|
|
private class ClockBoundsTransitionListener extends TransitionListenerAdapter {
|
|
|
|
@Override
|
|
public void onTransitionEnd(Transition transition) {
|
|
mClockView.setVisibility(mShowingHeader ? View.INVISIBLE : View.VISIBLE);
|
|
mClockViewBold.setVisibility(mShowingHeader ? View.VISIBLE : View.INVISIBLE);
|
|
mClockView.setScaleX(1f);
|
|
mClockViewBold.setScaleX(1f);
|
|
mClockView.setScaleY(1f);
|
|
mClockViewBold.setScaleY(1f);
|
|
}
|
|
|
|
@Override
|
|
public void onTransitionCancel(Transition transition) {
|
|
onTransitionEnd(transition);
|
|
}
|
|
}
|
|
}
|