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