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