am bf1960ce: Merge "Fade notifications when exiting dozing" into lmp-mr1-dev

automerge: 6456513

* commit '6456513f6ff844fd15f3453a66fcc8fb439e1ef7':
  Fade notifications when exiting dozing
This commit is contained in:
Jorim Jaggi
2014-11-20 21:31:41 +00:00
committed by android-build-merger
21 changed files with 599 additions and 90 deletions

View File

@@ -28,6 +28,7 @@
android:layout_height="@dimen/notification_large_icon_height"
/>
<LinearLayout
android:id="@+id/notification_main_column"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="top"

View File

@@ -28,6 +28,7 @@
android:layout_height="@dimen/notification_large_icon_height"
/>
<LinearLayout
android:id="@+id/notification_main_column"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"

View File

@@ -28,6 +28,7 @@
android:layout_height="@dimen/notification_large_icon_height"
/>
<LinearLayout
android:id="@+id/notification_main_column"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"

View File

@@ -28,6 +28,7 @@
android:layout_height="@dimen/notification_large_icon_height"
/>
<LinearLayout
android:id="@+id/notification_main_column"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"

View File

@@ -202,6 +202,7 @@
<java-symbol type="id" name="status_bar_latest_event_content" />
<java-symbol type="id" name="action_divider" />
<java-symbol type="id" name="overflow_divider" />
<java-symbol type="id" name="notification_main_column" />
<java-symbol type="id" name="sms_short_code_confirm_message" />
<java-symbol type="id" name="sms_short_code_detail_layout" />
<java-symbol type="id" name="sms_short_code_detail_message" />

View File

@@ -32,6 +32,7 @@
/>
<LinearLayout
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView

View File

@@ -0,0 +1,101 @@
/*
* Copyright (C) 2014 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;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
import android.view.View;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import com.android.systemui.statusbar.phone.NotificationPanelView;
/**
* Helper to invert the colors of views and fade between the states.
*/
public class ViewInvertHelper {
private final Paint mDarkPaint = new Paint();
private final Interpolator mLinearOutSlowInInterpolator;
private final View mTarget;
private final ColorMatrix mMatrix = new ColorMatrix();
private final ColorMatrix mGrayscaleMatrix = new ColorMatrix();
private final long mFadeDuration;
public ViewInvertHelper(View target, long fadeDuration) {
mTarget = target;
mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(mTarget.getContext(),
android.R.interpolator.linear_out_slow_in);
mFadeDuration = fadeDuration;
}
public void fade(final boolean invert, long delay) {
float startIntensity = invert ? 0f : 1f;
float endIntensity = invert ? 1f : 0f;
ValueAnimator animator = ValueAnimator.ofFloat(startIntensity, endIntensity);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
updateInvertPaint((Float) animation.getAnimatedValue());
mTarget.setLayerType(View.LAYER_TYPE_HARDWARE, mDarkPaint);
}
});
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
if (!invert) {
mTarget.setLayerType(View.LAYER_TYPE_NONE, null);
}
}
});
animator.setDuration(mFadeDuration);
animator.setInterpolator(mLinearOutSlowInInterpolator);
animator.setStartDelay(delay);
animator.start();
}
public void update(boolean invert) {
if (invert) {
updateInvertPaint(1f);
mTarget.setLayerType(View.LAYER_TYPE_HARDWARE, mDarkPaint);
} else {
mTarget.setLayerType(View.LAYER_TYPE_NONE, null);
}
}
public View getTarget() {
return mTarget;
}
private void updateInvertPaint(float intensity) {
float components = 1 - 2 * intensity;
final float[] invert = {
components, 0f, 0f, 0f, 255f * intensity,
0f, components, 0f, 0f, 255f * intensity,
0f, 0f, components, 0f, 255f * intensity,
0f, 0f, 0f, 1f, 0f
};
mMatrix.set(invert);
mGrayscaleMatrix.setSaturation(1 - intensity);
mMatrix.preConcat(mGrayscaleMatrix);
mDarkPaint.setColorFilter(new ColorMatrixColorFilter(mMatrix));
}
}

View File

