• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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.launcher3.graphics;
18 
19 import static com.android.launcher3.config.FeatureFlags.KEYGUARD_ANIMATION;
20 import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
21 
22 import android.animation.ObjectAnimator;
23 import android.graphics.Bitmap;
24 import android.graphics.Canvas;
25 import android.graphics.Color;
26 import android.graphics.LinearGradient;
27 import android.graphics.Paint;
28 import android.graphics.Rect;
29 import android.graphics.RectF;
30 import android.graphics.Shader;
31 import android.graphics.drawable.Drawable;
32 import android.util.DisplayMetrics;
33 import android.util.FloatProperty;
34 import android.view.View;
35 
36 import com.android.launcher3.BaseDraggingActivity;
37 import com.android.launcher3.DeviceProfile;
38 import com.android.launcher3.R;
39 import com.android.launcher3.config.FeatureFlags;
40 import com.android.launcher3.testing.shared.ResourceUtils;
41 import com.android.launcher3.util.DynamicResource;
42 import com.android.launcher3.util.ScreenOnTracker;
43 import com.android.launcher3.util.ScreenOnTracker.ScreenOnListener;
44 import com.android.launcher3.util.Themes;
45 import com.android.systemui.plugins.ResourceProvider;
46 
47 /**
48  * View scrim which draws behind hotseat and workspace
49  */
50 public class SysUiScrim implements View.OnAttachStateChangeListener {
51 
52     public static final FloatProperty<SysUiScrim> SYSUI_PROGRESS =
53             new FloatProperty<SysUiScrim>("sysUiProgress") {
54                 @Override
55                 public Float get(SysUiScrim scrim) {
56                     return scrim.mSysUiProgress;
57                 }
58 
59                 @Override
60                 public void setValue(SysUiScrim scrim, float value) {
61                     scrim.setSysUiProgress(value);
62                 }
63             };
64 
65     private static final FloatProperty<SysUiScrim> SYSUI_ANIM_MULTIPLIER =
66             new FloatProperty<SysUiScrim>("sysUiAnimMultiplier") {
67                 @Override
68                 public Float get(SysUiScrim scrim) {
69                     return scrim.mSysUiAnimMultiplier;
70                 }
71 
72                 @Override
73                 public void setValue(SysUiScrim scrim, float value) {
74                     scrim.mSysUiAnimMultiplier = value;
75                     scrim.reapplySysUiAlpha();
76                 }
77             };
78 
79     /**
80      * Receiver used to get a signal that the user unlocked their device.
81      */
82     private final ScreenOnListener mScreenOnListener = new ScreenOnListener() {
83         @Override
84         public void onScreenOnChanged(boolean isOn) {
85             if (!isOn) {
86                 mAnimateScrimOnNextDraw = true;
87             }
88         }
89 
90         @Override
91         public void onUserPresent() {
92             // ACTION_USER_PRESENT is sent after onStart/onResume. This covers the case where
93             // the user unlocked and the Launcher is not in the foreground.
94             mAnimateScrimOnNextDraw = false;
95         }
96     };
97 
98     private static final int MAX_HOTSEAT_SCRIM_ALPHA = 100;
99     private static final int ALPHA_MASK_HEIGHT_DP = 500;
100     private static final int ALPHA_MASK_BITMAP_DP = 200;
101     private static final int ALPHA_MASK_WIDTH_DP = 2;
102 
103     private boolean mDrawTopScrim, mDrawBottomScrim, mDrawWallpaperScrim;
104 
105     private final RectF mWallpaperScrimRect = new RectF();
106     private final Paint mWallpaperScrimPaint = new Paint();
107     private final RectF mFinalMaskRect = new RectF();
108     private final Paint mBottomMaskPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
109     private final Bitmap mBottomMask;
110     private final int mMaskHeight;
111 
112     private final View mRoot;
113     private final BaseDraggingActivity mActivity;
114     private final Drawable mTopScrim;
115 
116     private float mSysUiProgress = 1;
117     private boolean mHideSysUiScrim;
118 
119     private boolean mAnimateScrimOnNextDraw = false;
120     private float mSysUiAnimMultiplier = 1;
121     private int mWallpaperScrimMaxAlpha;
122 
SysUiScrim(View view)123     public SysUiScrim(View view) {
124         mRoot = view;
125         mActivity = BaseDraggingActivity.fromContext(view.getContext());
126         mMaskHeight = ResourceUtils.pxFromDp(ALPHA_MASK_BITMAP_DP,
127                 view.getResources().getDisplayMetrics());
128         mTopScrim = Themes.getAttrDrawable(view.getContext(), R.attr.workspaceStatusBarScrim);
129         if (mTopScrim != null) {
130             mTopScrim.setDither(true);
131             mBottomMask = createDitheredAlphaMask();
132             mHideSysUiScrim = false;
133         } else {
134             mBottomMask = null;
135             mHideSysUiScrim = true;
136         }
137 
138         mDrawWallpaperScrim = FeatureFlags.ENABLE_WALLPAPER_SCRIM.get()
139                 && !Themes.getAttrBoolean(view.getContext(), R.attr.isMainColorDark)
140                 && !Themes.getAttrBoolean(view.getContext(), R.attr.isWorkspaceDarkText);
141         ResourceProvider rp = DynamicResource.provider(view.getContext());
142         int wallpaperScrimColor = rp.getColor(R.color.wallpaper_scrim_color);
143         mWallpaperScrimMaxAlpha = Color.alpha(wallpaperScrimColor);
144         mWallpaperScrimPaint.setColor(wallpaperScrimColor);
145 
146         view.addOnAttachStateChangeListener(this);
147     }
148 
149     /**
150      * Draw the top and bottom scrims
151      */
draw(Canvas canvas)152     public void draw(Canvas canvas) {
153         if (!mHideSysUiScrim) {
154             if (mSysUiProgress <= 0) {
155                 mAnimateScrimOnNextDraw = false;
156                 return;
157             }
158 
159             if (mAnimateScrimOnNextDraw) {
160                 mSysUiAnimMultiplier = 0;
161                 reapplySysUiAlphaNoInvalidate();
162 
163                 ObjectAnimator oa = createSysuiMultiplierAnim(1);
164                 oa.setDuration(600);
165                 oa.setStartDelay(mActivity.getWindow().getTransitionBackgroundFadeDuration());
166                 oa.start();
167                 mAnimateScrimOnNextDraw = false;
168             }
169 
170             if (mDrawWallpaperScrim) {
171                 canvas.drawRect(mWallpaperScrimRect, mWallpaperScrimPaint);
172             }
173             if (mDrawTopScrim) {
174                 mTopScrim.draw(canvas);
175             }
176             if (mDrawBottomScrim) {
177                 canvas.drawBitmap(mBottomMask, null, mFinalMaskRect, mBottomMaskPaint);
178             }
179         }
180     }
181 
182     /**
183      * @return an ObjectAnimator that controls the fade in/out of the sys ui scrim.
184      */
createSysuiMultiplierAnim(float... values)185     public ObjectAnimator createSysuiMultiplierAnim(float... values) {
186         ObjectAnimator anim = ObjectAnimator.ofFloat(this, SYSUI_ANIM_MULTIPLIER, values);
187         anim.setAutoCancel(true);
188         return anim;
189     }
190 
191     /**
192      * Determines whether to draw the top and/or bottom scrim based on new insets.
193      *
194      * In order for the bottom scrim to be drawn this 3 condition should be meet at the same time:
195      * the device is in 3 button navigation, the taskbar is not present and the Hotseat is
196      * horizontal
197      */
onInsetsChanged(Rect insets)198     public void onInsetsChanged(Rect insets) {
199         DeviceProfile dp = mActivity.getDeviceProfile();
200         mDrawTopScrim = mTopScrim != null && insets.top > 0;
201         mDrawBottomScrim = mBottomMask != null
202                 && !dp.isVerticalBarLayout()
203                 && !dp.isGestureMode
204                 && !dp.isTaskbarPresent;
205     }
206 
207     @Override
onViewAttachedToWindow(View view)208     public void onViewAttachedToWindow(View view) {
209         if (!KEYGUARD_ANIMATION.get() && mTopScrim != null) {
210             ScreenOnTracker.INSTANCE.get(mActivity).addListener(mScreenOnListener);
211         }
212     }
213 
214     @Override
onViewDetachedFromWindow(View view)215     public void onViewDetachedFromWindow(View view) {
216         if (!KEYGUARD_ANIMATION.get() && mTopScrim != null) {
217             ScreenOnTracker.INSTANCE.get(mActivity).removeListener(mScreenOnListener);
218         }
219     }
220 
221     /**
222      * Set the width and height of the view being scrimmed
223      * @param w
224      * @param h
225      */
setSize(int w, int h)226     public void setSize(int w, int h) {
227         if (mTopScrim != null) {
228             mTopScrim.setBounds(0, 0, w, h);
229             mFinalMaskRect.set(0, h - mMaskHeight, w, h);
230         }
231         mWallpaperScrimRect.set(0, 0, w, h);
232     }
233 
setSysUiProgress(float progress)234     private void setSysUiProgress(float progress) {
235         if (progress != mSysUiProgress) {
236             mSysUiProgress = progress;
237             reapplySysUiAlpha();
238         }
239     }
240 
reapplySysUiAlpha()241     private void reapplySysUiAlpha() {
242         reapplySysUiAlphaNoInvalidate();
243         if (!mHideSysUiScrim) {
244             mRoot.invalidate();
245         }
246     }
247 
reapplySysUiAlphaNoInvalidate()248     private void reapplySysUiAlphaNoInvalidate() {
249         float factor = mSysUiProgress * mSysUiAnimMultiplier;
250         mBottomMaskPaint.setAlpha(Math.round(MAX_HOTSEAT_SCRIM_ALPHA * factor));
251         if (mTopScrim != null) {
252             mTopScrim.setAlpha(Math.round(255 * factor));
253         }
254         mWallpaperScrimPaint.setAlpha(Math.round(mWallpaperScrimMaxAlpha * factor));
255     }
256 
createDitheredAlphaMask()257     private Bitmap createDitheredAlphaMask() {
258         DisplayMetrics dm = mActivity.getResources().getDisplayMetrics();
259         int width = ResourceUtils.pxFromDp(ALPHA_MASK_WIDTH_DP, dm);
260         int gradientHeight = ResourceUtils.pxFromDp(ALPHA_MASK_HEIGHT_DP, dm);
261         Bitmap dst = Bitmap.createBitmap(width, mMaskHeight, Bitmap.Config.ALPHA_8);
262         Canvas c = new Canvas(dst);
263         Paint paint = new Paint(Paint.DITHER_FLAG);
264         LinearGradient lg = new LinearGradient(0, 0, 0, gradientHeight,
265                 new int[]{
266                         0x00FFFFFF,
267                         setColorAlphaBound(Color.WHITE, (int) (0xFF * 0.95)),
268                         0xFFFFFFFF},
269                 new float[]{0f, 0.8f, 1f},
270                 Shader.TileMode.CLAMP);
271         paint.setShader(lg);
272         c.drawRect(0, 0, width, gradientHeight, paint);
273         return dst;
274     }
275 }
276