• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16 
17 package com.android.systemui.statusbar;
18 
19 import android.animation.Animator;
20 import android.animation.AnimatorListenerAdapter;
21 import android.animation.ValueAnimator;
22 import android.content.Context;
23 import android.graphics.Color;
24 import android.graphics.ColorFilter;
25 import android.graphics.ColorMatrix;
26 import android.graphics.ColorMatrixColorFilter;
27 import android.graphics.PorterDuff;
28 import android.graphics.PorterDuffColorFilter;
29 import android.graphics.drawable.Drawable;
30 import android.view.View;
31 import android.view.animation.AnimationUtils;
32 import android.view.animation.Interpolator;
33 import android.widget.ImageView;
34 
35 import com.android.systemui.R;
36 import com.android.systemui.ViewInvertHelper;
37 import com.android.systemui.statusbar.phone.NotificationPanelView;
38 
39 /**
40  * Wraps a notification view inflated from a template.
41  */
42 public class NotificationTemplateViewWrapper extends NotificationViewWrapper {
43 
44     private final ColorMatrix mGrayscaleColorMatrix = new ColorMatrix();
45     private final PorterDuffColorFilter mIconColorFilter = new PorterDuffColorFilter(
46             0, PorterDuff.Mode.SRC_ATOP);
47     private final int mIconDarkAlpha;
48     private final int mIconBackgroundDarkColor;
49     private final Interpolator mLinearOutSlowInInterpolator;
50 
51     private int mIconBackgroundColor;
52     private ViewInvertHelper mInvertHelper;
53     private ImageView mIcon;
54     protected ImageView mPicture;
55 
56     /** Whether the icon needs to be forced grayscale when in dark mode. */
57     private boolean mIconForceGraysaleWhenDark;
58 
NotificationTemplateViewWrapper(Context ctx, View view)59     protected NotificationTemplateViewWrapper(Context ctx, View view) {
60         super(view);
61         mIconDarkAlpha = ctx.getResources().getInteger(R.integer.doze_small_icon_alpha);
62         mIconBackgroundDarkColor =
63                 ctx.getResources().getColor(R.color.doze_small_icon_background_color);
64         mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(ctx,
65                 android.R.interpolator.linear_out_slow_in);
66         resolveViews();
67     }
68 
resolveViews()69     private void resolveViews() {
70         View mainColumn = mView.findViewById(com.android.internal.R.id.notification_main_column);
71         mInvertHelper = mainColumn != null
72                 ? new ViewInvertHelper(mainColumn, NotificationPanelView.DOZE_ANIMATION_DURATION)
73                 : null;
74         ImageView largeIcon = (ImageView) mView.findViewById(com.android.internal.R.id.icon);
75         ImageView rightIcon = (ImageView) mView.findViewById(com.android.internal.R.id.right_icon);
76         mIcon = resolveIcon(largeIcon, rightIcon);
77         mPicture = resolvePicture(largeIcon);
78         mIconBackgroundColor = resolveBackgroundColor(mIcon);
79 
80         // If the icon already has a color filter, we assume that we already forced the icon to be
81         // white when we created the notification.
82         mIconForceGraysaleWhenDark = mIcon != null && mIcon.getDrawable().getColorFilter() != null;
83     }
84 
resolveIcon(ImageView largeIcon, ImageView rightIcon)85     private ImageView resolveIcon(ImageView largeIcon, ImageView rightIcon) {
86         return largeIcon != null && largeIcon.getBackground() != null ? largeIcon
87                 : rightIcon != null && rightIcon.getVisibility() == View.VISIBLE ? rightIcon
88                 : null;
89     }
90 
resolvePicture(ImageView largeIcon)91     private ImageView resolvePicture(ImageView largeIcon) {
92         return largeIcon != null && largeIcon.getBackground() == null
93                 ? largeIcon
94                 : null;
95     }
96 
resolveBackgroundColor(ImageView icon)97     private int resolveBackgroundColor(ImageView icon) {
98         if (icon != null && icon.getBackground() != null) {
99             ColorFilter filter = icon.getBackground().getColorFilter();
100             if (filter instanceof PorterDuffColorFilter) {
101                 return ((PorterDuffColorFilter) filter).getColor();
102             }
103         }
104         return 0;
105     }
106 
107     @Override
notifyContentUpdated()108     public void notifyContentUpdated() {
109         super.notifyContentUpdated();
110 
111         // Reinspect the notification.
112         resolveViews();
113     }
114 
115     @Override
setDark(boolean dark, boolean fade, long delay)116     public void setDark(boolean dark, boolean fade, long delay) {
117         if (mInvertHelper != null) {
118             if (fade) {
119                 mInvertHelper.fade(dark, delay);
120             } else {
121                 mInvertHelper.update(dark);
122             }
123         }
124         if (mIcon != null) {
125             if (fade) {
126                 fadeIconColorFilter(mIcon, dark, delay);
127                 fadeIconAlpha(mIcon, dark, delay);
128                 if (!mIconForceGraysaleWhenDark) {
129                     fadeGrayscale(mIcon, dark, delay);
130                 }
131             } else {
132                 updateIconColorFilter(mIcon, dark);
133                 updateIconAlpha(mIcon, dark);
134                 if (!mIconForceGraysaleWhenDark) {
135                     updateGrayscale(mIcon, dark);
136                 }
137             }
138         }
139         setPictureGrayscale(dark, fade, delay);
140     }
141 
setPictureGrayscale(boolean grayscale, boolean fade, long delay)142     protected void setPictureGrayscale(boolean grayscale, boolean fade, long delay) {
143         if (mPicture != null) {
144             if (fade) {
145                 fadeGrayscale(mPicture, grayscale, delay);
146             } else {
147                 updateGrayscale(mPicture, grayscale);
148             }
149         }
150     }
151 
startIntensityAnimation(ValueAnimator.AnimatorUpdateListener updateListener, boolean dark, long delay, Animator.AnimatorListener listener)152     private void startIntensityAnimation(ValueAnimator.AnimatorUpdateListener updateListener,
153             boolean dark, long delay, Animator.AnimatorListener listener) {
154         float startIntensity = dark ? 0f : 1f;
155         float endIntensity = dark ? 1f : 0f;
156         ValueAnimator animator = ValueAnimator.ofFloat(startIntensity, endIntensity);
157         animator.addUpdateListener(updateListener);
158         animator.setDuration(NotificationPanelView.DOZE_ANIMATION_DURATION);
159         animator.setInterpolator(mLinearOutSlowInInterpolator);
160         animator.setStartDelay(delay);
161         if (listener != null) {
162             animator.addListener(listener);
163         }
164         animator.start();
165     }
166 
fadeIconColorFilter(final ImageView target, boolean dark, long delay)167     private void fadeIconColorFilter(final ImageView target, boolean dark, long delay) {
168         startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
169             @Override
170             public void onAnimationUpdate(ValueAnimator animation) {
171                 updateIconColorFilter(target, (Float) animation.getAnimatedValue());
172             }
173         }, dark, delay, null /* listener */);
174     }
175 
fadeIconAlpha(final ImageView target, boolean dark, long delay)176     private void fadeIconAlpha(final ImageView target, boolean dark, long delay) {
177         startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
178             @Override
179             public void onAnimationUpdate(ValueAnimator animation) {
180                 float t = (float) animation.getAnimatedValue();
181                 target.setImageAlpha((int) (255 * (1f - t) + mIconDarkAlpha * t));
182             }
183         }, dark, delay, null /* listener */);
184     }
185 
fadeGrayscale(final ImageView target, final boolean dark, long delay)186     protected void fadeGrayscale(final ImageView target, final boolean dark, long delay) {
187         startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
188             @Override
189             public void onAnimationUpdate(ValueAnimator animation) {
190                 updateGrayscaleMatrix((float) animation.getAnimatedValue());
191                 target.setColorFilter(new ColorMatrixColorFilter(mGrayscaleColorMatrix));
192             }
193         }, dark, delay, new AnimatorListenerAdapter() {
194             @Override
195             public void onAnimationEnd(Animator animation) {
196                 if (!dark) {
197                     target.setColorFilter(null);
198                 }
199             }
200         });
201     }
202 
updateIconColorFilter(ImageView target, boolean dark)203     private void updateIconColorFilter(ImageView target, boolean dark) {
204         updateIconColorFilter(target, dark ? 1f : 0f);
205     }
206 
updateIconColorFilter(ImageView target, float intensity)207     private void updateIconColorFilter(ImageView target, float intensity) {
208         int color = interpolateColor(mIconBackgroundColor, mIconBackgroundDarkColor, intensity);
209         mIconColorFilter.setColor(color);
210         Drawable background = target.getBackground();
211 
212         // The background might be null for legacy notifications. Also, the notification might have
213         // been modified during the animation, so background might be null here.
214         if (background != null) {
215             background.mutate().setColorFilter(mIconColorFilter);
216         }
217     }
218 
updateIconAlpha(ImageView target, boolean dark)219     private void updateIconAlpha(ImageView target, boolean dark) {
220         target.setImageAlpha(dark ? mIconDarkAlpha : 255);
221     }
222 
updateGrayscale(ImageView target, boolean dark)223     protected void updateGrayscale(ImageView target, boolean dark) {
224         if (dark) {
225             updateGrayscaleMatrix(1f);
226             target.setColorFilter(new ColorMatrixColorFilter(mGrayscaleColorMatrix));
227         } else {
228             target.setColorFilter(null);
229         }
230     }
231 
updateGrayscaleMatrix(float intensity)232     private void updateGrayscaleMatrix(float intensity) {
233         mGrayscaleColorMatrix.setSaturation(1 - intensity);
234     }
235 
interpolateColor(int source, int target, float t)236     private static int interpolateColor(int source, int target, float t) {
237         int aSource = Color.alpha(source);
238         int rSource = Color.red(source);
239         int gSource = Color.green(source);
240         int bSource = Color.blue(source);
241         int aTarget = Color.alpha(target);
242         int rTarget = Color.red(target);
243         int gTarget = Color.green(target);
244         int bTarget = Color.blue(target);
245         return Color.argb(
246                 (int) (aSource * (1f - t) + aTarget * t),
247                 (int) (rSource * (1f - t) + rTarget * t),
248                 (int) (gSource * (1f - t) + gTarget * t),
249                 (int) (bSource * (1f - t) + bTarget * t));
250     }
251 }
252