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