blob: 842bd22cf4cccaafb2f9c2138e8d6e2415c7bec3 [file] [log] [blame]
Selim Cinek9c7712d2015-12-08 19:19:48 -08001/*
2 * Copyright (C) 2015 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
Selim Cinek0ffbda62016-01-01 20:29:12 +010017package com.android.systemui.statusbar.notification;
Selim Cinek9c7712d2015-12-08 19:19:48 -080018
19import android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
21import android.animation.ValueAnimator;
22import android.content.Context;
23import android.graphics.Color;
24import android.graphics.ColorFilter;
25import android.graphics.ColorMatrix;
26import android.graphics.ColorMatrixColorFilter;
27import android.graphics.PorterDuff;
28import android.graphics.PorterDuffColorFilter;
29import android.graphics.drawable.Drawable;
Selim Cinek4ffd6362015-12-29 15:12:23 +010030import android.service.notification.StatusBarNotification;
Selim Cinek9c7712d2015-12-08 19:19:48 -080031import android.view.NotificationHeaderView;
32import android.view.View;
Selim Cinek0ffbda62016-01-01 20:29:12 +010033import android.view.ViewGroup;
Selim Cinek9c7712d2015-12-08 19:19:48 -080034import android.widget.ImageView;
35
Winsonc0d70582016-01-29 10:24:39 -080036import com.android.systemui.Interpolators;
Selim Cinek9c7712d2015-12-08 19:19:48 -080037import com.android.systemui.R;
38import com.android.systemui.ViewInvertHelper;
Selim Cinek0ffbda62016-01-01 20:29:12 +010039import com.android.systemui.statusbar.TransformableView;
40import com.android.systemui.statusbar.ViewTransformationHelper;
Selim Cinek9c7712d2015-12-08 19:19:48 -080041import com.android.systemui.statusbar.phone.NotificationPanelView;
42
Selim Cinek0ffbda62016-01-01 20:29:12 +010043import java.util.Stack;
Selim Cinek9c7712d2015-12-08 19:19:48 -080044
45/**
46 * Wraps a notification header view.
47 */
48public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
49
50 private final ColorMatrix mGrayscaleColorMatrix = new ColorMatrix();
51 private final PorterDuffColorFilter mIconColorFilter = new PorterDuffColorFilter(
52 0, PorterDuff.Mode.SRC_ATOP);
53 private final int mIconDarkAlpha;
54 private final int mIconDarkColor = 0xffffffff;
Selim Cinek9c7712d2015-12-08 19:19:48 -080055 protected final ViewInvertHelper mInvertHelper;
56
Selim Cinek4ffd6362015-12-29 15:12:23 +010057 protected final ViewTransformationHelper mTransformationHelper;
58
Selim Cinek9c7712d2015-12-08 19:19:48 -080059 protected int mColor;
60 private ImageView mIcon;
61
62 private ImageView mExpandButton;
63 private NotificationHeaderView mNotificationHeader;
64
65 protected NotificationHeaderViewWrapper(Context ctx, View view) {
66 super(view);
67 mIconDarkAlpha = ctx.getResources().getInteger(R.integer.doze_small_icon_alpha);
Selim Cinek9c7712d2015-12-08 19:19:48 -080068 mInvertHelper = new ViewInvertHelper(ctx, NotificationPanelView.DOZE_ANIMATION_DURATION);
Selim Cinek4ffd6362015-12-29 15:12:23 +010069 mTransformationHelper = new ViewTransformationHelper();
Selim Cinek9c7712d2015-12-08 19:19:48 -080070 resolveHeaderViews();
Selim Cinek4ffd6362015-12-29 15:12:23 +010071 updateInvertHelper();
Selim Cinek9c7712d2015-12-08 19:19:48 -080072 }
73
74 protected void resolveHeaderViews() {
75 mIcon = (ImageView) mView.findViewById(com.android.internal.R.id.icon);
76 mExpandButton = (ImageView) mView.findViewById(com.android.internal.R.id.expand_button);
77 mColor = resolveColor(mExpandButton);
78 mNotificationHeader = (NotificationHeaderView) mView.findViewById(
79 com.android.internal.R.id.notification_header);
Selim Cinek9c7712d2015-12-08 19:19:48 -080080 }
81
82 private int resolveColor(ImageView icon) {
83 if (icon != null && icon.getDrawable() != null) {
84 ColorFilter filter = icon.getDrawable().getColorFilter();
85 if (filter instanceof PorterDuffColorFilter) {
86 return ((PorterDuffColorFilter) filter).getColor();
87 }
88 }
89 return 0;
90 }
91
92 @Override
Selim Cinek4ffd6362015-12-29 15:12:23 +010093 public void notifyContentUpdated(StatusBarNotification notification) {
Selim Cinek19ba7052016-01-27 20:04:27 -080094 super.notifyContentUpdated(notification);
Selim Cinek9c7712d2015-12-08 19:19:48 -080095 // Reinspect the notification.
96 resolveHeaderViews();
Selim Cinek4ffd6362015-12-29 15:12:23 +010097 updateInvertHelper();
98 updateTransformedTypes();
Selim Cinek646d2052016-01-03 14:42:02 +080099 addRemainingTransformTypes();
Selim Cinek0ffbda62016-01-01 20:29:12 +0100100 updateCropToPaddingForImageViews();
101 }
102
103 /**
Selim Cinek646d2052016-01-03 14:42:02 +0800104 * Adds the remaining TransformTypes to the TransformHelper. This is done to make sure that each
105 * child is faded automatically and doesn't have to be manually added.
106 * The keys used for the views are the ids.
107 */
108 private void addRemainingTransformTypes() {
109 mTransformationHelper.addRemainingTransformTypes(mView);
110 }
111
112 /**
Selim Cinek0ffbda62016-01-01 20:29:12 +0100113 * Since we are deactivating the clipping when transforming the ImageViews don't get clipped
114 * anymore during these transitions. We can avoid that by using
115 * {@link ImageView#setCropToPadding(boolean)} on all ImageViews.
116 */
117 private void updateCropToPaddingForImageViews() {
118 Stack<View> stack = new Stack<>();
119 stack.push(mView);
120 while (!stack.isEmpty()) {
121 View child = stack.pop();
122 if (child instanceof ImageView) {
123 ((ImageView) child).setCropToPadding(true);
124 } else if (child instanceof ViewGroup){
125 ViewGroup group = (ViewGroup) child;
126 for (int i = 0; i < group.getChildCount(); i++) {
127 stack.push(group.getChildAt(i));
128 }
129 }
130 }
Selim Cinek4ffd6362015-12-29 15:12:23 +0100131 }
132
133 protected void updateInvertHelper() {
134 mInvertHelper.clearTargets();
135 for (int i = 0; i < mNotificationHeader.getChildCount(); i++) {
136 View child = mNotificationHeader.getChildAt(i);
137 if (child != mIcon) {
138 mInvertHelper.addTarget(child);
139 }
140 }
141 }
142
143 protected void updateTransformedTypes() {
144 mTransformationHelper.reset();
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800145 mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_HEADER,
146 mNotificationHeader);
Selim Cinek9c7712d2015-12-08 19:19:48 -0800147 }
148
149 @Override
150 public void setDark(boolean dark, boolean fade, long delay) {
Selim Cinekb88b9842016-02-26 09:25:33 -0800151 if (dark == mDark && mDarkInitialized) {
Selim Cinek19ba7052016-01-27 20:04:27 -0800152 return;
153 }
154 super.setDark(dark, fade, delay);
Selim Cinek9c7712d2015-12-08 19:19:48 -0800155 if (fade) {
156 mInvertHelper.fade(dark, delay);
157 } else {
158 mInvertHelper.update(dark);
159 }
160 if (mIcon != null) {
161 boolean hadColorFilter = mNotificationHeader.getOriginalIconColor()
162 != NotificationHeaderView.NO_COLOR;
163 if (fade) {
164 if (hadColorFilter) {
165 fadeIconColorFilter(mIcon, dark, delay);
166 fadeIconAlpha(mIcon, dark, delay);
167 } else {
168 fadeGrayscale(mIcon, dark, delay);
169 }
170 } else {
171 if (hadColorFilter) {
172 updateIconColorFilter(mIcon, dark);
173 updateIconAlpha(mIcon, dark);
174 } else {
175 updateGrayscale(mIcon, dark);
176 }
177 }
178 }
179 }
180
181 protected void startIntensityAnimation(ValueAnimator.AnimatorUpdateListener updateListener,
182 boolean dark, long delay, Animator.AnimatorListener listener) {
183 float startIntensity = dark ? 0f : 1f;
184 float endIntensity = dark ? 1f : 0f;
185 ValueAnimator animator = ValueAnimator.ofFloat(startIntensity, endIntensity);
186 animator.addUpdateListener(updateListener);
187 animator.setDuration(NotificationPanelView.DOZE_ANIMATION_DURATION);
Selim Cinekc18010f2016-01-20 13:41:30 -0800188 animator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
Selim Cinek9c7712d2015-12-08 19:19:48 -0800189 animator.setStartDelay(delay);
190 if (listener != null) {
191 animator.addListener(listener);
192 }
193 animator.start();
194 }
195
196 private void fadeIconColorFilter(final ImageView target, boolean dark, long delay) {
197 startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
198 @Override
199 public void onAnimationUpdate(ValueAnimator animation) {
200 updateIconColorFilter(target, (Float) animation.getAnimatedValue());
201 }
202 }, dark, delay, null /* listener */);
203 }
204
205 private void fadeIconAlpha(final ImageView target, boolean dark, long delay) {
206 startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
207 @Override
208 public void onAnimationUpdate(ValueAnimator animation) {
209 float t = (float) animation.getAnimatedValue();
210 target.setImageAlpha((int) (255 * (1f - t) + mIconDarkAlpha * t));
211 }
212 }, dark, delay, null /* listener */);
213 }
214
215 protected void fadeGrayscale(final ImageView target, final boolean dark, long delay) {
216 startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
217 @Override
218 public void onAnimationUpdate(ValueAnimator animation) {
219 updateGrayscaleMatrix((float) animation.getAnimatedValue());
220 target.setColorFilter(new ColorMatrixColorFilter(mGrayscaleColorMatrix));
221 }
222 }, dark, delay, new AnimatorListenerAdapter() {
223 @Override
224 public void onAnimationEnd(Animator animation) {
225 if (!dark) {
226 target.setColorFilter(null);
227 }
228 }
229 });
230 }
231
232 private void updateIconColorFilter(ImageView target, boolean dark) {
233 updateIconColorFilter(target, dark ? 1f : 0f);
234 }
235
236 private void updateIconColorFilter(ImageView target, float intensity) {
237 int color = interpolateColor(mColor, mIconDarkColor, intensity);
238 mIconColorFilter.setColor(color);
239 Drawable iconDrawable = target.getDrawable();
240
241 // Also, the notification might have been modified during the animation, so background
242 // might be null here.
243 if (iconDrawable != null) {
244 iconDrawable.mutate().setColorFilter(mIconColorFilter);
245 }
246 }
247
248 private void updateIconAlpha(ImageView target, boolean dark) {
249 target.setImageAlpha(dark ? mIconDarkAlpha : 255);
250 }
251
252 protected void updateGrayscale(ImageView target, boolean dark) {
253 if (dark) {
254 updateGrayscaleMatrix(1f);
255 target.setColorFilter(new ColorMatrixColorFilter(mGrayscaleColorMatrix));
256 } else {
257 target.setColorFilter(null);
258 }
259 }
260
261 @Override
262 public void updateExpandability(boolean expandable, View.OnClickListener onClickListener) {
263 mExpandButton.setVisibility(expandable ? View.VISIBLE : View.GONE);
264 mNotificationHeader.setOnClickListener(expandable ? onClickListener : null);
265 }
266
267 private void updateGrayscaleMatrix(float intensity) {
268 mGrayscaleColorMatrix.setSaturation(1 - intensity);
269 }
270
271 private static int interpolateColor(int source, int target, float t) {
272 int aSource = Color.alpha(source);
273 int rSource = Color.red(source);
274 int gSource = Color.green(source);
275 int bSource = Color.blue(source);
276 int aTarget = Color.alpha(target);
277 int rTarget = Color.red(target);
278 int gTarget = Color.green(target);
279 int bTarget = Color.blue(target);
280 return Color.argb(
281 (int) (aSource * (1f - t) + aTarget * t),
282 (int) (rSource * (1f - t) + rTarget * t),
283 (int) (gSource * (1f - t) + gTarget * t),
284 (int) (bSource * (1f - t) + bTarget * t));
285 }
286
287 @Override
288 public NotificationHeaderView getNotificationHeader() {
289 return mNotificationHeader;
290 }
Selim Cinek4ffd6362015-12-29 15:12:23 +0100291
292 @Override
293 public TransformState getCurrentState(int fadingView) {
294 return mTransformationHelper.getCurrentState(fadingView);
295 }
296
297 @Override
298 public void transformTo(TransformableView notification, Runnable endRunnable) {
299 mTransformationHelper.transformTo(notification, endRunnable);
300 }
301
302 @Override
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800303 public void transformTo(TransformableView notification, float transformationAmount) {
304 mTransformationHelper.transformTo(notification, transformationAmount);
305 }
306
307 @Override
Selim Cinek4ffd6362015-12-29 15:12:23 +0100308 public void transformFrom(TransformableView notification) {
309 mTransformationHelper.transformFrom(notification);
310 }
311
312 @Override
Selim Cinek8f2f6a62016-02-23 19:56:31 -0800313 public void transformFrom(TransformableView notification, float transformationAmount) {
314 mTransformationHelper.transformFrom(notification, transformationAmount);
315 }
316
317 @Override
Selim Cinek4ffd6362015-12-29 15:12:23 +0100318 public void setVisible(boolean visible) {
319 super.setVisible(visible);
320 mTransformationHelper.setVisible(visible);
321 }
Selim Cinek9c7712d2015-12-08 19:19:48 -0800322}