Fixed fading and dozemode for custom notifications

The custom notifications were fading really ugly when they
had a dark background like media notifications, because
it was fading from dark to dark.
Now this background is shared for both custom views which also
reduces overdraw for them.

In addition does the doze mode now work much nicer because we're
only fading them to greyscale instead of inverting.
This also fixed an issue where legacy custom notifications with
a dark background were colorful during doze.

Bug: 19437552
Change-Id: I87798da9ac11b9abfe4470b6ca53b555da3aa629
This commit is contained in:
Selim Cinek
2016-03-04 14:44:56 -08:00
parent e81b82ba25
commit c317933a91
8 changed files with 256 additions and 36 deletions

View File

@@ -48,6 +48,7 @@
<item type="id" name="transformation_start_y_tag"/>
<item type="id" name="transformation_start_scale_x_tag"/>
<item type="id" name="transformation_start_scale_y_tag"/>
<item type="id" name="custom_background_color"/>
<!-- Whether the icon is from a notification for which targetSdk < L -->
<item type="id" name="icon_is_pre_L"/>

View File

@@ -36,7 +36,9 @@ import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.classifier.FalsingManager;
import com.android.systemui.statusbar.notification.FakeShadowView;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.stack.StackStateAnimator;
/**
* Base class for both {@link ExpandableNotificationRow} and {@link NotificationOverflowContainer}
@@ -121,6 +123,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
private float mAnimationTranslationY;
private boolean mDrawingAppearAnimation;
private ValueAnimator mAppearAnimator;
private ValueAnimator mBackgroundColorAnimator;
private float mAppearAnimationFraction = -1.0f;
private float mAppearAnimationTranslation;
private boolean mShowingLegacyBackground;
@@ -157,6 +160,9 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
};
private float mShadowAlpha = 1.0f;
private FakeShadowView mFakeShadow;
private int mCurrentBackgroundTint;
private int mTargetTint;
private int mStartTint;
public ActivatableNotificationView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -457,21 +463,63 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
* Sets the tint color of the background
*/
public void setTintColor(int color) {
setTintColor(color, false);
}
/**
* Sets the tint color of the background
*/
public void setTintColor(int color, boolean animated) {
mBgTint = color;
updateBackgroundTint();
updateBackgroundTint(animated);
}
protected void updateBackgroundTint() {
int color = getBgColor();
updateBackgroundTint(false /* animated */);
}
private void updateBackgroundTint(boolean animated) {
if (mBackgroundColorAnimator != null) {
mBackgroundColorAnimator.cancel();
}
int rippleColor = getRippleColor();
mBackgroundDimmed.setRippleColor(rippleColor);
mBackgroundNormal.setRippleColor(rippleColor);
int color = calculateBgColor();
if (!animated) {
setBackgroundTintColor(color);
} else if (color != mCurrentBackgroundTint) {
mStartTint = mCurrentBackgroundTint;
mTargetTint = color;
mBackgroundColorAnimator = ValueAnimator.ofFloat(0.0f, 1.0f);
mBackgroundColorAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int newColor = NotificationUtils.interpolateColors(mStartTint, mTargetTint,
animation.getAnimatedFraction());
setBackgroundTintColor(newColor);
}
});
mBackgroundColorAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
mBackgroundColorAnimator.setInterpolator(Interpolators.LINEAR);
mBackgroundColorAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mBackgroundColorAnimator = null;
}
});
mBackgroundColorAnimator.start();
}
}
private void setBackgroundTintColor(int color) {
mCurrentBackgroundTint = color;
if (color == mNormalColor) {
// We don't need to tint a normal notification
color = 0;
}
mBackgroundDimmed.setTint(color);
mBackgroundNormal.setTint(color);
mBackgroundDimmed.setRippleColor(rippleColor);
mBackgroundNormal.setRippleColor(rippleColor);
}
/**
@@ -773,8 +821,12 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
protected abstract View getContentView();
public int getBgColor() {
if (mBgTint != 0) {
public int calculateBgColor() {
return calculateBgColor(true /* withTint */);
}
private int calculateBgColor(boolean withTint) {
if (withTint && mBgTint != 0) {
return mBgTint;
} else if (mShowingLegacyBackground) {
return mLegacyColor;
@@ -839,7 +891,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
}
public boolean hasSameBgColor(ActivatableNotificationView otherView) {
return getBgColor() == otherView.getBgColor();
return calculateBgColor() == otherView.calculateBgColor();
}
@Override
@@ -863,6 +915,10 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
outlineTranslation);
}
public int getBackgroundColorWithoutTint() {
return calculateBgColor(false /* withTint */);
}
public interface OnActivatedListener {
void onActivated(ActivatableNotificationView view);
void onActivationReset(ActivatableNotificationView view);

View File

@@ -568,6 +568,13 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
mPublicLayout.reInflateViews();
}
public void setContentBackground(int customBackgroundColor, boolean animate,
NotificationContentView notificationContentView) {
if (getShowingLayout() == notificationContentView) {
setTintColor(customBackgroundColor, animate);
}
}
public interface ExpansionLogger {
public void logNotificationExpansion(String key, boolean userAction, boolean expanded);
}
@@ -1107,7 +1114,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
} else {
animateShowingPublic(delay, duration);
}
NotificationContentView showingLayout = getShowingLayout();
showingLayout.updateBackgroundColor(animated);
mPrivateLayout.updateExpandButtons(isExpandable());
updateClearability();
mShowingPublicInitialized = true;

