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