@@ -43,6 +43,7 @@ import android.view.animation.LinearInterpolator;
import android.view.animation.PathInterpolator;
import com.android.systemui.R;
import com.android.systemui.statusbar.phone.NotificationPanelView;
/**
* Base class for both {@link ExpandableNotificationRow} and {@link NotificationOverflowContainer}
@@ -53,6 +54,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
private static final long DOUBLETAP_TIMEOUT_MS = 1200;
private static final int BACKGROUND_ANIMATION_LENGTH_MS = 220;
private static final int ACTIVATE_ANIMATION_LENGTH = 220;
private static final int DARK_ANIMATION_LENGTH = 170;
/**
* The amount of width, which is kept in the end when performing a disappear animation (also
@@ -84,6 +86,11 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
*/
private static final float VERTICAL_ANIMATION_START = 1.0f;
/**
* Scale for the background to animate from when exiting dark mode.
*/
private static final float DARK_EXIT_SCALE_START = 0.93f;
private static final Interpolator ACTIVATE_INVERSE_INTERPOLATOR
= new PathInterpolator(0.6f, 0, 0.5f, 1);
private static final Interpolator ACTIVATE_INVERSE_ALPHA_INTERPOLATOR
@@ -94,7 +101,6 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
private boolean mDimmed;
private boolean mDark;
private final Paint mDarkPaint = createDarkPaint();
private int mBgTint = 0;
private final int mRoundedRectCornerRadius;
@@ -332,40 +338,32 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
if (mDimmed != dimmed) {
mDimmed = dimmed;
if (fade) {
fadeBackground();
fadeDimmedBackground();
} else {
updateBackground();
}
}
}
public void setDark(boolean dark, boolean fade) {
// TODO implement fade
if (mDark != dark) {
mDark = dark;
if (mDark) {
setLayerType(View.LAYER_TYPE_HARDWARE, mDarkPaint);
} else {
setLayerType(View.LAYER_TYPE_NONE, null);
}
public void setDark(boolean dark, boolean fade, long delay) {
super.setDark(dark, fade, delay);
if (mDark == dark) {
return;
}
}
private static Paint createDarkPaint() {
final Paint p = new Paint();
final float[] invert = {
-1f, 0f, 0f, 1f, 1f,
0f, -1f, 0f, 1f, 1f,
0f, 0f, -1f, 1f, 1f,
0f, 0f, 0f, 1f, 0f
};
final ColorMatrix m = new ColorMatrix(invert);
final ColorMatrix grayscale = new ColorMatrix();
grayscale.setSaturation(0);
m.preConcat(grayscale);
p.setColorFilter(new ColorMatrixColorFilter(m));
return p;
}
mDark = dark;
if (!dark && fade) {
if (mActivated) {
mBackgroundDimmed.setVisibility(View.VISIBLE);
mBackgroundNormal.setVisibility(View.VISIBLE);
} else {
mBackgroundDimmed.setVisibility(View.VISIBLE);
mBackgroundNormal.setVisibility(View.INVISIBLE);
}
fadeDarkToDimmed(delay);
} else {
updateBackground();
}
}
public void setShowingLegacyBackground(boolean showing) {
mShowingLegacyBackground = showing;
@@ -402,7 +400,39 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
mBackgroundNormal.setRippleColor(rippleColor);
}
private void fadeBackground() {
/**
* Fades the dimmed background when exiting dark mode.
*/
private void fadeDarkToDimmed(long delay) {
mBackgroundDimmed.setAlpha(0f);
mBackgroundDimmed.setPivotX(mBackgroundDimmed.getWidth() / 2f);
mBackgroundDimmed.setPivotY(getActualHeight() / 2f);
mBackgroundDimmed.setScaleX(DARK_EXIT_SCALE_START);
mBackgroundDimmed.setScaleY(DARK_EXIT_SCALE_START);
mBackgroundDimmed.animate()
.alpha(1f)
.scaleX(1f)
.scaleY(1f)
.setDuration(DARK_ANIMATION_LENGTH)
.setStartDelay(delay)
.setInterpolator(mLinearOutSlowInInterpolator)
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationCancel(Animator animation) {
// Jump state if we are cancelled
mBackgroundDimmed.setScaleX(1f);
mBackgroundDimmed.setScaleY(1f);
mBackgroundDimmed.setAlpha(1f);
}
})
.start();
}
/**
* Fades the background when the dimmed state changes.
*/
private void fadeDimmedBackground() {
mBackgroundDimmed.animate().cancel();
mBackgroundNormal.animate().cancel();
if (mDimmed) {
mBackgroundDimmed.setVisibility(View.VISIBLE);
@@ -443,11 +473,14 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
}
private void updateBackground() {
if (mDimmed) {
cancelFadeAnimations();
if (mDark) {
mBackgroundDimmed.setVisibility(View.INVISIBLE);
mBackgroundNormal.setVisibility(View.INVISIBLE);
} else if (mDimmed) {
mBackgroundDimmed.setVisibility(View.VISIBLE);
mBackgroundNormal.setVisibility(View.INVISIBLE);
} else {
cancelFadeAnimations();
mBackgroundDimmed.setVisibility(View.INVISIBLE);
mBackgroundNormal.setVisibility(View.VISIBLE);
mBackgroundNormal.setAlpha(1f);
@@ -459,6 +492,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
if (mBackgroundAnimator != null) {
mBackgroundAnimator.cancel();
}
mBackgroundDimmed.animate().cancel();
mBackgroundNormal.animate().cancel();
}

View File

@@ -207,11 +207,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
}
@Override
public void setDark(boolean dark, boolean fade) {
super.setDark(dark, fade);
public void setDark(boolean dark, boolean fade, long delay) {
super.setDark(dark, fade, delay);
final NotificationContentView showing = getShowingLayout();
if (showing != null) {
showing.setDark(dark, fade);
showing.setDark(dark, fade, delay);
}
}

View File

@@ -39,6 +39,7 @@ public abstract class ExpandableView extends FrameLayout {
private int mActualHeight;
protected int mClipTopAmount;
private boolean mActualHeightInitialized;
private boolean mDark;
private ArrayList<View> mMatchParentViews = new ArrayList<View>();
public ExpandableView(Context context, AttributeSet attrs) {
@@ -185,8 +186,14 @@ public abstract class ExpandableView extends FrameLayout {
*
* @param dark Whether the notification should be dark.
* @param fade Whether an animation should be played to change the state.
* @param delay If fading, the delay of the animation.
*/
public void setDark(boolean dark, boolean fade) {
public void setDark(boolean dark, boolean fade, long delay) {
mDark = dark;
}
public boolean isDark() {
return mDark;
}
/**

View File

@@ -42,14 +42,14 @@ import com.android.systemui.R;
public class NotificationContentView extends FrameLayout {
private static final long ANIMATION_DURATION_LENGTH = 170;
private static final Paint INVERT_PAINT = createInvertPaint();
private static final ColorFilter NO_COLOR_FILTER = new ColorFilter();
private final Rect mClipBounds = new Rect();
private View mContractedChild;
private View mExpandedChild;
private NotificationViewWrapper mContractedWrapper;
private int mSmallHeight;
private int mClipTopAmount;
private int mActualHeight;
@@ -122,6 +122,7 @@ public class NotificationContentView extends FrameLayout {
sanitizeContractedLayoutParams(child);
addView(child);
mContractedChild = child;
mContractedWrapper = NotificationViewWrapper.wrap(getContext(), child);
selectLayout(false /* animate */, true /* force */);
}
@@ -249,38 +250,10 @@ public class NotificationContentView extends FrameLayout {
return mExpandedChild != null;
}
public void setDark(boolean dark, boolean fade) {
public void setDark(boolean dark, boolean fade, long delay) {
if (mDark == dark || mContractedChild == null) return;
mDark = dark;
setImageViewDark(dark, fade, com.android.internal.R.id.right_icon);
setImageViewDark(dark, fade, com.android.internal.R.id.icon);
}
private void setImageViewDark(boolean dark, boolean fade, int imageViewId) {
// TODO: implement fade
final ImageView v = (ImageView) mContractedChild.findViewById(imageViewId);
if (v == null) return;
final Drawable d = v.getBackground();
if (dark) {
v.setLayerType(LAYER_TYPE_HARDWARE, INVERT_PAINT);
if (d != null) {
v.setTag(R.id.doze_saved_filter_tag, d.getColorFilter() != null ? d.getColorFilter()
: NO_COLOR_FILTER);
d.setColorFilter(getResources().getColor(R.color.doze_small_icon_background_color),
PorterDuff.Mode.SRC_ATOP);
v.setImageAlpha(getResources().getInteger(R.integer.doze_small_icon_alpha));
}
} else {
v.setLayerType(LAYER_TYPE_NONE, null);
if (d != null) {
final ColorFilter filter = (ColorFilter) v.getTag(R.id.doze_saved_filter_tag);
if (filter != null) {
d.setColorFilter(filter == NO_COLOR_FILTER ? null : filter);
v.setTag(R.id.doze_saved_filter_tag, null);
}
v.setImageAlpha(0xff);
}
}
mContractedWrapper.setDark(dark, fade, delay);
}
@Override
@@ -290,16 +263,4 @@ public class NotificationContentView extends FrameLayout {
// layout, and saves us some layers.
return false;
}
private static Paint createInvertPaint() {
final Paint p = new Paint();
final float[] invert = {
-1f, 0f, 0f, 1f, 1f,
0f, -1f, 0f, 1f, 1f,
0f, 0f, -1f, 1f, 1f,
0f, 0f, 0f, 1f, 0f
};
p.setColorFilter(new ColorMatrixColorFilter(new ColorMatrix(invert)));
return p;
}
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright (C) 2014 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.view.View;
import com.android.systemui.ViewInvertHelper;
import com.android.systemui.statusbar.phone.NotificationPanelView;
/**
* Wraps a notification containing a custom view.
*/
public class NotificationCustomViewWrapper extends NotificationViewWrapper {
private final ViewInvertHelper mInvertHelper;
private boolean mDark;
protected NotificationCustomViewWrapper(View view) {
super(view);
mInvertHelper = new ViewInvertHelper(view, NotificationPanelView.DOZE_ANIMATION_DURATION);
}
@Override
public void setDark(boolean dark, boolean fade, long delay) {
if (mDark != dark) {
mDark = dark;
if (fade) {
mInvertHelper.fade(dark, delay);
} else {
mInvertHelper.update(dark);
}
}
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright (C) 2014 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.view.View;
/**
* Wraps a media notification.
*/
public class NotificationMediaViewWrapper extends NotificationTemplateViewWrapper {
private boolean mDark;
protected NotificationMediaViewWrapper(Context ctx, View view) {
super(ctx, view);
}
@Override
public void setDark(boolean dark, boolean fade, long delay) {
if (mDark != dark) {
mDark = dark;
// Only update the large icon, because the rest is already inverted.
setPictureGrayscale(dark, fade, delay);
}
}
}

View File

@@ -21,6 +21,8 @@ import android.util.AttributeSet;
import android.widget.TextView;
import com.android.systemui.R;
import com.android.systemui.ViewInvertHelper;
import com.android.systemui.statusbar.phone.NotificationPanelView;
/**
* Container view for overflowing notification icons on Keyguard.
@@ -28,6 +30,8 @@ import com.android.systemui.R;
public class NotificationOverflowContainer extends ActivatableNotificationView {
private NotificationOverflowIconsView mIconsView;
private ViewInvertHelper mViewInvertHelper;
private boolean mDark;
public NotificationOverflowContainer(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -39,6 +43,20 @@ public class NotificationOverflowContainer extends ActivatableNotificationView {
mIconsView = (NotificationOverflowIconsView) findViewById(R.id.overflow_icons_view);
mIconsView.setMoreText((TextView) findViewById(R.id.more_text));
mIconsView.setOverflowIndicator(findViewById(R.id.more_icon_overflow));
mViewInvertHelper = new ViewInvertHelper(findViewById(R.id.content),
NotificationPanelView.DOZE_ANIMATION_DURATION);
}
@Override
public void setDark(boolean dark, boolean fade, long delay) {
super.setDark(dark, fade, delay);
if (mDark == dark) return;
mDark = dark;
if (fade) {
mViewInvertHelper.fade(dark, delay);
} else {
mViewInvertHelper.update(dark);
}
}
public NotificationOverflowIconsView getIconsView() {

View File

@@ -0,0 +1,223 @@
/*
* Copyright (C) 2014 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.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.view.View;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.ImageView;
import com.android.systemui.R;
import com.android.systemui.ViewInvertHelper;
import com.android.systemui.statusbar.phone.NotificationPanelView;
/**
* Wraps a notification view inflated from a template.
*/
public class NotificationTemplateViewWrapper extends NotificationViewWrapper {
private final ViewInvertHelper mInvertHelper;
private final ImageView mIcon;
protected final ImageView mPicture;
private final ColorMatrix mGrayscaleColorMatrix = new ColorMatrix();
private final PorterDuffColorFilter mIconColorFilter = new PorterDuffColorFilter(
0, PorterDuff.Mode.SRC_ATOP);
private final int mIconDarkAlpha;
private final int mIconBackgroundColor;
private final int mIconBackgroundDarkColor;
private final Interpolator mLinearOutSlowInInterpolator;
private boolean mDark;
protected NotificationTemplateViewWrapper(Context ctx, View view) {
super(view);
mIconDarkAlpha = ctx.getResources().getInteger(R.integer.doze_small_icon_alpha);
mIconBackgroundDarkColor =
ctx.getResources().getColor(R.color.doze_small_icon_background_color);
mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(ctx,
android.R.interpolator.linear_out_slow_in);
View mainColumn = view.findViewById(com.android.internal.R.id.notification_main_column);
mInvertHelper = mainColumn != null
? new ViewInvertHelper(mainColumn, NotificationPanelView.DOZE_ANIMATION_DURATION)
: null;
ImageView largeIcon = (ImageView) view.findViewById(com.android.internal.R.id.icon);
ImageView rightIcon = (ImageView) view.findViewById(com.android.internal.R.id.right_icon);
mIcon = resolveIcon(largeIcon, rightIcon);
mPicture = resolvePicture(largeIcon);
mIconBackgroundColor = resolveBackgroundColor(mIcon);
}
private ImageView resolveIcon(ImageView largeIcon, ImageView rightIcon) {
return largeIcon != null && largeIcon.getBackground() != null ? largeIcon
: rightIcon != null && rightIcon.getBackground() != null ? rightIcon
: null;
}
private ImageView resolvePicture(ImageView largeIcon) {
return largeIcon != null && largeIcon.getBackground() == null
? largeIcon
: null;
}
private int resolveBackgroundColor(ImageView icon) {
if (icon != null && icon.getBackground() != null) {
ColorFilter filter = icon.getBackground().getColorFilter();
if (filter instanceof PorterDuffColorFilter) {
return ((PorterDuffColorFilter) filter).getColor();
}
}
return 0;
}
@Override
public void setDark(boolean dark, boolean fade, long delay) {
if (mDark != dark) {
mDark = dark;
if (mInvertHelper != null) {
if (fade) {
mInvertHelper.fade(dark, delay);
} else {
mInvertHelper.update(dark);
}
}
if (mIcon != null) {
if (fade) {
fadeIconColorFilter(mIcon, dark, delay);
fadeIconAlpha(mIcon, dark, delay);
} else {
updateIconColorFilter(mIcon, dark);
updateIconAlpha(mIcon, dark);
}
}
setPictureGrayscale(dark, fade, delay);
}
}
protected void setPictureGrayscale(boolean grayscale, boolean fade, long delay) {
if (mPicture != null) {
if (fade) {
fadeGrayscale(mPicture, grayscale, delay);
} else {
updateGrayscale(mPicture, grayscale);
}
}
}
private 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(mLinearOutSlowInInterpolator);
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
public void onAnimationUpdate(ValueAnimator animation) {
updateIconColorFilter(target, (Float) animation.getAnimatedValue());
}
}, dark, delay, null /* listener */);
}
private void fadeIconAlpha(final ImageView target, boolean dark, long delay) {
startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float t = (float) animation.getAnimatedValue();
target.setImageAlpha((int) (255 * (1f - t) + mIconDarkAlpha * t));
}
}, dark, delay, null /* listener */);
}
protected void fadeGrayscale(final ImageView target, final boolean dark, long delay) {
startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
updateGrayscaleMatrix((float) animation.getAnimatedValue());
target.setColorFilter(new ColorMatrixColorFilter(mGrayscaleColorMatrix));
}
}, dark, delay, new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
if (!dark) {
target.setColorFilter(null);
}
}
});
}
private void updateIconColorFilter(ImageView target, boolean dark) {
updateIconColorFilter(target, dark ? 1f : 0f);
}
private void updateIconColorFilter(ImageView target, float intensity) {
int color = interpolateColor(mIconBackgroundColor, mIconBackgroundDarkColor, intensity);
mIconColorFilter.setColor(color);
target.getBackground().mutate().setColorFilter(mIconColorFilter);
}
private void updateIconAlpha(ImageView target, boolean dark) {
target.setImageAlpha(dark ? mIconDarkAlpha : 255);
}
protected void updateGrayscale(ImageView target, boolean dark) {
if (dark) {
updateGrayscaleMatrix(1f);
target.setColorFilter(new ColorMatrixColorFilter(mGrayscaleColorMatrix));
} else {
target.setColorFilter(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);
int gSource = Color.green(source);
int bSource = Color.blue(source);
int aTarget = Color.alpha(target);
int rTarget = Color.red(target);
int gTarget = Color.green(target);
int bTarget = Color.blue(target);
return Color.argb(
(int) (aSource * (1f - t) + aTarget * t),
(int) (rSource * (1f - t) + rTarget * t),
(int) (gSource * (1f - t) + gTarget * t),
(int) (bSource * (1f - t) + bTarget * t));
}
}

