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