1 /* 2 * Copyright (C) 2013 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.phone; 18 19 import android.annotation.IntDef; 20 import android.content.Context; 21 import android.content.res.Resources; 22 import android.graphics.Canvas; 23 import android.graphics.Color; 24 import android.graphics.ColorFilter; 25 import android.graphics.Paint; 26 import android.graphics.PixelFormat; 27 import android.graphics.PorterDuff; 28 import android.graphics.PorterDuff.Mode; 29 import android.graphics.PorterDuffColorFilter; 30 import android.graphics.Rect; 31 import android.graphics.drawable.Drawable; 32 import android.os.SystemClock; 33 import android.util.Log; 34 import android.view.View; 35 36 import com.android.app.animation.Interpolators; 37 import com.android.settingslib.Utils; 38 import com.android.systemui.res.R; 39 40 import java.lang.annotation.Retention; 41 import java.lang.annotation.RetentionPolicy; 42 43 public class BarTransitions { 44 private static final boolean DEBUG = false; 45 private static final boolean DEBUG_COLORS = false; 46 47 public static final int MODE_TRANSPARENT = 0; 48 public static final int MODE_SEMI_TRANSPARENT = 1; 49 public static final int MODE_TRANSLUCENT = 2; 50 public static final int MODE_LIGHTS_OUT = 3; 51 public static final int MODE_OPAQUE = 4; 52 public static final int MODE_WARNING = 5; 53 public static final int MODE_LIGHTS_OUT_TRANSPARENT = 6; 54 55 @IntDef(flag = true, prefix = { "MODE_" }, value = { 56 MODE_OPAQUE, 57 MODE_SEMI_TRANSPARENT, 58 MODE_TRANSLUCENT, 59 MODE_LIGHTS_OUT, 60 MODE_TRANSPARENT, 61 MODE_WARNING, 62 MODE_LIGHTS_OUT_TRANSPARENT 63 }) 64 @Retention(RetentionPolicy.SOURCE) 65 public @interface TransitionMode {} 66 67 public static final int LIGHTS_IN_DURATION = 250; 68 public static final int LIGHTS_OUT_DURATION = 1500; 69 public static final int BACKGROUND_DURATION = 200; 70 71 private final String mTag; 72 private final View mView; 73 protected final BarBackgroundDrawable mBarBackground; 74 75 private @TransitionMode int mMode; 76 private boolean mAlwaysOpaque = false; 77 BarTransitions(View view, int gradientResourceId)78 public BarTransitions(View view, int gradientResourceId) { 79 mTag = "BarTransitions." + view.getClass().getSimpleName(); 80 mView = view; 81 mBarBackground = new BarBackgroundDrawable(mView.getContext(), gradientResourceId); 82 mView.setBackground(mBarBackground); 83 } 84 destroy()85 public void destroy() { 86 // To be overridden 87 } 88 getMode()89 public int getMode() { 90 return mMode; 91 } 92 setAutoDim(boolean autoDim)93 public void setAutoDim(boolean autoDim) { 94 // Default is don't care. 95 } 96 97 /** 98 * @param alwaysOpaque if {@code true}, the bar's background will always be opaque, regardless 99 * of what mode it is currently set to. 100 */ setAlwaysOpaque(boolean alwaysOpaque)101 public void setAlwaysOpaque(boolean alwaysOpaque) { 102 mAlwaysOpaque = alwaysOpaque; 103 } 104 isAlwaysOpaque()105 public boolean isAlwaysOpaque() { 106 // Low-end devices do not support translucent modes, fallback to opaque 107 return mAlwaysOpaque; 108 } 109 transitionTo(int mode, boolean animate)110 public void transitionTo(int mode, boolean animate) { 111 if (isAlwaysOpaque() && (mode == MODE_SEMI_TRANSPARENT || mode == MODE_TRANSLUCENT 112 || mode == MODE_TRANSPARENT)) { 113 mode = MODE_OPAQUE; 114 } 115 if (isAlwaysOpaque() && (mode == MODE_LIGHTS_OUT_TRANSPARENT)) { 116 mode = MODE_LIGHTS_OUT; 117 } 118 if (mMode == mode) return; 119 int oldMode = mMode; 120 mMode = mode; 121 if (DEBUG) Log.d(mTag, String.format("%s -> %s animate=%s", 122 modeToString(oldMode), modeToString(mode), animate)); 123 onTransition(oldMode, mMode, animate); 124 } 125 onTransition(int oldMode, int newMode, boolean animate)126 protected void onTransition(int oldMode, int newMode, boolean animate) { 127 applyModeBackground(oldMode, newMode, animate); 128 } 129 applyModeBackground(int oldMode, int newMode, boolean animate)130 protected void applyModeBackground(int oldMode, int newMode, boolean animate) { 131 if (DEBUG) Log.d(mTag, String.format("applyModeBackground oldMode=%s newMode=%s animate=%s", 132 modeToString(oldMode), modeToString(newMode), animate)); 133 mBarBackground.applyModeBackground(oldMode, newMode, animate); 134 } 135 modeToString(int mode)136 public static String modeToString(int mode) { 137 if (mode == MODE_OPAQUE) return "MODE_OPAQUE"; 138 if (mode == MODE_SEMI_TRANSPARENT) return "MODE_SEMI_TRANSPARENT"; 139 if (mode == MODE_TRANSLUCENT) return "MODE_TRANSLUCENT"; 140 if (mode == MODE_LIGHTS_OUT) return "MODE_LIGHTS_OUT"; 141 if (mode == MODE_TRANSPARENT) return "MODE_TRANSPARENT"; 142 if (mode == MODE_WARNING) return "MODE_WARNING"; 143 if (mode == MODE_LIGHTS_OUT_TRANSPARENT) return "MODE_LIGHTS_OUT_TRANSPARENT"; 144 throw new IllegalArgumentException("Unknown mode " + mode); 145 } 146 finishAnimations()147 public void finishAnimations() { 148 mBarBackground.finishAnimation(); 149 } 150 isLightsOut(int mode)151 protected boolean isLightsOut(int mode) { 152 return mode == MODE_LIGHTS_OUT || mode == MODE_LIGHTS_OUT_TRANSPARENT; 153 } 154 155 protected static class BarBackgroundDrawable extends Drawable { 156 private final int mOpaque; 157 private final int mSemiTransparent; 158 private final int mTransparent; 159 private final int mWarning; 160 private final Drawable mGradient; 161 162 private int mMode = -1; 163 private boolean mAnimating; 164 private long mStartTime; 165 private long mEndTime; 166 167 private int mGradientAlpha; 168 private int mColor; 169 private float mOverrideAlpha = 1f; 170 private PorterDuffColorFilter mTintFilter; 171 private Paint mPaint = new Paint(); 172 173 private int mGradientAlphaStart; 174 private int mColorStart; 175 private Rect mFrame; 176 177 BarBackgroundDrawable(Context context, int gradientResourceId)178 public BarBackgroundDrawable(Context context, int gradientResourceId) { 179 final Resources res = context.getResources(); 180 if (DEBUG_COLORS) { 181 mOpaque = 0xff0000ff; 182 mSemiTransparent = 0x7f0000ff; 183 mTransparent = 0x2f0000ff; 184 mWarning = 0xffff0000; 185 } else { 186 mOpaque = context.getColor(R.color.system_bar_background_opaque); 187 mSemiTransparent = context.getColor( 188 com.android.internal.R.color.system_bar_background_semi_transparent); 189 mTransparent = context.getColor(R.color.system_bar_background_transparent); 190 mWarning = Utils.getColorAttrDefaultColor(context, android.R.attr.colorError); 191 } 192 mGradient = context.getDrawable(gradientResourceId); 193 } 194 setFrame(Rect frame)195 public void setFrame(Rect frame) { 196 mFrame = frame; 197 } 198 setOverrideAlpha(float overrideAlpha)199 public void setOverrideAlpha(float overrideAlpha) { 200 mOverrideAlpha = overrideAlpha; 201 invalidateSelf(); 202 } 203 getOverrideAlpha()204 public float getOverrideAlpha() { 205 return mOverrideAlpha; 206 } 207 getColor()208 public int getColor() { 209 return mColor; 210 } 211 getFrame()212 public Rect getFrame() { 213 return mFrame; 214 } 215 216 @Override setAlpha(int alpha)217 public void setAlpha(int alpha) { 218 // noop 219 } 220 221 @Override setColorFilter(ColorFilter colorFilter)222 public void setColorFilter(ColorFilter colorFilter) { 223 // noop 224 } 225 226 @Override setTint(int color)227 public void setTint(int color) { 228 PorterDuff.Mode targetMode = mTintFilter == null ? Mode.SRC_IN : 229 mTintFilter.getMode(); 230 if (mTintFilter == null || mTintFilter.getColor() != color) { 231 mTintFilter = new PorterDuffColorFilter(color, targetMode); 232 } 233 invalidateSelf(); 234 } 235 236 @Override setTintMode(Mode tintMode)237 public void setTintMode(Mode tintMode) { 238 int targetColor = mTintFilter == null ? 0 : mTintFilter.getColor(); 239 if (mTintFilter == null || mTintFilter.getMode() != tintMode) { 240 mTintFilter = new PorterDuffColorFilter(targetColor, tintMode); 241 } 242 invalidateSelf(); 243 } 244 245 @Override onBoundsChange(Rect bounds)246 protected void onBoundsChange(Rect bounds) { 247 super.onBoundsChange(bounds); 248 mGradient.setBounds(bounds); 249 } 250 applyModeBackground(int oldMode, int newMode, boolean animate)251 public void applyModeBackground(int oldMode, int newMode, boolean animate) { 252 if (mMode == newMode) return; 253 mMode = newMode; 254 mAnimating = animate; 255 if (animate) { 256 long now = SystemClock.elapsedRealtime(); 257 mStartTime = now; 258 mEndTime = now + BACKGROUND_DURATION; 259 mGradientAlphaStart = mGradientAlpha; 260 mColorStart = mColor; 261 } 262 invalidateSelf(); 263 } 264 265 @Override getOpacity()266 public int getOpacity() { 267 return PixelFormat.TRANSLUCENT; 268 } 269 finishAnimation()270 public void finishAnimation() { 271 if (mAnimating) { 272 mAnimating = false; 273 invalidateSelf(); 274 } 275 } 276 277 @Override draw(Canvas canvas)278 public void draw(Canvas canvas) { 279 int targetGradientAlpha = 0, targetColor = 0; 280 if (mMode == MODE_WARNING) { 281 targetColor = mWarning; 282 } else if (mMode == MODE_TRANSLUCENT) { 283 targetColor = mSemiTransparent; 284 } else if (mMode == MODE_SEMI_TRANSPARENT) { 285 targetColor = mSemiTransparent; 286 } else if (mMode == MODE_TRANSPARENT || mMode == MODE_LIGHTS_OUT_TRANSPARENT) { 287 targetColor = mTransparent; 288 } else { 289 targetColor = mOpaque; 290 } 291 292 if (!mAnimating) { 293 mColor = targetColor; 294 mGradientAlpha = targetGradientAlpha; 295 } else { 296 final long now = SystemClock.elapsedRealtime(); 297 if (now >= mEndTime) { 298 mAnimating = false; 299 mColor = targetColor; 300 mGradientAlpha = targetGradientAlpha; 301 } else { 302 final float t = (now - mStartTime) / (float)(mEndTime - mStartTime); 303 final float v = Math.max(0, Math.min( 304 Interpolators.LINEAR.getInterpolation(t), 1)); 305 mGradientAlpha = (int)(v * targetGradientAlpha + mGradientAlphaStart * (1 - v)); 306 mColor = Color.argb( 307 (int)(v * Color.alpha(targetColor) + Color.alpha(mColorStart) * (1 - v)), 308 (int)(v * Color.red(targetColor) + Color.red(mColorStart) * (1 - v)), 309 (int)(v * Color.green(targetColor) + Color.green(mColorStart) * (1 - v)), 310 (int)(v * Color.blue(targetColor) + Color.blue(mColorStart) * (1 - v))); 311 } 312 } 313 if (mGradientAlpha > 0) { 314 mGradient.setAlpha(mGradientAlpha); 315 mGradient.draw(canvas); 316 } 317 318 if (Color.alpha(mColor) > 0) { 319 mPaint.setColor(mColor); 320 if (mTintFilter != null) { 321 mPaint.setColorFilter(mTintFilter); 322 } 323 mPaint.setAlpha((int) (Color.alpha(mColor) * mOverrideAlpha)); 324 if (mFrame != null) { 325 canvas.drawRect(mFrame, mPaint); 326 } else { 327 canvas.drawPaint(mPaint); 328 } 329 } 330 if (mAnimating) { 331 invalidateSelf(); // keep going 332 } 333 } 334 } 335 } 336