View File

@@ -33,6 +33,7 @@ import com.android.systemui.R;
import com.android.systemui.statusbar.notification.HybridNotificationView;
import com.android.systemui.statusbar.notification.HybridNotificationViewManager;
import com.android.systemui.statusbar.notification.NotificationCustomViewWrapper;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.NotificationViewWrapper;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.policy.RemoteInputView;
@@ -48,7 +49,7 @@ public class NotificationContentView extends FrameLayout {
private static final int VISIBLE_TYPE_EXPANDED = 1;
private static final int VISIBLE_TYPE_HEADSUP = 2;
private static final int VISIBLE_TYPE_SINGLELINE = 3;
private static final int UNDEFINED = -1;
public static final int UNDEFINED = -1;
private final Rect mClipBounds = new Rect();
private final int mMinContractedHeight;
@@ -367,6 +368,7 @@ public class NotificationContentView extends FrameLayout {
getViewForVisibleType(visibleType).setVisibility(View.VISIBLE);
hiddenView.transformTo(shownView, 0.0f);
mVisibleType = visibleType;
updateBackgroundColor(true /* animate */);
}
if (mTransformationStartVisibleType != UNDEFINED
&& mVisibleType != mTransformationStartVisibleType) {
@@ -376,11 +378,29 @@ public class NotificationContentView extends FrameLayout {
float transformationAmount = calculateTransformationAmount();
shownView.transformFrom(hiddenView, transformationAmount);
hiddenView.transformTo(shownView, transformationAmount);
updateBackgroundTransformation(transformationAmount);
} else {
updateViewVisibilities(visibleType);
updateBackgroundColor(false);
}
}
private void updateBackgroundTransformation(float transformationAmount) {
int endColor = getBackgroundColor(mVisibleType);
int startColor = getBackgroundColor(mTransformationStartVisibleType);
if (endColor != startColor) {
if (startColor == 0) {
startColor = mContainingNotification.getBackgroundColorWithoutTint();
}
if (endColor == 0) {
endColor = mContainingNotification.getBackgroundColorWithoutTint();
}
endColor = NotificationUtils.interpolateColors(startColor, endColor,
transformationAmount);
}
mContainingNotification.setContentBackground(endColor, false, this);
}
private float calculateTransformationAmount() {
int startHeight = getViewForVisibleType(mTransformationStartVisibleType).getHeight();
int endHeight = getViewForVisibleType(mVisibleType).getHeight();
@@ -457,9 +477,24 @@ public class NotificationContentView extends FrameLayout {
updateViewVisibilities(visibleType);
}
mVisibleType = visibleType;
updateBackgroundColor(animate);
}
}
public void updateBackgroundColor(boolean animate) {
int customBackgroundColor = getBackgroundColor(mVisibleType);
mContainingNotification.setContentBackground(customBackgroundColor, animate, this);
}
private int getBackgroundColor(int visibleType) {
NotificationViewWrapper currentVisibleWrapper = getVisibleWrapper(visibleType);
int customBackgroundColor = 0;
if (currentVisibleWrapper != null) {
customBackgroundColor = currentVisibleWrapper.getCustomBackgroundColor();
}
return customBackgroundColor;
}
private void updateViewVisibilities(int visibleType) {
boolean contractedVisible = visibleType == VISIBLE_TYPE_CONTRACTED;
mContractedWrapper.setVisible(contractedVisible);
@@ -530,8 +565,8 @@ public class NotificationContentView extends FrameLayout {
}
}
private NotificationViewWrapper getCurrentVisibleWrapper() {
switch (mVisibleType) {
private NotificationViewWrapper getVisibleWrapper(int visibleType) {
switch (visibleType) {
case VISIBLE_TYPE_EXPANDED:
return mExpandedWrapper;
case VISIBLE_TYPE_HEADSUP:
@@ -606,7 +641,6 @@ public class NotificationContentView extends FrameLayout {
return;
}
mDark = dark;
dark = dark && !mShowingLegacyBackground;
if (mVisibleType == VISIBLE_TYPE_CONTRACTED || !dark) {
mContractedWrapper.setDark(dark, fade, delay);
}
@@ -637,6 +671,19 @@ public class NotificationContentView extends FrameLayout {
public void setShowingLegacyBackground(boolean showing) {
mShowingLegacyBackground = showing;
updateShowingLegacyBackground();
}
private void updateShowingLegacyBackground() {
if (mContractedChild != null) {
mContractedWrapper.setShowingLegacyBackground(mShowingLegacyBackground);
}
if (mExpandedChild != null) {
mExpandedWrapper.setShowingLegacyBackground(mShowingLegacyBackground);
}
if (mHeadsUpChild != null) {
mHeadsUpWrapper.setShowingLegacyBackground(mShowingLegacyBackground);
}
}
public void setIsChildInGroup(boolean isChildInGroup) {
@@ -658,6 +705,7 @@ public class NotificationContentView extends FrameLayout {
if (mHeadsUpChild != null) {
mHeadsUpWrapper.notifyContentUpdated(entry.notification);
}
updateShowingLegacyBackground();
selectLayout(false /* animate */, true /* force */);
setDark(mDark, false /* animate */, 0 /* delay */);
}
@@ -779,7 +827,7 @@ public class NotificationContentView extends FrameLayout {
}
public NotificationHeaderView getVisibleNotificationHeader() {
NotificationViewWrapper wrapper = getCurrentVisibleWrapper();
NotificationViewWrapper wrapper = getVisibleWrapper(mVisibleType);
return wrapper == null ? null : wrapper.getNotificationHeader();
}
@@ -807,6 +855,7 @@ public class NotificationContentView extends FrameLayout {
mTransformationStartVisibleType = UNDEFINED;
mVisibleType = calculateVisibleType();
updateViewVisibilities(mVisibleType);
updateBackgroundColor(false);
}
}
}

View File

@@ -16,8 +16,19 @@
package com.android.systemui.statusbar.notification;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.graphics.Color;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.service.notification.StatusBarNotification;
import android.support.v4.graphics.ColorUtils;
import android.view.View;
import com.android.systemui.R;
import com.android.systemui.ViewInvertHelper;
import com.android.systemui.statusbar.phone.NotificationPanelView;
@@ -27,6 +38,11 @@ import com.android.systemui.statusbar.phone.NotificationPanelView;
public class NotificationCustomViewWrapper extends NotificationViewWrapper {
private final ViewInvertHelper mInvertHelper;
private final Paint mGreyPaint = new Paint();
private int mBackgroundColor = 0;
private static final int CUSTOM_BACKGROUND_TAG = R.id.custom_background_color;
private boolean mShouldInvertDark;
private boolean mShowingLegacyBackground;
protected NotificationCustomViewWrapper(View view) {
super(view);
@@ -39,10 +55,46 @@ public class NotificationCustomViewWrapper extends NotificationViewWrapper {
return;
}
super.setDark(dark, fade, delay);
if (fade) {
mInvertHelper.fade(dark, delay);
if (!mShowingLegacyBackground && mShouldInvertDark) {
if (fade) {
mInvertHelper.fade(dark, delay);
} else {
mInvertHelper.update(dark);
}
} else {
mInvertHelper.update(dark);
mView.setLayerType(dark ? View.LAYER_TYPE_HARDWARE : View.LAYER_TYPE_NONE, null);
if (fade) {
fadeGrayscale(dark, delay);
} else {
updateGrayscale(dark);
}
}
}
protected void fadeGrayscale(final boolean dark, long delay) {
startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
updateGrayscaleMatrix((float) animation.getAnimatedValue());
mGreyPaint.setColorFilter(new ColorMatrixColorFilter(mGrayscaleColorMatrix));
mView.setLayerPaint(mGreyPaint);
}
}, dark, delay, new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
if (!dark) {
mView.setLayerType(View.LAYER_TYPE_NONE, null);
}
}
});
}
protected void updateGrayscale(boolean dark) {
if (dark) {
updateGrayscaleMatrix(1f);
mGreyPaint.setColorFilter(
new ColorMatrixColorFilter(mGrayscaleColorMatrix));
mView.setLayerPaint(mGreyPaint);
}
}
@@ -51,4 +103,35 @@ public class NotificationCustomViewWrapper extends NotificationViewWrapper {
super.setVisible(visible);
mView.setAlpha(visible ? 1.0f : 0.0f);
}
@Override
public void notifyContentUpdated(StatusBarNotification notification) {
super.notifyContentUpdated(notification);
Drawable background = mView.getBackground();
mBackgroundColor = 0;
if (background instanceof ColorDrawable) {
mBackgroundColor = ((ColorDrawable) background).getColor();
mView.setBackground(null);
mView.setTag(CUSTOM_BACKGROUND_TAG, mBackgroundColor);
} else if (mView.getTag(CUSTOM_BACKGROUND_TAG) != null) {
mBackgroundColor = (int) mView.getTag(CUSTOM_BACKGROUND_TAG);
}
mShouldInvertDark = mBackgroundColor == 0 || isColorLight(mBackgroundColor);
}
private boolean isColorLight(int backgroundColor) {
return Color.alpha(backgroundColor) == 0
|| ColorUtils.calculateLuminance(backgroundColor) > 0.5;
}
@Override
public int getCustomBackgroundColor() {
return mBackgroundColor;
}
@Override
public void setShowingLegacyBackground(boolean showing) {
super.setShowingLegacyBackground(showing);
mShowingLegacyBackground = showing;
}
}

