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