Merge "AOD clock and notification animation" into pi-dev
am: 22db17a97a
Change-Id: I4060191b2adc0ee0b9cd6aeee96a90ac09243c6b
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 45814ea..7ecd9ab 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -207,6 +207,9 @@
<!-- The bottom padding for the notification header -->
<dimen name="notification_header_padding_bottom">16dp</dimen>
+ <!-- The margin at the top of the notification header when dozing. -->
+ <dimen name="notification_header_margin_top_ambient">3dp</dimen>
+
<!-- The margin at the bottom of the notification header. -->
<dimen name="notification_header_margin_bottom">0dp</dimen>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index 2d97db0..67b3c92 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -1309,6 +1309,7 @@
</style>
<style name="Notification.Header.Ambient">
+ <item name="layout_marginTop">@dimen/notification_header_margin_top_ambient</item>
<item name="gravity">top|center_horizontal</item>
</style>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_presentation.xml b/packages/SystemUI/res-keyguard/layout/keyguard_presentation.xml
index 6dea493..3193101 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_presentation.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_presentation.xml
@@ -46,8 +46,7 @@
style="@style/widget_big_thin"
android:format12Hour="@string/keyguard_widget_12_hours_format"
android:format24Hour="@string/keyguard_widget_24_hours_format"
- android:baselineAligned="true"
- android:layout_marginBottom="@dimen/bottom_text_spacing_digital" />
+ android:baselineAligned="true" />
<include layout="@layout/keyguard_status_area" />
<ImageView
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml b/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml
index 611aa43..db03ed2 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml
@@ -28,12 +28,14 @@
android:clipToPadding="false"
android:orientation="vertical"
android:layout_centerHorizontal="true">
- <TextView android:id="@+id/title"
+ <com.android.systemui.statusbar.AlphaOptimizedTextView
+ android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginBottom="7dp"
+ android:layout_marginBottom="@dimen/widget_title_bottom_margin"
android:paddingStart="64dp"
android:paddingEnd="64dp"
+ android:visibility="gone"
android:textColor="?attr/wallpaperTextColor"
android:theme="@style/TextAppearance.Keyguard"
/>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
index fa14d1b..ba05ccf 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
@@ -67,21 +67,17 @@
android:singleLine="true"
style="@style/widget_big_thin"
android:format12Hour="@string/keyguard_widget_12_hours_format"
- android:format24Hour="@string/keyguard_widget_24_hours_format"
- android:layout_marginBottom="@dimen/bottom_text_spacing_digital" />
+ android:format24Hour="@string/keyguard_widget_24_hours_format" />
<View
android:id="@+id/clock_separator"
android:layout_width="@dimen/widget_separator_width"
android:layout_height="@dimen/widget_separator_thickness"
- android:layout_marginTop="22dp"
android:layout_below="@id/clock_view"
android:background="#f00"
- android:backgroundTint="?attr/wallpaperTextColor"
android:layout_centerHorizontal="true" />
<include layout="@layout/keyguard_status_area"
android:id="@+id/keyguard_status_area"
- android:layout_marginTop="20dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/clock_separator" />
diff --git a/packages/SystemUI/res-keyguard/values-sw600dp/dimens.xml b/packages/SystemUI/res-keyguard/values-sw600dp/dimens.xml
index 4ae7cb9..fdca44d 100644
--- a/packages/SystemUI/res-keyguard/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values-sw600dp/dimens.xml
@@ -28,7 +28,6 @@
<!-- Overload default clock widget parameters -->
<dimen name="widget_big_font_size">100dp</dimen>
<dimen name="widget_label_font_size">16sp</dimen>
- <dimen name="bottom_text_spacing_digital">-1dp</dimen>
<!-- EmergencyCarrierArea overlap - amount to overlap the emergency button and carrier text.
Should be 0 on devices with plenty of room (e.g. tablets) -->
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index 37de433..712b6e5 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -41,14 +41,14 @@
Should be 0 on devices with plenty of room (e.g. tablets) -->
<dimen name="eca_overlap">-10dip</dimen>
- <!-- Default clock parameters -->
- <dimen name="bottom_text_spacing_digital">-24dp</dimen>
<!-- Slice header -->
- <dimen name="widget_title_font_size">24sp</dimen>
- <!-- Slice subtitle -->
- <dimen name="widget_label_font_size">16sp</dimen>
+ <dimen name="widget_title_font_size">24dp</dimen>
+ <dimen name="widget_title_bottom_margin">7dp</dimen>
+ <dimen name="bottom_text_spacing_digital">0dp</dimen>
+ <!-- Slice subtitle -->
+ <dimen name="widget_label_font_size">16dp</dimen>
<!-- Slice offset when pulsing -->
- <dimen name="widget_pulsing_bottom_padding">24dp</dimen>
+ <dimen name="widget_pulsing_bottom_padding">48dp</dimen>
<!-- Clock without header -->
<dimen name="widget_big_font_size">64dp</dimen>
<!-- Clock with header -->
diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml
index 5f52e2a..b90b4dd 100644
--- a/packages/SystemUI/res-keyguard/values/styles.xml
+++ b/packages/SystemUI/res-keyguard/values/styles.xml
@@ -53,6 +53,7 @@
</style>
<style name="widget_big_thin">
<item name="android:textSize">@dimen/widget_big_font_size</item>
+ <item name="android:paddingBottom">@dimen/bottom_text_spacing_digital</item>
<item name="android:fontFamily">@*android:string/config_headlineFontFamilyLight</item>
<item name="android:fontFeatureSettings">@*android:string/config_headlineFontFeatureSettings</item>
</style>
@@ -89,6 +90,7 @@
<style name="TextAppearance.Keyguard.Secondary">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
+ <item name="android:lines">1</item>
<item name="android:textSize">@dimen/widget_label_font_size</item>
</style>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index ac9fb2b..438d2f5 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -140,6 +140,9 @@
notification panel collapses -->
<dimen name="shelf_appear_translation">42dp</dimen>
+ <!-- Vertical translation of pulsing notification animations -->
+ <dimen name="pulsing_notification_appear_translation">10dp</dimen>
+
<!-- The amount the content shifts upwards when transforming into the icon -->
<dimen name="notification_icon_transform_content_shift">32dp</dimen>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
index 9a26b35..c31cea2 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
@@ -34,6 +34,7 @@
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
+import android.view.ViewGroup;
import android.view.animation.Animation;
import android.widget.Button;
import android.widget.LinearLayout;
@@ -46,8 +47,9 @@
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.keyguard.KeyguardSliceProvider;
+import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.tuner.TunerService;
-import com.android.systemui.util.wakelock.WakeLock;
+import com.android.systemui.util.wakelock.KeepAwakeAnimationListener;
import java.util.HashMap;
import java.util.List;
@@ -64,21 +66,29 @@
* View visible under the clock on the lock screen and AoD.
*/
public class KeyguardSliceView extends LinearLayout implements View.OnClickListener,
- Observer<Slice>, TunerService.Tunable {
+ Observer<Slice>, TunerService.Tunable, ConfigurationController.ConfigurationListener {
private static final String TAG = "KeyguardSliceView";
+ public static final int DEFAULT_ANIM_DURATION = 550;
+
private final HashMap<View, PendingIntent> mClickActions;
private Uri mKeyguardSliceUri;
- private TextView mTitle;
- private LinearLayout mRow;
+ @VisibleForTesting
+ TextView mTitle;
+ private Row mRow;
private int mTextColor;
private float mDarkAmount = 0;
private LiveData<Slice> mLiveData;
private int mIconSize;
- private Consumer<Boolean> mListener;
+ /**
+ * Listener called whenever the view contents change.
+ * Boolean will be true when the change happens animated.
+ */
+ private Consumer<Boolean> mContentChangeListener;
private boolean mHasHeader;
- private boolean mHideContent;
+ private Slice mSlice;
+ private boolean mPulsing;
public KeyguardSliceView(Context context) {
this(context, null, 0);
@@ -95,6 +105,18 @@
tunerService.addTunable(this, Settings.Secure.KEYGUARD_SLICE_URI);
mClickActions = new HashMap<>();
+
+ LayoutTransition transition = new LayoutTransition();
+ transition.setStagger(LayoutTransition.CHANGE_APPEARING, DEFAULT_ANIM_DURATION / 2);
+ transition.setDuration(LayoutTransition.APPEARING, DEFAULT_ANIM_DURATION);
+ transition.setDuration(LayoutTransition.DISAPPEARING, DEFAULT_ANIM_DURATION / 2);
+ transition.disableTransitionType(LayoutTransition.CHANGE_APPEARING);
+ transition.disableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
+ transition.setInterpolator(LayoutTransition.APPEARING, Interpolators.FAST_OUT_SLOW_IN);
+ transition.setInterpolator(LayoutTransition.DISAPPEARING, Interpolators.ALPHA_OUT);
+ transition.setAnimateParentHierarchy(false);
+ transition.addTransitionListener(new SliceViewTransitionListener());
+ setLayoutTransition(transition);
}
@Override
@@ -103,7 +125,6 @@
mTitle = findViewById(R.id.title);
mRow = findViewById(R.id.row);
mTextColor = Utils.getColorAttr(mContext, R.attr.wallpaperTextColor);
- mIconSize = (int) mContext.getResources().getDimension(R.dimen.widget_icon_size);
}
@Override
@@ -112,6 +133,7 @@
// Make sure we always have the most current slice
mLiveData.observeForever(this);
+ Dependency.get(ConfigurationController.class).addCallback(this);
}
@Override
@@ -119,17 +141,29 @@
super.onDetachedFromWindow();
mLiveData.removeObserver(this);
+ Dependency.get(ConfigurationController.class).removeCallback(this);
}
- private void showSlice(Slice slice) {
+ private void showSlice() {
+ if (mPulsing) {
+ mTitle.setVisibility(GONE);
+ mRow.setVisibility(GONE);
+ mContentChangeListener.accept(getLayoutTransition() != null);
+ return;
+ }
- ListContent lc = new ListContent(getContext(), slice);
+ if (mSlice == null) {
+ return;
+ }
+
+ ListContent lc = new ListContent(getContext(), mSlice);
mHasHeader = lc.hasHeader();
List<SliceItem> subItems = lc.getRowItems();
if (!mHasHeader) {
mTitle.setVisibility(GONE);
} else {
mTitle.setVisibility(VISIBLE);
+
// If there's a header it'll be the first subitem
RowContent header = new RowContent(getContext(), subItems.get(0),
true /* showStartItem */);
@@ -154,6 +188,7 @@
final int subItemsCount = subItems.size();
final int blendedColor = getTextColor();
final int startIndex = mHasHeader ? 1 : 0; // First item is header; skip it
+ mRow.setVisibility(subItemsCount > 0 ? VISIBLE : GONE);
for (int i = startIndex; i < subItemsCount; i++) {
SliceItem item = subItems.get(i);
RowContent rc = new RowContent(getContext(), item, true /* showStartItem */);
@@ -200,15 +235,20 @@
}
}
- updateVisibility();
- mListener.accept(mHasHeader);
+ if (mContentChangeListener != null) {
+ mContentChangeListener.accept(getLayoutTransition() != null);
+ }
}
- private void updateVisibility() {
- final boolean hasContent = mHasHeader || mRow.getChildCount() > 0;
- final int visibility = hasContent && !mHideContent ? VISIBLE : GONE;
- if (visibility != getVisibility()) {
- setVisibility(visibility);
+ public void setPulsing(boolean pulsing, boolean animate) {
+ mPulsing = pulsing;
+ LayoutTransition transition = getLayoutTransition();
+ if (!animate) {
+ setLayoutTransition(null);
+ }
+ showSlice();
+ if (!animate) {
+ setLayoutTransition(transition);
}
}
@@ -252,8 +292,9 @@
return optimalString.toString();
}
- public void setDark(float darkAmount) {
+ public void setDarkAmount(float darkAmount) {
mDarkAmount = darkAmount;
+ mRow.setDarkAmount(darkAmount);
updateTextColors();
}
@@ -281,12 +322,17 @@
}
}
- public void setListener(Consumer<Boolean> listener) {
- mListener = listener;
+ /**
+ * Listener that gets invoked every time the title or the row visibility changes.
+ * Parameter will be {@code true} whenever the change happens animated.
+ * @param contentChangeListener The listener.
+ */
+ public void setContentChangeListener(Consumer<Boolean> contentChangeListener) {
+ mContentChangeListener = contentChangeListener;
}
public boolean hasHeader() {
- return mHasHeader;
+ return mTitle.getVisibility() == VISIBLE;
}
/**
@@ -295,7 +341,8 @@
*/
@Override
public void onChanged(Slice slice) {
- showSlice(slice);
+ mSlice = slice;
+ showSlice();
}
@Override
@@ -333,15 +380,20 @@
updateTextColors();
}
- public void setHideContent(boolean hideContent) {
- mHideContent = hideContent;
- updateVisibility();
+ @Override
+ public void onDensityOrFontScaleChanged() {
+ mIconSize = mContext.getResources().getDimensionPixelSize(R.dimen.widget_icon_size);
}
public static class Row extends LinearLayout {
- private static final long ROW_ANIM_DURATION = 350;
- private final WakeLock mAnimationWakeLock;
+ /**
+ * This view is visible in AOD, which means that the device will sleep if we
+ * don't hold a wake lock. We want to enter doze only after all views have reached
+ * their desired positions.
+ */
+ private final Animation.AnimationListener mKeepAwakeListener;
+ private float mDarkAmount;
public Row(Context context) {
this(context, null);
@@ -357,16 +409,13 @@
public Row(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
- mAnimationWakeLock = WakeLock.createPartial(context, "slice animation");
+ mKeepAwakeListener = new KeepAwakeAnimationListener(mContext);
}
@Override
protected void onFinishInflate() {
LayoutTransition transition = new LayoutTransition();
- transition.setDuration(ROW_ANIM_DURATION);
- transition.setStagger(LayoutTransition.CHANGING, ROW_ANIM_DURATION);
- transition.setStagger(LayoutTransition.CHANGE_APPEARING, ROW_ANIM_DURATION);
- transition.setStagger(LayoutTransition.CHANGE_DISAPPEARING, ROW_ANIM_DURATION);
+ transition.setDuration(DEFAULT_ANIM_DURATION);
PropertyValuesHolder left = PropertyValuesHolder.ofInt("left", 0, 1);
PropertyValuesHolder right = PropertyValuesHolder.ofInt("right", 0, 1);
@@ -378,7 +427,8 @@
Interpolators.ACCELERATE_DECELERATE);
transition.setInterpolator(LayoutTransition.CHANGE_DISAPPEARING,
Interpolators.ACCELERATE_DECELERATE);
- transition.setStartDelay(LayoutTransition.CHANGE_APPEARING, ROW_ANIM_DURATION);
+ transition.setStartDelay(LayoutTransition.CHANGE_APPEARING, DEFAULT_ANIM_DURATION);
+ transition.setStartDelay(LayoutTransition.CHANGE_DISAPPEARING, DEFAULT_ANIM_DURATION);
ObjectAnimator appearAnimator = ObjectAnimator.ofFloat(null, "alpha", 0f, 1f);
transition.setAnimator(LayoutTransition.APPEARING, appearAnimator);
@@ -386,31 +436,11 @@
ObjectAnimator disappearAnimator = ObjectAnimator.ofFloat(null, "alpha", 1f, 0f);
transition.setInterpolator(LayoutTransition.DISAPPEARING, Interpolators.ALPHA_OUT);
- transition.setDuration(LayoutTransition.DISAPPEARING, ROW_ANIM_DURATION / 2);
+ transition.setDuration(LayoutTransition.DISAPPEARING, DEFAULT_ANIM_DURATION / 4);
transition.setAnimator(LayoutTransition.DISAPPEARING, disappearAnimator);
transition.setAnimateParentHierarchy(false);
setLayoutTransition(transition);
-
- // This view is visible in AOD, which means that the device will sleep if we
- // don't hold a wake lock. We want to enter doze only after all views have reached
- // their desired positions.
- setLayoutAnimationListener(new Animation.AnimationListener() {
- @Override
- public void onAnimationStart(Animation animation) {
- mAnimationWakeLock.acquire();
- }
-
- @Override
- public void onAnimationEnd(Animation animation) {
- mAnimationWakeLock.release();
- }
-
- @Override
- public void onAnimationRepeat(Animation animation) {
-
- }
- });
}
@Override
@@ -424,23 +454,58 @@
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
+
+ public void setDarkAmount(float darkAmount) {
+ boolean isAwake = darkAmount != 0;
+ boolean wasAwake = mDarkAmount != 0;
+ if (isAwake == wasAwake) {
+ return;
+ }
+ mDarkAmount = darkAmount;
+ setLayoutAnimationListener(isAwake ? null : mKeepAwakeListener);
+ }
+
+ @Override
+ public boolean hasOverlappingRendering() {
+ return false;
+ }
}
/**
* Representation of an item that appears under the clock on main keyguard message.
*/
- private class KeyguardSliceButton extends Button {
+ @VisibleForTesting
+ static class KeyguardSliceButton extends Button implements
+ ConfigurationController.ConfigurationListener {
+ private final Context mContext;
public KeyguardSliceButton(Context context) {
super(context, null /* attrs */, 0 /* styleAttr */,
com.android.keyguard.R.style.TextAppearance_Keyguard_Secondary);
- int horizontalPadding = (int) context.getResources()
+ mContext = context;
+ onDensityOrFontScaleChanged();
+ setEllipsize(TruncateAt.END);
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ Dependency.get(ConfigurationController.class).addCallback(this);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ Dependency.get(ConfigurationController.class).removeCallback(this);
+ }
+
+ @Override
+ public void onDensityOrFontScaleChanged() {
+ int horizontalPadding = (int) mContext.getResources()
.getDimension(R.dimen.widget_horizontal_padding);
setPadding(horizontalPadding / 2, 0, horizontalPadding / 2, 0);
- setCompoundDrawablePadding((int) context.getResources()
+ setCompoundDrawablePadding((int) mContext.getResources()
.getDimension(R.dimen.widget_icon_padding));
- setMaxLines(1);
- setEllipsize(TruncateAt.END);
}
@Override
@@ -465,4 +530,38 @@
}
}
}
+
+ private class SliceViewTransitionListener implements LayoutTransition.TransitionListener {
+ @Override
+ public void startTransition(LayoutTransition transition, ViewGroup container, View view,
+ int transitionType) {
+ switch (transitionType) {
+ case LayoutTransition.APPEARING:
+ int translation = getResources().getDimensionPixelSize(
+ R.dimen.pulsing_notification_appear_translation);
+ view.setTranslationY(translation);
+ view.animate()
+ .translationY(0)
+ .setDuration(DEFAULT_ANIM_DURATION)
+ .setInterpolator(Interpolators.ALPHA_IN)
+ .start();
+ break;
+ case LayoutTransition.DISAPPEARING:
+ if (view == mTitle) {
+ // Translate the view to the inverse of its height, so the layout event
+ // won't misposition it.
+ LayoutParams params = (LayoutParams) mTitle.getLayoutParams();
+ int margin = params.topMargin + params.bottomMargin;
+ mTitle.setTranslationY(-mTitle.getHeight() - margin);
+ }
+ break;
+ }
+ }
+
+ @Override
+ public void endTransition(LayoutTransition transition, ViewGroup container, View view,
+ int transitionType) {
+
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index ff3af17..ce34d0b 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -16,12 +16,11 @@
package com.android.keyguard;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.app.ActivityManager;
-import android.app.AlarmManager;
import android.app.IActivityManager;
import android.content.Context;
-import android.content.res.ColorStateList;
-import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Color;
import android.os.Handler;
@@ -37,18 +36,24 @@
import android.util.Slog;
import android.util.TypedValue;
import android.view.View;
-import android.view.ViewGroup;
import android.widget.GridLayout;
+import android.widget.RelativeLayout;
import android.widget.TextClock;
import android.widget.TextView;
import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.ViewClippingUtil;
+import com.android.systemui.Dependency;
+import com.android.systemui.Interpolators;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.util.wakelock.KeepAwakeAnimationListener;
import com.google.android.collect.Sets;
import java.util.Locale;
-public class KeyguardStatusView extends GridLayout {
+public class KeyguardStatusView extends GridLayout implements
+ ConfigurationController.ConfigurationListener, View.OnLayoutChangeListener {
private static final boolean DEBUG = KeyguardConstants.DEBUG;
private static final String TAG = "KeyguardStatusView";
private static final int MARQUEE_DELAY_MS = 2000;
@@ -56,13 +61,11 @@
private final LockPatternUtils mLockPatternUtils;
private final IActivityManager mIActivityManager;
private final float mSmallClockScale;
- private final float mWidgetPadding;
private TextView mLogoutView;
private TextClock mClockView;
private View mClockSeparator;
private TextView mOwnerInfo;
- private ViewGroup mClockContainer;
private KeyguardSliceView mKeyguardSlice;
private Runnable mPendingMarqueeStart;
private Handler mHandler;
@@ -71,6 +74,9 @@
private boolean mPulsing;
private float mDarkAmount = 0;
private int mTextColor;
+ private float mWidgetPadding;
+ private boolean mAnimateLayout;
+ private int mLastLayoutHeight;
private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
@@ -127,7 +133,7 @@
mHandler = new Handler(Looper.myLooper());
mSmallClockScale = getResources().getDimension(R.dimen.widget_small_font_size)
/ getResources().getDimension(R.dimen.widget_big_font_size);
- mWidgetPadding = getResources().getDimension(R.dimen.widget_vertical_padding);
+ onDensityOrFontScaleChanged();
}
private void setEnableMarquee(boolean enabled) {
@@ -160,7 +166,6 @@
mLogoutView = findViewById(R.id.logout);
mLogoutView.setOnClickListener(this::onLogoutClicked);
- mClockContainer = findViewById(R.id.keyguard_clock_container);
mClockView = findViewById(R.id.clock_view);
mClockView.setShowCurrentUserTime(true);
if (KeyguardClockAccessibilityDelegate.isNeeded(mContext)) {
@@ -169,46 +174,111 @@
mOwnerInfo = findViewById(R.id.owner_info);
mKeyguardSlice = findViewById(R.id.keyguard_status_area);
mClockSeparator = findViewById(R.id.clock_separator);
- mVisibleInDoze = Sets.newArraySet(mClockView, mKeyguardSlice, mClockSeparator);
+ mVisibleInDoze = Sets.newArraySet(mClockView, mKeyguardSlice);
mTextColor = mClockView.getCurrentTextColor();
- mKeyguardSlice.setListener(this::onSliceContentChanged);
- onSliceContentChanged(mKeyguardSlice.hasHeader());
+ mClockView.addOnLayoutChangeListener(this);
+ mClockSeparator.addOnLayoutChangeListener(this);
+ mKeyguardSlice.setContentChangeListener(this::onSliceContentChanged);
+ onSliceContentChanged(false /* animated */);
boolean shouldMarquee = KeyguardUpdateMonitor.getInstance(mContext).isDeviceInteractive();
setEnableMarquee(shouldMarquee);
refresh();
updateOwnerInfo();
updateLogoutView();
+ updateDark();
// Disable elegant text height because our fancy colon makes the ymin value huge for no
// reason.
mClockView.setElegantTextHeight(false);
}
- private void onSliceContentChanged(boolean hasHeader) {
- final boolean smallClock = hasHeader || mPulsing;
- final float clockScale = smallClock ? mSmallClockScale : 1;
- float translation = (mClockView.getHeight() - (mClockView.getHeight() * clockScale)) / 2f;
- if (smallClock) {
- translation -= mWidgetPadding;
+ private void onSliceContentChanged(boolean animated) {
+ mAnimateLayout = animated;
+ boolean smallClock = mKeyguardSlice.hasHeader() || mPulsing;
+ float clockScale = smallClock ? mSmallClockScale : 1;
+
+ RelativeLayout.LayoutParams layoutParams =
+ (RelativeLayout.LayoutParams) mClockView.getLayoutParams();
+ int height = mClockView.getHeight();
+ layoutParams.bottomMargin = (int) -(height - (clockScale * height));
+ mClockView.setLayoutParams(layoutParams);
+
+ layoutParams = (RelativeLayout.LayoutParams) mClockSeparator.getLayoutParams();
+ layoutParams.topMargin = smallClock ? (int) mWidgetPadding : 0;
+ layoutParams.bottomMargin = layoutParams.topMargin;
+ mClockSeparator.setLayoutParams(layoutParams);
+ }
+
+ /**
+ * Animate clock and its separator when necessary.
+ */
+ @Override
+ public void onLayoutChange(View view, int left, int top, int right, int bottom,
+ int oldLeft, int oldTop, int oldRight, int oldBottom) {
+ int heightOffset = mPulsing ? 0 : getHeight() - mLastLayoutHeight;
+ boolean hasHeader = mKeyguardSlice.hasHeader();
+ boolean smallClock = hasHeader || mPulsing;
+ long duration = KeyguardSliceView.DEFAULT_ANIM_DURATION;
+ long delay = smallClock ? 0 : duration / 4;
+
+ if (view == mClockView) {
+ float clockScale = smallClock ? mSmallClockScale : 1;
+ if (mAnimateLayout) {
+ mClockView.setY(oldTop + heightOffset);
+ mClockView.animate().cancel();
+ mClockView.animate()
+ .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+ .setDuration(duration)
+ .setListener(new ClipChildrenAnimationListener())
+ .setStartDelay(delay)
+ .y(top)
+ .scaleX(clockScale)
+ .scaleY(clockScale)
+ .start();
+ } else {
+ mClockView.setY(top);
+ mClockView.setScaleX(clockScale);
+ mClockView.setScaleY(clockScale);
+ }
+ } else if (view == mClockSeparator) {
+ boolean hasSeparator = hasHeader && !mPulsing;
+ float alpha = hasSeparator ? 1 : 0;
+ if (mAnimateLayout) {
+ boolean isAwake = mDarkAmount != 0;
+ mClockSeparator.setY(oldTop + heightOffset);
+ mClockSeparator.animate().cancel();
+ mClockSeparator.animate()
+ .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+ .setDuration(duration)
+ .setListener(isAwake ? null : new KeepAwakeAnimationListener(getContext()))
+ .setStartDelay(delay)
+ .y(top)
+ .alpha(alpha)
+ .start();
+ } else {
+ mClockSeparator.setY(top);
+ mClockSeparator.setAlpha(alpha);
+ }
}
- mClockView.setTranslationY(translation);
- mClockView.setScaleX(clockScale);
- mClockView.setScaleY(clockScale);
- mClockSeparator.setVisibility(hasHeader && !mPulsing ? VISIBLE : GONE);
}
@Override
- protected void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- mClockView.setTextSize(TypedValue.COMPLEX_UNIT_PX,
- getResources().getDimensionPixelSize(R.dimen.widget_big_font_size));
- // Some layouts like burmese have a different margin for the clock
- MarginLayoutParams layoutParams = (MarginLayoutParams) mClockView.getLayoutParams();
- layoutParams.bottomMargin = getResources().getDimensionPixelSize(
- R.dimen.bottom_text_spacing_digital);
- mClockView.setLayoutParams(layoutParams);
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ mClockView.setPivotX(mClockView.getWidth() / 2);
+ mClockView.setPivotY(0);
+ mLastLayoutHeight = getHeight();
+ }
+
+ @Override
+ public void onDensityOrFontScaleChanged() {
+ mWidgetPadding = getResources().getDimension(R.dimen.widget_vertical_padding);
+ if (mClockView != null) {
+ mClockView.setTextSize(TypedValue.COMPLEX_UNIT_PX,
+ getResources().getDimensionPixelSize(R.dimen.widget_big_font_size));
+ }
if (mOwnerInfo != null) {
mOwnerInfo.setTextSize(TypedValue.COMPLEX_UNIT_PX,
getResources().getDimensionPixelSize(R.dimen.widget_label_font_size));
@@ -224,14 +294,6 @@
refreshTime();
}
- public int getClockBottom() {
- if (mOwnerInfo != null && mOwnerInfo.getVisibility() == VISIBLE) {
- return mOwnerInfo.getBottom();
- } else {
- return mClockContainer.getBottom();
- }
- }
-
public int getLogoutButtonHeight() {
return mLogoutView.getVisibility() == VISIBLE ? mLogoutView.getHeight() : 0;
}
@@ -249,34 +311,8 @@
private void updateOwnerInfo() {
if (mOwnerInfo == null) return;
- String ownerInfo = getOwnerInfo();
- if (!TextUtils.isEmpty(ownerInfo)) {
- mOwnerInfo.setVisibility(View.VISIBLE);
- mOwnerInfo.setText(ownerInfo);
- } else {
- mOwnerInfo.setVisibility(View.GONE);
- }
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mInfoCallback);
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mInfoCallback);
- }
-
- private String getOwnerInfo() {
- String info = null;
- if (mLockPatternUtils.isDeviceOwnerInfoEnabled()) {
- // Use the device owner information set by device policy client via
- // device policy manager.
- info = mLockPatternUtils.getDeviceOwnerInfo();
- } else {
+ String info = mLockPatternUtils.getDeviceOwnerInfo();
+ if (info == null) {
// Use the current user owner information if enabled.
final boolean ownerInfoEnabled = mLockPatternUtils.isOwnerInfoEnabled(
KeyguardUpdateMonitor.getCurrentUser());
@@ -284,7 +320,21 @@
info = mLockPatternUtils.getOwnerInfo(KeyguardUpdateMonitor.getCurrentUser());
}
}
- return info;
+ mOwnerInfo.setText(info);
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mInfoCallback);
+ Dependency.get(ConfigurationController.class).addCallback(this);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mInfoCallback);
+ Dependency.get(ConfigurationController.class).removeCallback(this);
}
@Override
@@ -329,32 +379,27 @@
return;
}
mDarkAmount = darkAmount;
-
- boolean dark = darkAmount == 1;
- mLogoutView.setAlpha(dark ? 0 : 1);
- final int N = mClockContainer.getChildCount();
- for (int i = 0; i < N; i++) {
- View child = mClockContainer.getChildAt(i);
- if (mVisibleInDoze.contains(child)) {
- continue;
- }
- child.setAlpha(dark ? 0 : 1);
- }
- if (mOwnerInfo != null) {
- mOwnerInfo.setAlpha(dark ? 0 : 1);
- }
-
- final int blendedTextColor = ColorUtils.blendARGB(mTextColor, Color.WHITE, darkAmount);
- updateDozeVisibleViews();
- mKeyguardSlice.setDark(darkAmount);
- mClockView.setTextColor(blendedTextColor);
- mClockSeparator.setBackgroundTintList(ColorStateList.valueOf(blendedTextColor));
+ updateDark();
}
- public void setPulsing(boolean pulsing) {
+ private void updateDark() {
+ boolean dark = mDarkAmount == 1;
+ mLogoutView.setAlpha(dark ? 0 : 1);
+ if (mOwnerInfo != null) {
+ boolean hasText = !TextUtils.isEmpty(mOwnerInfo.getText());
+ mOwnerInfo.setVisibility(hasText && mDarkAmount != 1 ? VISIBLE : GONE);
+ }
+
+ final int blendedTextColor = ColorUtils.blendARGB(mTextColor, Color.WHITE, mDarkAmount);
+ updateDozeVisibleViews();
+ mKeyguardSlice.setDarkAmount(mDarkAmount);
+ mClockView.setTextColor(blendedTextColor);
+ mClockSeparator.setBackgroundColor(blendedTextColor);
+ }
+
+ public void setPulsing(boolean pulsing, boolean animate) {
mPulsing = pulsing;
- mKeyguardSlice.setHideContent(pulsing);
- onSliceContentChanged(mKeyguardSlice.hasHeader());
+ mKeyguardSlice.setPulsing(pulsing, animate);
updateDozeVisibleViews();
}
@@ -378,4 +423,24 @@
Log.e(TAG, "Failed to logout user", re);
}
}
+
+ private class ClipChildrenAnimationListener extends AnimatorListenerAdapter implements
+ ViewClippingUtil.ClippingParameters {
+
+ ClipChildrenAnimationListener() {
+ ViewClippingUtil.setClippingDeactivated(mClockView, true /* deactivated */,
+ this /* clippingParams */);
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ ViewClippingUtil.setClippingDeactivated(mClockView, false /* deactivated */,
+ this /* clippingParams */);
+ }
+
+ @Override
+ public boolean shouldFinish(View view) {
+ return view == getParent();
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
index b0bda16..90140ff 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
@@ -80,7 +80,9 @@
}
boolean messagePending = mHandler.hasCallbacks(mApplyPendingScreenState);
- if (messagePending || oldState == DozeMachine.State.INITIALIZED) {
+ boolean pulseEnding = oldState == DozeMachine.State.DOZE_PULSE_DONE
+ && newState == DozeMachine.State.DOZE_AOD;
+ if (messagePending || oldState == DozeMachine.State.INITIALIZED || pulseEnding) {
// During initialization, we hide the navigation bar. That is however only applied after
// a traversal; setting the screen state here is immediate however, so it can happen
// that the screen turns on again before the navigation bar is hidden. To work around
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AlphaOptimizedTextView.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlphaOptimizedTextView.java
new file mode 100644
index 0000000..742005d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AlphaOptimizedTextView.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.TextView;
+
+/**
+ * A TextView which does not have overlapping renderings commands and therefore does not need a
+ * layer when alpha is changed.
+ */
+public class AlphaOptimizedTextView extends TextView {
+ public AlphaOptimizedTextView(Context context) {
+ super(context);
+ }
+
+ public AlphaOptimizedTextView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public AlphaOptimizedTextView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public AlphaOptimizedTextView(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ @Override
+ public boolean hasOverlappingRendering() {
+ return false;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index a0a97c5..932c3c1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -1866,7 +1866,7 @@
if (view == null && mQsExpanded) {
return;
}
- if (needsAnimation) {
+ if (needsAnimation && mDarkAmount == 0) {
mAnimateNextPositionUpdate = true;
}
ExpandableView firstChildNotGone = mNotificationStackScroller.getFirstChildNotGone();
@@ -2652,10 +2652,13 @@
public void setPulsing(boolean pulsing) {
mPulsing = pulsing;
- mKeyguardStatusView.setPulsing(pulsing);
- positionClockAndNotifications();
- mNotificationStackScroller.setPulsing(pulsing, mKeyguardStatusView.getLocationOnScreen()[1]
- + mKeyguardStatusView.getClockBottom());
+ final boolean canAnimatePulse =
+ !DozeParameters.getInstance(mContext).getDisplayNeedsBlanking();
+ if (canAnimatePulse) {
+ mAnimateNextPositionUpdate = true;
+ }
+ mNotificationStackScroller.setPulsing(pulsing, canAnimatePulse);
+ mKeyguardStatusView.setPulsing(pulsing, canAnimatePulse);
}
public void setAmbientIndicationBottomPadding(int ambientIndicationBottomPadding) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index b1c0a96..a0b3c65 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -69,6 +69,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.keyguard.KeyguardSliceView;
import com.android.settingslib.Utils;
import com.android.systemui.Dependency;
import com.android.systemui.ExpandHelper;
@@ -370,6 +371,7 @@
private boolean mGroupExpandedForMeasure;
private boolean mScrollable;
private View mForcedScroll;
+ private View mNeedingPulseAnimation;
private float mDarkAmount = 0f;
private static final Property<NotificationStackScrollLayout, Float> DARK_AMOUNT =
new FloatProperty<NotificationStackScrollLayout>("darkAmount") {
@@ -406,7 +408,6 @@
private final int mSeparatorWidth;
private final int mSeparatorThickness;
private final Rect mBackgroundAnimationRect = new Rect();
- private int mClockBottom;
private int mAntiBurnInOffsetX;
private ArrayList<BiConsumer<Float, Float>> mExpandedHeightListeners = new ArrayList<>();
private int mHeadsUpInset;
@@ -717,11 +718,7 @@
}
private void updateAlgorithmHeightAndPadding() {
- if (mPulsing) {
- mTopPadding = mClockBottom;
- } else {
- mTopPadding = (int) MathUtils.lerp(mRegularTopPadding, mDarkTopPadding, mDarkAmount);
- }
+ mTopPadding = (int) MathUtils.lerp(mRegularTopPadding, mDarkTopPadding, mDarkAmount);
mAmbientState.setLayoutHeight(getLayoutHeight());
updateAlgorithmLayoutMinHeight();
mAmbientState.setTopPadding(mTopPadding);
@@ -3174,6 +3171,7 @@
generateViewResizeEvent();
generateGroupExpansionEvent();
generateAnimateEverythingEvent();
+ generatePulsingAnimationEvent();
mNeedsAnimation = false;
}
@@ -4428,18 +4426,28 @@
return mIsExpanded;
}
- public void setPulsing(boolean pulsing, int clockBottom) {
+ public void setPulsing(boolean pulsing, boolean animated) {
if (!mPulsing && !pulsing) {
return;
}
mPulsing = pulsing;
- mClockBottom = clockBottom;
+ mNeedingPulseAnimation = animated ? getFirstChildNotGone() : null;
mAmbientState.setPulsing(pulsing);
updateNotificationAnimationStates();
updateAlgorithmHeightAndPadding();
updateContentHeight();
- notifyHeightChangeListener(mShelf);
requestChildrenUpdate();
+ notifyHeightChangeListener(null, animated);
+ mNeedsAnimation |= animated;
+ }
+
+ private void generatePulsingAnimationEvent() {
+ if (mNeedingPulseAnimation != null) {
+ int type = mPulsing ? AnimationEvent.ANIMATION_TYPE_PULSE_APPEAR
+ : AnimationEvent.ANIMATION_TYPE_PULSE_DISAPPEAR;
+ mAnimationEvents.add(new AnimationEvent(mNeedingPulseAnimation, type));
+ mNeedingPulseAnimation = null;
+ }
}
public void setFadingOut(boolean fadingOut) {
@@ -5007,6 +5015,16 @@
.animateTopInset()
.animateY()
.animateZ(),
+
+ // ANIMATION_TYPE_PULSE_APPEAR
+ new AnimationFilter()
+ .animateAlpha()
+ .animateY(),
+
+ // ANIMATION_TYPE_PULSE_DISAPPEAR
+ new AnimationFilter()
+ .animateAlpha()
+ .animateY(),
};
static int[] LENGTHS = new int[] {
@@ -5067,6 +5085,12 @@
// ANIMATION_TYPE_EVERYTHING
StackStateAnimator.ANIMATION_DURATION_STANDARD,
+
+ // ANIMATION_TYPE_PULSE_APPEAR
+ KeyguardSliceView.DEFAULT_ANIM_DURATION,
+
+ // ANIMATION_TYPE_PULSE_DISAPPEAR
+ KeyguardSliceView.DEFAULT_ANIM_DURATION / 2,
};
static final int ANIMATION_TYPE_ADD = 0;
@@ -5088,6 +5112,8 @@
static final int ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK = 16;
static final int ANIMATION_TYPE_HEADS_UP_OTHER = 17;
static final int ANIMATION_TYPE_EVERYTHING = 18;
+ static final int ANIMATION_TYPE_PULSE_APPEAR = 19;
+ static final int ANIMATION_TYPE_PULSE_DISAPPEAR = 20;
static final int DARK_ANIMATION_ORIGIN_INDEX_ABOVE = -1;
static final int DARK_ANIMATION_ORIGIN_INDEX_BELOW = -2;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
index c80bdc6..d01db14 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
@@ -60,6 +60,7 @@
public static final int ANIMATION_DELAY_HEADS_UP_CLICKED= 120;
private final int mGoToFullShadeAppearingTranslation;
+ private final int mPulsingAppearingTranslation;
private final ExpandableViewState mTmpState = new ExpandableViewState();
private final AnimationProperties mAnimationProperties;
public NotificationStackScrollLayout mHostLayout;
@@ -90,6 +91,9 @@
mGoToFullShadeAppearingTranslation =
hostLayout.getContext().getResources().getDimensionPixelSize(
R.dimen.go_to_full_shade_appearing_translation);
+ mPulsingAppearingTranslation =
+ hostLayout.getContext().getResources().getDimensionPixelSize(
+ R.dimen.pulsing_notification_appear_translation);
mAnimationProperties = new AnimationProperties() {
@Override
public AnimationFilter getAnimationFilter() {
@@ -427,6 +431,18 @@
ExpandableNotificationRow row = (ExpandableNotificationRow) event.changingView;
row.prepareExpansionChanged(finalState);
} else if (event.animationType == NotificationStackScrollLayout
+ .AnimationEvent.ANIMATION_TYPE_PULSE_APPEAR) {
+ ExpandableViewState viewState = finalState.getViewStateForView(changingView);
+ mTmpState.copyFrom(viewState);
+ mTmpState.yTranslation += mPulsingAppearingTranslation;
+ mTmpState.alpha = 0;
+ mTmpState.applyToView(changingView);
+ } else if (event.animationType == NotificationStackScrollLayout
+ .AnimationEvent.ANIMATION_TYPE_PULSE_DISAPPEAR) {
+ ExpandableViewState viewState = finalState.getViewStateForView(changingView);
+ viewState.yTranslation += mPulsingAppearingTranslation;
+ viewState.alpha = 0;
+ } else if (event.animationType == NotificationStackScrollLayout
.AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR) {
// This item is added, initialize it's properties.
ExpandableViewState viewState = finalState.getViewStateForView(changingView);
diff --git a/packages/SystemUI/src/com/android/systemui/util/wakelock/KeepAwakeAnimationListener.java b/packages/SystemUI/src/com/android/systemui/util/wakelock/KeepAwakeAnimationListener.java
new file mode 100644
index 0000000..a54b0e1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/wakelock/KeepAwakeAnimationListener.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.util.wakelock;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.content.Context;
+import android.view.animation.Animation;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.util.Assert;
+
+public class KeepAwakeAnimationListener extends AnimatorListenerAdapter
+ implements Animation.AnimationListener {
+ @VisibleForTesting
+ static WakeLock sWakeLock;
+
+ public KeepAwakeAnimationListener(Context context) {
+ Assert.isMainThread();
+ if (sWakeLock == null) {
+ sWakeLock = WakeLock.createPartial(context, "animation");
+ }
+ }
+
+ @Override
+ public void onAnimationStart(Animation animation) {
+ onStart();
+ }
+
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ onEnd();
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) {
+
+ }
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ onStart();
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ onEnd();
+ }
+
+ private void onStart() {
+ Assert.isMainThread();
+ sWakeLock.acquire();
+ }
+
+ private void onEnd() {
+ Assert.isMainThread();
+ sWakeLock.release();
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java
index 7686948..9a28657 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java
@@ -16,38 +16,77 @@
package com.android.keyguard;
import android.graphics.Color;
+import android.net.Uri;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.MeasureSpec;
+import android.view.ViewGroup;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.keyguard.KeyguardSliceProvider;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.Collections;
+import java.util.HashSet;
+
+import androidx.slice.SliceProvider;
+import androidx.slice.SliceSpecs;
+import androidx.slice.builders.ListBuilder;
+
@SmallTest
-@RunWithLooper
+@RunWithLooper(setAsMainLooper = true)
@RunWith(AndroidTestingRunner.class)
public class KeyguardSliceViewTest extends SysuiTestCase {
private KeyguardSliceView mKeyguardSliceView;
+ private Uri mSliceUri;
@Before
public void setUp() throws Exception {
mKeyguardSliceView = (KeyguardSliceView) LayoutInflater.from(getContext())
.inflate(R.layout.keyguard_status_area, null);
+ mSliceUri = Uri.parse(KeyguardSliceProvider.KEYGUARD_SLICE_URI);
+ SliceProvider.setSpecs(new HashSet<>(Collections.singletonList(SliceSpecs.LIST)));
+ }
+
+ @Test
+ public void showSlice_notifiesListener() {
+ ListBuilder builder = new ListBuilder(getContext(), mSliceUri);
+ boolean[] notified = {false};
+ mKeyguardSliceView.setContentChangeListener((hasHeader)-> {
+ notified[0] = true;
+ });
+ mKeyguardSliceView.onChanged(builder.build());
+ Assert.assertTrue("Listener should be notified about slice changes.", notified[0]);
+ }
+
+ @Test
+ public void hasHeader_readsSliceData() {
+ ListBuilder builder = new ListBuilder(getContext(), mSliceUri);
+ mKeyguardSliceView.onChanged(builder.build());
+ Assert.assertFalse("View should not have a header", mKeyguardSliceView.hasHeader());
+
+ builder.setHeader((ListBuilder.HeaderBuilder headerBuilder) -> {
+ headerBuilder.setTitle("header title!");
+ });
+ mKeyguardSliceView.onChanged(builder.build());
+ Assert.assertTrue("View should have a header", mKeyguardSliceView.hasHeader());
}
@Test
public void getTextColor_whiteTextWhenAOD() {
// Set text color to red since the default is white and test would always pass
mKeyguardSliceView.setTextColor(Color.RED);
- mKeyguardSliceView.setDark(0);
+ mKeyguardSliceView.setDarkAmount(0);
Assert.assertEquals("Should be using regular text color", Color.RED,
mKeyguardSliceView.getTextColor());
- mKeyguardSliceView.setDark(1);
+ mKeyguardSliceView.setDarkAmount(1);
Assert.assertEquals("Should be using AOD text color", Color.WHITE,
mKeyguardSliceView.getTextColor());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/KeepAwakeAnimationListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/KeepAwakeAnimationListenerTest.java
new file mode 100644
index 0000000..43942f7
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/KeepAwakeAnimationListenerTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.util.wakelock;
+
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.animation.Animator;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@RunWith(AndroidTestingRunner.class)
+public class KeepAwakeAnimationListenerTest extends SysuiTestCase {
+ @Mock WakeLock mWakeLock;
+ KeepAwakeAnimationListener mKeepAwakeAnimationListener;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ KeepAwakeAnimationListener.sWakeLock = mWakeLock;
+ mKeepAwakeAnimationListener = new KeepAwakeAnimationListener(getContext());
+ }
+
+ @Test
+ public void onAnimationStart_holdsWakeLock() {
+ mKeepAwakeAnimationListener.onAnimationStart((Animator) null);
+ verify(mWakeLock).acquire();
+ verify(mWakeLock, never()).release();
+
+ mKeepAwakeAnimationListener.onAnimationEnd((Animator) null);
+ verify(mWakeLock).release();
+ }
+}