View File

@@ -0,0 +1,56 @@
/*
* Copyright (C) 2014 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.view.View;
import com.android.internal.R;
/**
* Wraps the actual notification content view; used to implement behaviors which are different for
* the individual templates and custom views.
*/
public abstract class NotificationViewWrapper {
protected final View mView;
public static NotificationViewWrapper wrap(Context ctx, View v) {
// TODO: Figure out a better way to find out which template the view is.
if (v.findViewById(com.android.internal.R.id.media_actions) != null) {
return new NotificationMediaViewWrapper(ctx, v);
} else if (v.getId() == com.android.internal.R.id.status_bar_latest_event_content) {
return new NotificationTemplateViewWrapper(ctx, v);
} else {
return new NotificationCustomViewWrapper(v);
}
}
protected NotificationViewWrapper(View view) {
mView = view;
}
/**
* In dark mode, we draw as little as possible, assuming a black background.
*
* @param dark whether we should display ourselves in dark mode
* @param fade whether to animate the transition if the mode changes
* @param delay if fading, the delay of the animation
*/
public abstract void setDark(boolean dark, boolean fade, long delay);
}

View File

@@ -3708,14 +3708,11 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
if (mState != StatusBarState.KEYGUARD && !mNotificationPanel.isDozing()) {
return;
}
mNotificationPanel.setDozing(mDozing, mDozeScrimController.isPulsing() /*animate*/);
if (mDozing) {
mStackScroller.setDark(true, false /*animate*/);
} else {
mStackScroller.setDark(false, false /*animate*/);
}
boolean animate = !mDozing && mDozeScrimController.isPulsing();
mNotificationPanel.setDozing(mDozing, animate);
mStackScroller.setDark(mDozing, animate);
mScrimController.setDozing(mDozing);
mDozeScrimController.setDozing(mDozing, mDozeScrimController.isPulsing() /* animate */);
mDozeScrimController.setDozing(mDozing, animate);
}
public void updateStackScrollerState(boolean goingToFullShade) {

View File

@@ -33,6 +33,7 @@ public class AnimationFilter {
boolean animateHideSensitive;
boolean hasDelays;
boolean hasGoToFullShadeEvent;
boolean hasDarkEvent;
public AnimationFilter animateAlpha() {
animateAlpha = true;
@@ -98,6 +99,10 @@ public class AnimationFilter {
NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_GO_TO_FULL_SHADE) {
hasGoToFullShadeEvent = true;
}
if (events.get(i).animationType ==
NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_DARK) {
hasDarkEvent = true;
}
}
}
@@ -126,5 +131,6 @@ public class AnimationFilter {
animateHideSensitive = false;
hasDelays = false;
hasGoToFullShadeEvent = false;
hasDarkEvent = false;
}
}

