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.settingslib.Utils;
37 import com.android.systemui.Interpolators;
38 import com.android.systemui.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 PorterDuffColorFilter mTintFilter;
170         private Paint mPaint = new Paint();
171 
172         private int mGradientAlphaStart;
173         private int mColorStart;
174         private Rect mFrame;
175 
176 
BarBackgroundDrawable(Context context, int gradientResourceId)177         public BarBackgroundDrawable(Context context, int gradientResourceId) {
178             final Resources res = context.getResources();
179             if (DEBUG_COLORS) {
180                 mOpaque = 0xff0000ff;
181                 mSemiTransparent = 0x7f0000ff;
182                 mTransparent = 0x2f0000ff;
183                 mWarning = 0xffff0000;
184             } else {
185                 mOpaque = context.getColor(R.color.system_bar_background_opaque);
186                 mSemiTransparent = context.getColor(
187                         com.android.internal.R.color.system_bar_background_semi_transparent);
188                 mTransparent = context.getColor(R.color.system_bar_background_transparent);
189                 mWarning = Utils.getColorAttrDefaultColor(context, android.R.attr.colorError);
190             }
191             mGradient = context.getDrawable(gradientResourceId);
192         }
193 
setFrame(Rect frame)194         public void setFrame(Rect frame) {
195             mFrame = frame;
196         }
197 
198         @Override
setAlpha(int alpha)199         public void setAlpha(int alpha) {
200             // noop
201         }
202 
203         @Override
setColorFilter(ColorFilter colorFilter)204         public void setColorFilter(ColorFilter colorFilter) {
205             // noop
206         }
207 
208         @Override
setTint(int color)209         public void setTint(int color) {
210             PorterDuff.Mode targetMode = mTintFilter == null ? Mode.SRC_IN :
211                 mTintFilter.getMode();
212             if (mTintFilter == null || mTintFilter.getColor() != color) {
213                 mTintFilter = new PorterDuffColorFilter(color, targetMode);
214             }
215             invalidateSelf();
216         }
217 
218         @Override
setTintMode(Mode tintMode)219         public void setTintMode(Mode tintMode) {
220             int targetColor = mTintFilter == null ? 0 : mTintFilter.getColor();
221             if (mTintFilter == null || mTintFilter.getMode() != tintMode) {
222                 mTintFilter = new PorterDuffColorFilter(targetColor, tintMode);
223             }
224             invalidateSelf();
225         }
226 
227         @Override
onBoundsChange(Rect bounds)228         protected void onBoundsChange(Rect bounds) {
229             super.onBoundsChange(bounds);
230             mGradient.setBounds(bounds);
231         }
232 
applyModeBackground(int oldMode, int newMode, boolean animate)233         public void applyModeBackground(int oldMode, int newMode, boolean animate) {
234             if (mMode == newMode) return;
235             mMode = newMode;
236             mAnimating = animate;
237             if (animate) {
238                 long now = SystemClock.elapsedRealtime();
239                 mStartTime = now;
240                 mEndTime = now + BACKGROUND_DURATION;
241                 mGradientAlphaStart = mGradientAlpha;
242                 mColorStart = mColor;
243             }
244             invalidateSelf();
245         }
246 
247         @Override
getOpacity()248         public int getOpacity() {
249             return PixelFormat.TRANSLUCENT;
250         }
251 
finishAnimation()252         public void finishAnimation() {
253             if (mAnimating) {
254                 mAnimating = false;
255                 invalidateSelf();
256             }
257         }
258 
259         @Override
draw(Canvas canvas)260         public void draw(Canvas canvas) {
261             int targetGradientAlpha = 0, targetColor = 0;
262             if (mMode == MODE_WARNING) {
263                 targetColor = mWarning;
264             } else if (mMode == MODE_TRANSLUCENT) {
265                 targetColor = mSemiTransparent;
266             } else if (mMode == MODE_SEMI_TRANSPARENT) {
267                 targetColor = mSemiTransparent;
268             } else if (mMode == MODE_TRANSPARENT || mMode == MODE_LIGHTS_OUT_TRANSPARENT) {
269                 targetColor = mTransparent;
270             } else {
271                 targetColor = mOpaque;
272             }
273 
274             if (!mAnimating) {
275                 mColor = targetColor;
276                 mGradientAlpha = targetGradientAlpha;
277             } else {
278                 final long now = SystemClock.elapsedRealtime();
279                 if (now >= mEndTime) {
280                     mAnimating = false;
281                     mColor = targetColor;
282                     mGradientAlpha = targetGradientAlpha;
283                 } else {
284                     final float t = (now - mStartTime) / (float)(mEndTime - mStartTime);
285                     final float v = Math.max(0, Math.min(
286                             Interpolators.LINEAR.getInterpolation(t), 1));
287                     mGradientAlpha = (int)(v * targetGradientAlpha + mGradientAlphaStart * (1 - v));
288                     mColor = Color.argb(
289                           (int)(v * Color.alpha(targetColor) + Color.alpha(mColorStart) * (1 - v)),
290                           (int)(v * Color.red(targetColor) + Color.red(mColorStart) * (1 - v)),
291                           (int)(v * Color.green(targetColor) + Color.green(mColorStart) * (1 - v)),
292                           (int)(v * Color.blue(targetColor) + Color.blue(mColorStart) * (1 - v)));
293                 }
294             }
295             if (mGradientAlpha > 0) {
296                 mGradient.setAlpha(mGradientAlpha);
297                 mGradient.draw(canvas);
298             }
299             if (Color.alpha(mColor) > 0) {
300                 mPaint.setColor(mColor);
301                 if (mTintFilter != null) {
302                     mPaint.setColorFilter(mTintFilter);
303                 }
304                 if (mFrame != null) {
305                     canvas.drawRect(mFrame, mPaint);
306                 } else {
307                     canvas.drawPaint(mPaint);
308                 }
309             }
310             if (mAnimating) {
311                 invalidateSelf();  // keep going
312             }
313         }
314     }
315 }
316