blob: f20ccd5b92c8cf89168da104017dc61fe526f1b4 [file] [log] [blame]
Jorim Jaggi4e857f42014-11-17 19:14:04 +01001/*
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
17package com.android.systemui.statusbar;
18
19import android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
21import android.animation.ValueAnimator;
22import android.content.Context;
Selim Cinek75fe38c2015-11-20 12:47:59 -080023import android.content.res.ColorStateList;
Jorim Jaggi4e857f42014-11-17 19:14:04 +010024import android.graphics.Color;
25import android.graphics.ColorFilter;
26import android.graphics.ColorMatrix;
27import android.graphics.ColorMatrixColorFilter;
28import android.graphics.PorterDuff;
29import android.graphics.PorterDuffColorFilter;
Selim Cinek65b2e7c2015-10-26 14:11:31 -070030import android.graphics.Rect;
Jorim Jaggidacc9242014-12-08 19:21:26 +010031import android.graphics.drawable.Drawable;
Selim Cinek8d6440d2015-10-22 13:00:05 -070032import android.text.TextUtils;
Selim Cinek65b2e7c2015-10-26 14:11:31 -070033import android.view.MotionEvent;
Jorim Jaggi4e857f42014-11-17 19:14:04 +010034import android.view.View;
Selim Cinek65b2e7c2015-10-26 14:11:31 -070035import android.view.ViewConfiguration;
Selim Cinek75fe38c2015-11-20 12:47:59 -080036import android.view.ViewGroup;
Jorim Jaggi4e857f42014-11-17 19:14:04 +010037import android.view.animation.AnimationUtils;
38import android.view.animation.Interpolator;
39import android.widget.ImageView;
Selim Cinek75fe38c2015-11-20 12:47:59 -080040import android.widget.ProgressBar;
Selim Cinek8d6440d2015-10-22 13:00:05 -070041import android.widget.TextView;
Jorim Jaggi4e857f42014-11-17 19:14:04 +010042
43import com.android.systemui.R;
44import com.android.systemui.ViewInvertHelper;
45import com.android.systemui.statusbar.phone.NotificationPanelView;
46
Selim Cinek65b2e7c2015-10-26 14:11:31 -070047import java.util.ArrayList;
48
Jorim Jaggi4e857f42014-11-17 19:14:04 +010049/**
50 * Wraps a notification view inflated from a template.
51 */
52public class NotificationTemplateViewWrapper extends NotificationViewWrapper {
53
Jorim Jaggi4e857f42014-11-17 19:14:04 +010054 private final ColorMatrix mGrayscaleColorMatrix = new ColorMatrix();
55 private final PorterDuffColorFilter mIconColorFilter = new PorterDuffColorFilter(
56 0, PorterDuff.Mode.SRC_ATOP);
57 private final int mIconDarkAlpha;
Selim Cinek75fe38c2015-11-20 12:47:59 -080058 private final int mIconDarkColor = 0xffffffff;
59 private final int mDarkProgressTint = 0xffffffff;
Jorim Jaggi4e857f42014-11-17 19:14:04 +010060 private final Interpolator mLinearOutSlowInInterpolator;
61
Selim Cinek65b2e7c2015-10-26 14:11:31 -070062 private int mColor;
Jorim Jaggidacc9242014-12-08 19:21:26 +010063 private ViewInvertHelper mInvertHelper;
64 private ImageView mIcon;
65 protected ImageView mPicture;
66
Selim Cinek8d6440d2015-10-22 13:00:05 -070067 private TextView mSubText;
Selim Cinek29603462015-11-17 19:04:39 -080068 private View mSubTextDivider;
Selim Cinek65b2e7c2015-10-26 14:11:31 -070069 private ImageView mExpandButton;
Selim Cinek75fe38c2015-11-20 12:47:59 -080070 private ViewGroup mNotificationHeader;
Selim Cinek75fe38c2015-11-20 12:47:59 -080071 private ProgressBar mProgressBar;
Jorim Jaggi92df1f22014-12-16 19:44:41 +010072
Jorim Jaggi4e857f42014-11-17 19:14:04 +010073 protected NotificationTemplateViewWrapper(Context ctx, View view) {
74 super(view);
75 mIconDarkAlpha = ctx.getResources().getInteger(R.integer.doze_small_icon_alpha);
Jorim Jaggi4e857f42014-11-17 19:14:04 +010076 mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(ctx,
77 android.R.interpolator.linear_out_slow_in);
Selim Cinek75fe38c2015-11-20 12:47:59 -080078
Jorim Jaggidacc9242014-12-08 19:21:26 +010079 resolveViews();
80 }
81
82 private void resolveViews() {
83 View mainColumn = mView.findViewById(com.android.internal.R.id.notification_main_column);
Selim Cinek65b2e7c2015-10-26 14:11:31 -070084 mIcon = (ImageView) mView.findViewById(com.android.internal.R.id.icon);
85 mPicture = (ImageView) mView.findViewById(com.android.internal.R.id.right_icon);
Selim Cinek29603462015-11-17 19:04:39 -080086 mSubText = (TextView) mView.findViewById(com.android.internal.R.id.header_sub_text);
87 mSubTextDivider = mView.findViewById(com.android.internal.R.id.sub_text_divider);
Selim Cinek65b2e7c2015-10-26 14:11:31 -070088 mExpandButton = (ImageView) mView.findViewById(com.android.internal.R.id.expand_button);
89 mColor = resolveColor(mExpandButton);
Selim Cinek75fe38c2015-11-20 12:47:59 -080090 final View progress = mView.findViewById(com.android.internal.R.id.progress);
91 if (progress instanceof ProgressBar) {
92 mProgressBar = (ProgressBar) progress;
93 } else {
94 // It's still a viewstub
95 mProgressBar = null;
96 }
97 mNotificationHeader = (ViewGroup) mView.findViewById(
98 com.android.internal.R.id.notification_header);
Selim Cinek75fe38c2015-11-20 12:47:59 -080099 ArrayList<View> viewsToInvert = new ArrayList<>();
100 if (mainColumn != null) {
101 viewsToInvert.add(mainColumn);
102 }
103 for (int i = 0; i < mNotificationHeader.getChildCount(); i++) {
104 View child = mNotificationHeader.getChildAt(i);
105 if (child != mIcon) {
106 viewsToInvert.add(child);
107 }
108 }
109 mInvertHelper = new ViewInvertHelper(viewsToInvert,
110 NotificationPanelView.DOZE_ANIMATION_DURATION);
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100111 }
112
Selim Cinek65b2e7c2015-10-26 14:11:31 -0700113 private int resolveColor(ImageView icon) {
114 if (icon != null && icon.getDrawable() != null) {
115 ColorFilter filter = icon.getDrawable().getColorFilter();
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100116 if (filter instanceof PorterDuffColorFilter) {
117 return ((PorterDuffColorFilter) filter).getColor();
118 }
119 }
120 return 0;
121 }
122
123 @Override
Jorim Jaggidacc9242014-12-08 19:21:26 +0100124 public void notifyContentUpdated() {
125 super.notifyContentUpdated();
126
127 // Reinspect the notification.
128 resolveViews();
129 }
130
131 @Override
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100132 public void setDark(boolean dark, boolean fade, long delay) {
Jorim Jaggi394a5d62014-11-26 23:07:13 +0100133 if (mInvertHelper != null) {
134 if (fade) {
135 mInvertHelper.fade(dark, delay);
136 } else {
137 mInvertHelper.update(dark);
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100138 }
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100139 }
Jorim Jaggi394a5d62014-11-26 23:07:13 +0100140 if (mIcon != null) {
141 if (fade) {
142 fadeIconColorFilter(mIcon, dark, delay);
143 fadeIconAlpha(mIcon, dark, delay);
144 } else {
145 updateIconColorFilter(mIcon, dark);
146 updateIconAlpha(mIcon, dark);
147 }
148 }
149 setPictureGrayscale(dark, fade, delay);
Selim Cinek75fe38c2015-11-20 12:47:59 -0800150 setProgressBarDark(dark, fade, delay);
151 }
152
153 private void setProgressBarDark(boolean dark, boolean fade, long delay) {
154 if (mProgressBar != null) {
155 if (fade) {
156 fadeProgressDark(mProgressBar, dark, delay);
157 } else {
158 updateProgressDark(mProgressBar, dark);
159 }
160 }
161 }
162
163 private void fadeProgressDark(final ProgressBar target, final boolean dark, long delay) {
164 startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
165 @Override
166 public void onAnimationUpdate(ValueAnimator animation) {
167 float t = (float) animation.getAnimatedValue();
168 updateProgressDark(target, t);
169 }
170 }, dark, delay, null /* listener */);
171 }
172
173 private void updateProgressDark(ProgressBar target, float intensity) {
174 int color = interpolateColor(mColor, mDarkProgressTint, intensity);
175 target.getIndeterminateDrawable().mutate().setTint(color);
176 target.getProgressDrawable().mutate().setTint(color);
177 }
178
179 private void updateProgressDark(ProgressBar target, boolean dark) {
180 updateProgressDark(target, dark ? 1f : 0f);
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100181 }
182
183 protected void setPictureGrayscale(boolean grayscale, boolean fade, long delay) {
184 if (mPicture != null) {
185 if (fade) {
186 fadeGrayscale(mPicture, grayscale, delay);
187 } else {
188 updateGrayscale(mPicture, grayscale);
189 }
190 }
191 }
192
193 private void startIntensityAnimation(ValueAnimator.AnimatorUpdateListener updateListener,
194 boolean dark, long delay, Animator.AnimatorListener listener) {
195 float startIntensity = dark ? 0f : 1f;
196 float endIntensity = dark ? 1f : 0f;
197 ValueAnimator animator = ValueAnimator.ofFloat(startIntensity, endIntensity);
198 animator.addUpdateListener(updateListener);
199 animator.setDuration(NotificationPanelView.DOZE_ANIMATION_DURATION);
200 animator.setInterpolator(mLinearOutSlowInInterpolator);
201 animator.setStartDelay(delay);
202 if (listener != null) {
203 animator.addListener(listener);
204 }
205 animator.start();
206 }
207
208 private void fadeIconColorFilter(final ImageView target, boolean dark, long delay) {
209 startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
210 @Override
211 public void onAnimationUpdate(ValueAnimator animation) {
212 updateIconColorFilter(target, (Float) animation.getAnimatedValue());
213 }
214 }, dark, delay, null /* listener */);
215 }
216
217 private void fadeIconAlpha(final ImageView target, boolean dark, long delay) {
218 startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
219 @Override
220 public void onAnimationUpdate(ValueAnimator animation) {
221 float t = (float) animation.getAnimatedValue();
222 target.setImageAlpha((int) (255 * (1f - t) + mIconDarkAlpha * t));
223 }
224 }, dark, delay, null /* listener */);
225 }
226
227 protected void fadeGrayscale(final ImageView target, final boolean dark, long delay) {
228 startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
229 @Override
230 public void onAnimationUpdate(ValueAnimator animation) {
231 updateGrayscaleMatrix((float) animation.getAnimatedValue());
232 target.setColorFilter(new ColorMatrixColorFilter(mGrayscaleColorMatrix));
233 }
234 }, dark, delay, new AnimatorListenerAdapter() {
235 @Override
236 public void onAnimationEnd(Animator animation) {
237 if (!dark) {
238 target.setColorFilter(null);
239 }
240 }
241 });
242 }
243
244 private void updateIconColorFilter(ImageView target, boolean dark) {
245 updateIconColorFilter(target, dark ? 1f : 0f);
246 }
247
248 private void updateIconColorFilter(ImageView target, float intensity) {
Selim Cinek65b2e7c2015-10-26 14:11:31 -0700249 int color = interpolateColor(mColor, mIconDarkColor, intensity);
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100250 mIconColorFilter.setColor(color);
Selim Cinek65b2e7c2015-10-26 14:11:31 -0700251 Drawable iconDrawable = target.getDrawable();
Jorim Jaggidacc9242014-12-08 19:21:26 +0100252
Selim Cinek75fe38c2015-11-20 12:47:59 -0800253 // Also, the notification might have been modified during the animation, so background
254 // might be null here.
Selim Cinek65b2e7c2015-10-26 14:11:31 -0700255 if (iconDrawable != null) {
256 iconDrawable.mutate().setColorFilter(mIconColorFilter);
Jorim Jaggidacc9242014-12-08 19:21:26 +0100257 }
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100258 }
259
260 private void updateIconAlpha(ImageView target, boolean dark) {
261 target.setImageAlpha(dark ? mIconDarkAlpha : 255);
262 }
263
264 protected void updateGrayscale(ImageView target, boolean dark) {
265 if (dark) {
266 updateGrayscaleMatrix(1f);
267 target.setColorFilter(new ColorMatrixColorFilter(mGrayscaleColorMatrix));
268 } else {
269 target.setColorFilter(null);
270 }
271 }
272
Selim Cinek8d6440d2015-10-22 13:00:05 -0700273 @Override
274 public void setSubTextVisible(boolean visible) {
275 if (mSubText == null) {
276 return;
277 }
278 boolean subTextAvailable = !TextUtils.isEmpty(mSubText.getText());
279 if (visible && subTextAvailable) {
280 mSubText.setVisibility(View.VISIBLE);
Selim Cinek29603462015-11-17 19:04:39 -0800281 mSubTextDivider.setVisibility(View.VISIBLE);
Selim Cinek8d6440d2015-10-22 13:00:05 -0700282 } else {
283 mSubText.setVisibility(View.GONE);
Selim Cinek29603462015-11-17 19:04:39 -0800284 mSubTextDivider.setVisibility(View.GONE);
Selim Cinek8d6440d2015-10-22 13:00:05 -0700285 }
286 }
287
Selim Cinek65b2e7c2015-10-26 14:11:31 -0700288 @Override
289 public void updateExpandability(boolean expandable, View.OnClickListener onClickListener) {
290 mExpandButton.setVisibility(expandable ? View.VISIBLE : View.GONE);
Selim Cinekaef6c762015-11-20 17:00:18 -0800291 mNotificationHeader.setOnClickListener(expandable ? onClickListener : null);
Selim Cinek65b2e7c2015-10-26 14:11:31 -0700292 }
293
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100294 private void updateGrayscaleMatrix(float intensity) {
295 mGrayscaleColorMatrix.setSaturation(1 - intensity);
296 }
297
298 private static int interpolateColor(int source, int target, float t) {
299 int aSource = Color.alpha(source);
300 int rSource = Color.red(source);
301 int gSource = Color.green(source);
302 int bSource = Color.blue(source);
303 int aTarget = Color.alpha(target);
304 int rTarget = Color.red(target);
305 int gTarget = Color.green(target);
306 int bTarget = Color.blue(target);
307 return Color.argb(
308 (int) (aSource * (1f - t) + aTarget * t),
309 (int) (rSource * (1f - t) + rTarget * t),
310 (int) (gSource * (1f - t) + gTarget * t),
311 (int) (bSource * (1f - t) + bTarget * t));
312 }
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100313}