View File

@@ -2455,7 +2455,8 @@ public class NotificationStackScrollLayout extends ViewGroup
// ANIMATION_TYPE_DARK
new AnimationFilter()
.animateDark(),
.animateDark()
.hasDelays(),
// ANIMATION_TYPE_GO_TO_FULL_SHADE
new AnimationFilter()

View File

@@ -154,7 +154,7 @@ public class StackScrollState {
child.setDimmed(state.dimmed, false /* animate */);
// apply dark
child.setDark(state.dark, false /* animate */);
child.setDark(state.dark, false /* animate */, 0 /* delay */);
// apply hiding sensitive
child.setHideSensitive(

View File

@@ -46,6 +46,7 @@ public class StackStateAnimator {
public static final int ANIMATION_DELAY_PER_ELEMENT_INTERRUPTING = 80;
public static final int ANIMATION_DELAY_PER_ELEMENT_MANUAL = 32;
public static final int ANIMATION_DELAY_PER_ELEMENT_GO_TO_FULL_SHADE = 48;
public static final int ANIMATION_DELAY_PER_ELEMENT_DARK = 24;
private static final int DELAY_EFFECT_MAX_INDEX_DIFFERENCE = 2;
private static final int TAG_ANIMATOR_TRANSLATION_Y = R.id.translation_y_animator_tag;
@@ -161,11 +162,12 @@ public class StackStateAnimator {
boolean scaleChanging = child.getScaleX() != viewState.scale;
boolean alphaChanging = alpha != child.getAlpha();
boolean heightChanging = viewState.height != child.getActualHeight();
boolean darkChanging = viewState.dark != child.isDark();
boolean topInsetChanging = viewState.clipTopAmount != child.getClipTopAmount();
boolean wasAdded = mNewAddChildren.contains(child);
boolean hasDelays = mAnimationFilter.hasDelays;
boolean isDelayRelevant = yTranslationChanging || zTranslationChanging || scaleChanging ||
alphaChanging || heightChanging || topInsetChanging;
alphaChanging || heightChanging || topInsetChanging || darkChanging;
boolean noAnimation = wasAdded;
long delay = 0;
long duration = mCurrentLength;
@@ -242,7 +244,7 @@ public class StackStateAnimator {
&& !noAnimation);
// start dark animation
child.setDark(viewState.dark, mAnimationFilter.animateDark && !noAnimation);
child.setDark(viewState.dark, mAnimationFilter.animateDark && !noAnimation, delay);
// apply speed bump state
child.setBelowSpeedBump(viewState.belowSpeedBump);
@@ -262,6 +264,9 @@ public class StackStateAnimator {
private long calculateChildAnimationDelay(StackScrollState.ViewState viewState,
StackScrollState finalState) {
if (mAnimationFilter.hasDarkEvent) {
return calculateDelayDark(viewState);
}
if (mAnimationFilter.hasGoToFullShadeEvent) {
return calculateDelayGoToFullShade(viewState);
}
@@ -309,6 +314,10 @@ public class StackStateAnimator {
return minDelay;
}
private long calculateDelayDark(StackScrollState.ViewState viewState) {
return viewState.notGoneIndex * ANIMATION_DELAY_PER_ELEMENT_DARK;
}
private long calculateDelayGoToFullShade(StackScrollState.ViewState viewState) {
float index = viewState.notGoneIndex;
index = (float) Math.pow(index, 0.7f);