summaryrefslogtreecommitdiffstats
path: root/packages
diff options
context:
space:
mode:
authorJorim Jaggi <jjaggi@google.com>2014-11-17 19:14:04 +0100
committerJorim Jaggi <jjaggi@google.com>2014-11-19 16:52:43 +0100
commit4e857f4ef0357e05806819d0488a73a12208fe8f (patch)
tree70dcb8e48cd765ec22b4b2e2a46ab265a9c214f4 /packages
parent1ca6af34b48b18b8c4b5e2cdf3f1bd4295d8ac2e (diff)
downloadframeworks_base-4e857f4ef0357e05806819d0488a73a12208fe8f.zip
frameworks_base-4e857f4ef0357e05806819d0488a73a12208fe8f.tar.gz
frameworks_base-4e857f4ef0357e05806819d0488a73a12208fe8f.tar.bz2
Fade notifications when exiting dozing
Bug: 18146441 Change-Id: Ifa0cbb8157c65768c3a0c584166ca88c3016b730
Diffstat (limited to 'packages')
-rw-r--r--packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml1
-rw-r--r--packages/SystemUI/src/com/android/systemui/ViewInvertHelper.java101
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java92
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java49
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationCustomViewWrapper.java48
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaViewWrapper.java42
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationTemplateViewWrapper.java223
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewWrapper.java56
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java13
16 files changed, 592 insertions, 88 deletions
diff --git a/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml b/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml
index ef85847..4526af5 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml
@@ -32,6 +32,7 @@
/>
<LinearLayout
+ android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
diff --git a/packages/SystemUI/src/com/android/systemui/ViewInvertHelper.java b/packages/SystemUI/src/com/android/systemui/ViewInvertHelper.java
new file mode 100644
index 0000000..2ff8f8a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/ViewInvertHelper.java
@@ -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));
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
index 02b9378..7b60307 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
@@ -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);
+ public void setDark(boolean dark, boolean fade, long delay) {
+ super.setDark(dark, fade, delay);
+ if (mDark == dark) {
+ return;
+ }
+ mDark = dark;
+ if (!dark && fade) {
+ if (mActivated) {
+ mBackgroundDimmed.setVisibility(View.VISIBLE);
+ mBackgroundNormal.setVisibility(View.VISIBLE);
} else {
- setLayerType(View.LAYER_TYPE_NONE, null);
+ mBackgroundDimmed.setVisibility(View.VISIBLE);
+ mBackgroundNormal.setVisibility(View.INVISIBLE);
}
+ fadeDarkToDimmed(delay);
+ } else {
+ updateBackground();
}
- }
-
- 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;
- }
+ }
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();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 7345440..8ad8406 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -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);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
index bf1e78e..ebc663c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
@@ -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;
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index 99214a0..582b8d7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -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;
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationCustomViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationCustomViewWrapper.java
new file mode 100644
index 0000000..045be3e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationCustomViewWrapper.java
@@ -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);
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaViewWrapper.java
new file mode 100644
index 0000000..8f63a79
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaViewWrapper.java
@@ -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);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java
index edfd205..bfa3aa5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java
@@ -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() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationTemplateViewWrapper.java
new file mode 100644
index 0000000..8dc14b0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationTemplateViewWrapper.java
@@ -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));
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewWrapper.java
new file mode 100644
index 0000000..0a02573
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewWrapper.java
@@ -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);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index f5c994a..709a074 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -3689,14 +3689,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) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
index 3c93b19..3d4cda6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
@@ -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;
}
}
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 e63be97..c5f1161 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -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()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
index 4611370..0b1ce8f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
@@ -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(
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 a56440c..05077bf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
@@ -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);