Jorim Jaggi | 4e857f4 | 2014-11-17 19:14:04 +0100 | [diff] [blame] | 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; |
Selim Cinek | 75fe38c | 2015-11-20 12:47:59 -0800 | [diff] [blame] | 23 | import android.content.res.ColorStateList; |
Jorim Jaggi | 4e857f4 | 2014-11-17 19:14:04 +0100 | [diff] [blame] | 24 | import android.graphics.Color; |
| 25 | import android.graphics.ColorFilter; |
| 26 | import android.graphics.ColorMatrix; |
| 27 | import android.graphics.ColorMatrixColorFilter; |
| 28 | import android.graphics.PorterDuff; |
| 29 | import android.graphics.PorterDuffColorFilter; |
Selim Cinek | 65b2e7c | 2015-10-26 14:11:31 -0700 | [diff] [blame] | 30 | import android.graphics.Rect; |
Jorim Jaggi | dacc924 | 2014-12-08 19:21:26 +0100 | [diff] [blame] | 31 | import android.graphics.drawable.Drawable; |
Selim Cinek | 8d6440d | 2015-10-22 13:00:05 -0700 | [diff] [blame] | 32 | import android.text.TextUtils; |
Selim Cinek | 65b2e7c | 2015-10-26 14:11:31 -0700 | [diff] [blame] | 33 | import android.view.MotionEvent; |
Jorim Jaggi | 4e857f4 | 2014-11-17 19:14:04 +0100 | [diff] [blame] | 34 | import android.view.View; |
Selim Cinek | 65b2e7c | 2015-10-26 14:11:31 -0700 | [diff] [blame] | 35 | import android.view.ViewConfiguration; |
Selim Cinek | 75fe38c | 2015-11-20 12:47:59 -0800 | [diff] [blame] | 36 | import android.view.ViewGroup; |
Jorim Jaggi | 4e857f4 | 2014-11-17 19:14:04 +0100 | [diff] [blame] | 37 | import android.view.animation.AnimationUtils; |
| 38 | import android.view.animation.Interpolator; |
| 39 | import android.widget.ImageView; |
Selim Cinek | 75fe38c | 2015-11-20 12:47:59 -0800 | [diff] [blame] | 40 | import android.widget.ProgressBar; |
Selim Cinek | 8d6440d | 2015-10-22 13:00:05 -0700 | [diff] [blame] | 41 | import android.widget.TextView; |
Jorim Jaggi | 4e857f4 | 2014-11-17 19:14:04 +0100 | [diff] [blame] | 42 | |
| 43 | import com.android.systemui.R; |
| 44 | import com.android.systemui.ViewInvertHelper; |
| 45 | import com.android.systemui.statusbar.phone.NotificationPanelView; |
| 46 | |
Selim Cinek | 65b2e7c | 2015-10-26 14:11:31 -0700 | [diff] [blame] | 47 | import java.util.ArrayList; |
| 48 | |
Jorim Jaggi | 4e857f4 | 2014-11-17 19:14:04 +0100 | [diff] [blame] | 49 | /** |
| 50 | * Wraps a notification view inflated from a template. |
| 51 | */ |
| 52 | public class NotificationTemplateViewWrapper extends NotificationViewWrapper { |
| 53 | |
Jorim Jaggi | 4e857f4 | 2014-11-17 19:14:04 +0100 | [diff] [blame] | 54 | 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 Cinek | 75fe38c | 2015-11-20 12:47:59 -0800 | [diff] [blame] | 58 | private final int mIconDarkColor = 0xffffffff; |
| 59 | private final int mDarkProgressTint = 0xffffffff; |
Jorim Jaggi | 4e857f4 | 2014-11-17 19:14:04 +0100 | [diff] [blame] | 60 | private final Interpolator mLinearOutSlowInInterpolator; |
| 61 | |
Selim Cinek | 65b2e7c | 2015-10-26 14:11:31 -0700 | [diff] [blame] | 62 | private int mColor; |
Jorim Jaggi | dacc924 | 2014-12-08 19:21:26 +0100 | [diff] [blame] | 63 | private ViewInvertHelper mInvertHelper; |
| 64 | private ImageView mIcon; |
| 65 | protected ImageView mPicture; |
| 66 | |
Selim Cinek | 8d6440d | 2015-10-22 13:00:05 -0700 | [diff] [blame] | 67 | private TextView mSubText; |
Selim Cinek | 2960346 | 2015-11-17 19:04:39 -0800 | [diff] [blame] | 68 | private View mSubTextDivider; |
Selim Cinek | 65b2e7c | 2015-10-26 14:11:31 -0700 | [diff] [blame] | 69 | private ImageView mExpandButton; |
Selim Cinek | 75fe38c | 2015-11-20 12:47:59 -0800 | [diff] [blame] | 70 | private ViewGroup mNotificationHeader; |
Selim Cinek | 75fe38c | 2015-11-20 12:47:59 -0800 | [diff] [blame] | 71 | private ProgressBar mProgressBar; |
Jorim Jaggi | 92df1f2 | 2014-12-16 19:44:41 +0100 | [diff] [blame] | 72 | |
Jorim Jaggi | 4e857f4 | 2014-11-17 19:14:04 +0100 | [diff] [blame] | 73 | protected NotificationTemplateViewWrapper(Context ctx, View view) { |
| 74 | super(view); |
| 75 | mIconDarkAlpha = ctx.getResources().getInteger(R.integer.doze_small_icon_alpha); |
Jorim Jaggi | 4e857f4 | 2014-11-17 19:14:04 +0100 | [diff] [blame] | 76 | mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(ctx, |
| 77 | android.R.interpolator.linear_out_slow_in); |
Selim Cinek | 75fe38c | 2015-11-20 12:47:59 -0800 | [diff] [blame] | 78 | |
Jorim Jaggi | dacc924 | 2014-12-08 19:21:26 +0100 | [diff] [blame] | 79 | resolveViews(); |
| 80 | } |
| 81 | |
| 82 | private void resolveViews() { |
| 83 | View mainColumn = mView.findViewById(com.android.internal.R.id.notification_main_column); |
Selim Cinek | 65b2e7c | 2015-10-26 14:11:31 -0700 | [diff] [blame] | 84 | mIcon = (ImageView) mView.findViewById(com.android.internal.R.id.icon); |
| 85 | mPicture = (ImageView) mView.findViewById(com.android.internal.R.id.right_icon); |
Selim Cinek | 2960346 | 2015-11-17 19:04:39 -0800 | [diff] [blame] | 86 | 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 Cinek | 65b2e7c | 2015-10-26 14:11:31 -0700 | [diff] [blame] | 88 | mExpandButton = (ImageView) mView.findViewById(com.android.internal.R.id.expand_button); |
| 89 | mColor = resolveColor(mExpandButton); |
Selim Cinek | 75fe38c | 2015-11-20 12:47:59 -0800 | [diff] [blame] | 90 | 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 Cinek | 75fe38c | 2015-11-20 12:47:59 -0800 | [diff] [blame] | 99 | 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 Jaggi | 4e857f4 | 2014-11-17 19:14:04 +0100 | [diff] [blame] | 111 | } |
| 112 | |
Selim Cinek | 65b2e7c | 2015-10-26 14:11:31 -0700 | [diff] [blame] | 113 | private int resolveColor(ImageView icon) { |
| 114 | if (icon != null && icon.getDrawable() != null) { |
| 115 | ColorFilter filter = icon.getDrawable().getColorFilter(); |
Jorim Jaggi | 4e857f4 | 2014-11-17 19:14:04 +0100 | [diff] [blame] | 116 | if (filter instanceof PorterDuffColorFilter) { |
| 117 | return ((PorterDuffColorFilter) filter).getColor(); |
| 118 | } |
| 119 | } |
| 120 | return 0; |
| 121 | } |
| 122 | |
| 123 | @Override |
Jorim Jaggi | dacc924 | 2014-12-08 19:21:26 +0100 | [diff] [blame] | 124 | public void notifyContentUpdated() { |
| 125 | super.notifyContentUpdated(); |
| 126 | |
| 127 | // Reinspect the notification. |
| 128 | resolveViews(); |
| 129 | } |
| 130 | |
| 131 | @Override |
Jorim Jaggi | 4e857f4 | 2014-11-17 19:14:04 +0100 | [diff] [blame] | 132 | public void setDark(boolean dark, boolean fade, long delay) { |
Jorim Jaggi | 394a5d6 | 2014-11-26 23:07:13 +0100 | [diff] [blame] | 133 | if (mInvertHelper != null) { |
| 134 | if (fade) { |
| 135 | mInvertHelper.fade(dark, delay); |
| 136 | } else { |
| 137 | mInvertHelper.update(dark); |
Jorim Jaggi | 4e857f4 | 2014-11-17 19:14:04 +0100 | [diff] [blame] | 138 | } |
Jorim Jaggi | 4e857f4 | 2014-11-17 19:14:04 +0100 | [diff] [blame] | 139 | } |
Jorim Jaggi | 394a5d6 | 2014-11-26 23:07:13 +0100 | [diff] [blame] | 140 | 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 Cinek | 75fe38c | 2015-11-20 12:47:59 -0800 | [diff] [blame] | 150 | 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 Jaggi | 4e857f4 | 2014-11-17 19:14:04 +0100 | [diff] [blame] | 181 | } |
| 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 Cinek | 65b2e7c | 2015-10-26 14:11:31 -0700 | [diff] [blame] | 249 | int color = interpolateColor(mColor, mIconDarkColor, intensity); |
Jorim Jaggi | 4e857f4 | 2014-11-17 19:14:04 +0100 | [diff] [blame] | 250 | mIconColorFilter.setColor(color); |
Selim Cinek | 65b2e7c | 2015-10-26 14:11:31 -0700 | [diff] [blame] | 251 | Drawable iconDrawable = target.getDrawable(); |
Jorim Jaggi | dacc924 | 2014-12-08 19:21:26 +0100 | [diff] [blame] | 252 | |
Selim Cinek | 75fe38c | 2015-11-20 12:47:59 -0800 | [diff] [blame] | 253 | // Also, the notification might have been modified during the animation, so background |
| 254 | // might be null here. |
Selim Cinek | 65b2e7c | 2015-10-26 14:11:31 -0700 | [diff] [blame] | 255 | if (iconDrawable != null) { |
| 256 | iconDrawable.mutate().setColorFilter(mIconColorFilter); |
Jorim Jaggi | dacc924 | 2014-12-08 19:21:26 +0100 | [diff] [blame] | 257 | } |
Jorim Jaggi | 4e857f4 | 2014-11-17 19:14:04 +0100 | [diff] [blame] | 258 | } |
| 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 Cinek | 8d6440d | 2015-10-22 13:00:05 -0700 | [diff] [blame] | 273 | @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 Cinek | 2960346 | 2015-11-17 19:04:39 -0800 | [diff] [blame] | 281 | mSubTextDivider.setVisibility(View.VISIBLE); |
Selim Cinek | 8d6440d | 2015-10-22 13:00:05 -0700 | [diff] [blame] | 282 | } else { |
| 283 | mSubText.setVisibility(View.GONE); |
Selim Cinek | 2960346 | 2015-11-17 19:04:39 -0800 | [diff] [blame] | 284 | mSubTextDivider.setVisibility(View.GONE); |
Selim Cinek | 8d6440d | 2015-10-22 13:00:05 -0700 | [diff] [blame] | 285 | } |
| 286 | } |
| 287 | |
Selim Cinek | 65b2e7c | 2015-10-26 14:11:31 -0700 | [diff] [blame] | 288 | @Override |
| 289 | public void updateExpandability(boolean expandable, View.OnClickListener onClickListener) { |
| 290 | mExpandButton.setVisibility(expandable ? View.VISIBLE : View.GONE); |
Selim Cinek | aef6c76 | 2015-11-20 17:00:18 -0800 | [diff] [blame^] | 291 | mNotificationHeader.setOnClickListener(expandable ? onClickListener : null); |
Selim Cinek | 65b2e7c | 2015-10-26 14:11:31 -0700 | [diff] [blame] | 292 | } |
| 293 | |
Jorim Jaggi | 4e857f4 | 2014-11-17 19:14:04 +0100 | [diff] [blame] | 294 | 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 Jaggi | 4e857f4 | 2014-11-17 19:14:04 +0100 | [diff] [blame] | 313 | } |