View File

@@ -47,7 +47,6 @@ import java.util.Stack;
*/
public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
private final ColorMatrix mGrayscaleColorMatrix = new ColorMatrix();
private final PorterDuffColorFilter mIconColorFilter = new PorterDuffColorFilter(
0, PorterDuff.Mode.SRC_ATOP);
private final int mIconDarkAlpha;
@@ -178,21 +177,6 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
}
}
protected void startIntensityAnimation(ValueAnimator.AnimatorUpdateListener updateListener,
boolean dark, long delay, Animator.AnimatorListener listener) {
float startIntensity = dark ? 0f : 1f;
float endIntensity = dark ? 1f : 0f;
ValueAnimator animator = ValueAnimator.ofFloat(startIntensity, endIntensity);
animator.addUpdateListener(updateListener);
animator.setDuration(NotificationPanelView.DOZE_ANIMATION_DURATION);
animator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
animator.setStartDelay(delay);
if (listener != null) {
animator.addListener(listener);
}
animator.start();
}
private void fadeIconColorFilter(final ImageView target, boolean dark, long delay) {
startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
@Override
@@ -264,10 +248,6 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
mNotificationHeader.setOnClickListener(expandable ? onClickListener : null);
}
private void updateGrayscaleMatrix(float intensity) {
mGrayscaleColorMatrix.setSaturation(1 - intensity);
}
private static int interpolateColor(int source, int target, float t) {
int aSource = Color.alpha(source);
int rSource = Color.red(source);

View File

@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification;
import android.graphics.Color;
import android.widget.ImageView;
import com.android.internal.util.NotificationColorUtil;
@@ -38,4 +39,12 @@ public class NotificationUtils {
public static float interpolate(float start, float end, float amount) {
return start * (1.0f - amount) + end * amount;
}
public static int interpolateColors(int startColor, int endColor, float amount) {
return Color.argb(
(int) interpolate(Color.alpha(startColor), Color.alpha(endColor), amount),
(int) interpolate(Color.red(startColor), Color.red(endColor), amount),
(int) interpolate(Color.green(startColor), Color.green(endColor), amount),
(int) interpolate(Color.blue(startColor), Color.blue(endColor), amount));
}
}

View File

@@ -16,13 +16,19 @@
package com.android.systemui.statusbar.notification;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.ColorMatrix;
import android.service.notification.StatusBarNotification;
import android.view.NotificationHeaderView;
import android.view.View;
import com.android.systemui.Interpolators;
import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.statusbar.NotificationContentView;
import com.android.systemui.statusbar.TransformableView;
import com.android.systemui.statusbar.phone.NotificationPanelView;
/**
* Wraps the actual notification content view; used to implement behaviors which are different for
@@ -30,6 +36,7 @@ import com.android.systemui.statusbar.TransformableView;
*/
public abstract class NotificationViewWrapper implements TransformableView {
protected final ColorMatrix mGrayscaleColorMatrix = new ColorMatrix();
protected final View mView;
protected boolean mDark;
protected boolean mDarkInitialized = false;
@@ -75,6 +82,26 @@ public abstract class NotificationViewWrapper implements TransformableView {
mDarkInitialized = false;
};
protected void startIntensityAnimation(ValueAnimator.AnimatorUpdateListener updateListener,
boolean dark, long delay, Animator.AnimatorListener listener) {
float startIntensity = dark ? 0f : 1f;
float endIntensity = dark ? 1f : 0f;
ValueAnimator animator = ValueAnimator.ofFloat(startIntensity, endIntensity);
animator.addUpdateListener(updateListener);
animator.setDuration(NotificationPanelView.DOZE_ANIMATION_DURATION);
animator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
animator.setStartDelay(delay);
if (listener != null) {
animator.addListener(listener);
}
animator.start();
}
protected void updateGrayscaleMatrix(float intensity) {
mGrayscaleColorMatrix.setSaturation(1 - intensity);
}
/**
* Update the appearance of the expand button.
*
@@ -122,4 +149,11 @@ public abstract class NotificationViewWrapper implements TransformableView {
mView.animate().cancel();
mView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
}
public int getCustomBackgroundColor() {
return 0;
}
public void setShowingLegacyBackground(boolean showing) {
}
}