• 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 android.animation.ObjectAnimator;
23 import android.content.BroadcastReceiver;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.IntentFilter;
27 import android.graphics.Bitmap;
28 import android.graphics.Canvas;
29 import android.graphics.Color;
30 import android.graphics.LinearGradient;
31 import android.graphics.Paint;
32 import android.graphics.Rect;
33 import android.graphics.RectF;
34 import android.graphics.Region;
35 import android.graphics.Shader;
36 import android.graphics.drawable.Drawable;
37 import android.support.v4.graphics.ColorUtils;
38 import android.util.DisplayMetrics;
39 import android.util.Property;
40 import android.view.View;
41 
42 import com.android.launcher3.CellLayout;
43 import com.android.launcher3.Launcher;
44 import com.android.launcher3.R;
45 import com.android.launcher3.Utilities;
46 import com.android.launcher3.Workspace;
47 import com.android.launcher3.uioverrides.WallpaperColorInfo;
48 import com.android.launcher3.util.Themes;
49 
50 /**
51  * View scrim which draws behind hotseat and workspace
52  */
53 public class WorkspaceAndHotseatScrim implements
54         View.OnAttachStateChangeListener, WallpaperColorInfo.OnChangeListener {
55 
56     public static Property<WorkspaceAndHotseatScrim, Float> SCRIM_PROGRESS =
57             new Property<WorkspaceAndHotseatScrim, Float>(Float.TYPE, "scrimProgress") {
58                 @Override
59                 public Float get(WorkspaceAndHotseatScrim scrim) {
60                     return scrim.mScrimProgress;
61                 }
62 
63                 @Override
64                 public void set(WorkspaceAndHotseatScrim scrim, Float value) {
65                     scrim.setScrimProgress(value);
66                 }
67             };
68 
69     public static Property<WorkspaceAndHotseatScrim, Float> SYSUI_PROGRESS =
70             new Property<WorkspaceAndHotseatScrim, Float>(Float.TYPE, "sysUiProgress") {
71                 @Override
72                 public Float get(WorkspaceAndHotseatScrim scrim) {
73                     return scrim.mSysUiProgress;
74                 }
75 
76                 @Override
77                 public void set(WorkspaceAndHotseatScrim scrim, Float value) {
78                     scrim.setSysUiProgress(value);
79                 }
80             };
81 
82     private static Property<WorkspaceAndHotseatScrim, Float> SYSUI_ANIM_MULTIPLIER =
83             new Property<WorkspaceAndHotseatScrim, Float>(Float.TYPE, "sysUiAnimMultiplier") {
84                 @Override
85                 public Float get(WorkspaceAndHotseatScrim scrim) {
86                     return scrim.mSysUiAnimMultiplier;
87                 }
88 
89                 @Override
90                 public void set(WorkspaceAndHotseatScrim scrim, Float value) {
91                     scrim.mSysUiAnimMultiplier = value;
92                     scrim.reapplySysUiAlpha();
93                 }
94             };
95 
96     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
97         @Override
98         public void onReceive(Context context, Intent intent) {
99             final String action = intent.getAction();
100             if (ACTION_SCREEN_OFF.equals(action)) {
101                 mAnimateScrimOnNextDraw = true;
102             } else if (ACTION_USER_PRESENT.equals(action)) {
103                 // ACTION_USER_PRESENT is sent after onStart/onResume. This covers the case where
104                 // the user unlocked and the Launcher is not in the foreground.
105                 mAnimateScrimOnNextDraw = false;
106             }
107         }
108     };
109 
110     private static final int DARK_SCRIM_COLOR = 0x55000000;
111     private static final int MAX_HOTSEAT_SCRIM_ALPHA = 100;
112     private static final int ALPHA_MASK_HEIGHT_DP = 500;
113     private static final int ALPHA_MASK_BITMAP_DP = 200;
114     private static final int ALPHA_MASK_WIDTH_DP = 2;
115 
116     private final Rect mHighlightRect = new Rect();
117     private final Launcher mLauncher;
118     private final WallpaperColorInfo mWallpaperColorInfo;
119     private final View mRoot;
120 
121     private Workspace mWorkspace;
122 
123     private final boolean mHasSysUiScrim;
124     private boolean mDrawTopScrim, mDrawBottomScrim;
125 
126     private final RectF mFinalMaskRect = new RectF();
127     private final Paint mBottomMaskPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
128     private final Bitmap mBottomMask;
129     private final int mMaskHeight;
130 
131     private final Drawable mTopScrim;
132 
133     private int mFullScrimColor;
134 
135     private float mScrimProgress;
136     private int mScrimAlpha = 0;
137 
138     private float mSysUiProgress = 1;
139     private boolean mHideSysUiScrim;
140 
141     private boolean mAnimateScrimOnNextDraw = false;
142     private float mSysUiAnimMultiplier = 1;
143 
WorkspaceAndHotseatScrim(View view)144     public WorkspaceAndHotseatScrim(View view) {
145         mRoot = view;
146         mLauncher = Launcher.getLauncher(view.getContext());
147         mWallpaperColorInfo = WallpaperColorInfo.getInstance(mLauncher);
148 
149         mMaskHeight = Utilities.pxFromDp(ALPHA_MASK_BITMAP_DP,
150                 view.getResources().getDisplayMetrics());
151 
152         mHasSysUiScrim = !mWallpaperColorInfo.supportsDarkText();
153         if (mHasSysUiScrim) {
154             mTopScrim = Themes.getAttrDrawable(view.getContext(), R.attr.workspaceStatusBarScrim);
155             mBottomMask = createDitheredAlphaMask();
156         } else {
157             mTopScrim = null;
158             mBottomMask = null;
159         }
160 
161         view.addOnAttachStateChangeListener(this);
162         onExtractedColorsChanged(mWallpaperColorInfo);
163     }
164 
setWorkspace(Workspace workspace)165     public void setWorkspace(Workspace workspace)  {
166         mWorkspace = workspace;
167     }
168 
draw(Canvas canvas)169     public void draw(Canvas canvas) {
170         // Draw the background below children.
171         if (mScrimAlpha > 0) {
172             // Update the scroll position first to ensure scrim cutout is in the right place.
173             mWorkspace.computeScrollWithoutInvalidation();
174             CellLayout currCellLayout = mWorkspace.getCurrentDragOverlappingLayout();
175             canvas.save();
176             if (currCellLayout != null && currCellLayout != mLauncher.getHotseat().getLayout()) {
177                 // Cut a hole in the darkening scrim on the page that should be highlighted, if any.
178                 mLauncher.getDragLayer()
179                         .getDescendantRectRelativeToSelf(currCellLayout, mHighlightRect);
180                 canvas.clipRect(mHighlightRect, Region.Op.DIFFERENCE);
181             }
182 
183             canvas.drawColor(ColorUtils.setAlphaComponent(mFullScrimColor, mScrimAlpha));
184             canvas.restore();
185         }
186 
187         if (!mHideSysUiScrim && mHasSysUiScrim) {
188             if (mSysUiProgress <= 0) {
189                 mAnimateScrimOnNextDraw = false;
190                 return;
191             }
192 
193             if (mAnimateScrimOnNextDraw) {
194                 mSysUiAnimMultiplier = 0;
195                 reapplySysUiAlphaNoInvalidate();
196 
197                 ObjectAnimator anim = ObjectAnimator.ofFloat(this, SYSUI_ANIM_MULTIPLIER, 1);
198                 anim.setAutoCancel(true);
199                 anim.setDuration(600);
200                 anim.setStartDelay(mLauncher.getWindow().getTransitionBackgroundFadeDuration());
201                 anim.start();
202                 mAnimateScrimOnNextDraw = false;
203             }
204 
205             if (mDrawTopScrim) {
206                 mTopScrim.draw(canvas);
207             }
208             if (mDrawBottomScrim) {
209                 canvas.drawBitmap(mBottomMask, null, mFinalMaskRect, mBottomMaskPaint);
210             }
211         }
212     }
213 
onInsetsChanged(Rect insets)214     public void onInsetsChanged(Rect insets) {
215         mDrawTopScrim = insets.top > 0;
216         mDrawBottomScrim = !mLauncher.getDeviceProfile().isVerticalBarLayout();
217     }
218 
setScrimProgress(float progress)219     private void setScrimProgress(float progress) {
220         if (mScrimProgress != progress) {
221             mScrimProgress = progress;
222             mScrimAlpha = Math.round(255 * mScrimProgress);
223             invalidate();
224         }
225     }
226 
227     @Override
onViewAttachedToWindow(View view)228     public void onViewAttachedToWindow(View view) {
229         mWallpaperColorInfo.addOnChangeListener(this);
230         onExtractedColorsChanged(mWallpaperColorInfo);
231 
232         if (mHasSysUiScrim) {
233             IntentFilter filter = new IntentFilter(ACTION_SCREEN_OFF);
234             filter.addAction(ACTION_USER_PRESENT); // When the device wakes up + keyguard is gone
235             mRoot.getContext().registerReceiver(mReceiver, filter);
236         }
237     }
238 
239     @Override
onViewDetachedFromWindow(View view)240     public void onViewDetachedFromWindow(View view) {
241         mWallpaperColorInfo.removeOnChangeListener(this);
242         if (mHasSysUiScrim) {
243             mRoot.getContext().unregisterReceiver(mReceiver);
244         }
245     }
246 
247     @Override
onExtractedColorsChanged(WallpaperColorInfo wallpaperColorInfo)248     public void onExtractedColorsChanged(WallpaperColorInfo wallpaperColorInfo) {
249         // for super light wallpaper it needs to be darken for contrast to workspace
250         // for dark wallpapers the text is white so darkening works as well
251         mBottomMaskPaint.setColor(ColorUtils.compositeColors(DARK_SCRIM_COLOR,
252                 wallpaperColorInfo.getMainColor()));
253         reapplySysUiAlpha();
254         mFullScrimColor = wallpaperColorInfo.getMainColor();
255         if (mScrimAlpha > 0) {
256             invalidate();
257         }
258     }
259 
setSize(int w, int h)260     public void setSize(int w, int h) {
261         if (mHasSysUiScrim) {
262             mTopScrim.setBounds(0, 0, w, h);
263             mFinalMaskRect.set(0, h - mMaskHeight, w, h);
264         }
265     }
266 
hideSysUiScrim(boolean hideSysUiScrim)267     public void hideSysUiScrim(boolean hideSysUiScrim) {
268         mHideSysUiScrim = hideSysUiScrim;
269         if (!hideSysUiScrim) {
270             mAnimateScrimOnNextDraw = true;
271         }
272         invalidate();
273     }
274 
setSysUiProgress(float progress)275     private void setSysUiProgress(float progress) {
276         if (progress != mSysUiProgress) {
277             mSysUiProgress = progress;
278             reapplySysUiAlpha();
279         }
280     }
281 
reapplySysUiAlpha()282     private void reapplySysUiAlpha() {
283         if (mHasSysUiScrim) {
284             reapplySysUiAlphaNoInvalidate();
285             if (!mHideSysUiScrim) {
286                 invalidate();
287             }
288         }
289     }
290 
reapplySysUiAlphaNoInvalidate()291     private void reapplySysUiAlphaNoInvalidate() {
292         float factor = mSysUiProgress * mSysUiAnimMultiplier;
293         mBottomMaskPaint.setAlpha(Math.round(MAX_HOTSEAT_SCRIM_ALPHA * factor));
294         mTopScrim.setAlpha(Math.round(255 * factor));
295     }
296 
invalidate()297     public void invalidate() {
298         mRoot.invalidate();
299     }
300 
createDitheredAlphaMask()301     public Bitmap createDitheredAlphaMask() {
302         DisplayMetrics dm = mLauncher.getResources().getDisplayMetrics();
303         int width = Utilities.pxFromDp(ALPHA_MASK_WIDTH_DP, dm);
304         int gradientHeight = Utilities.pxFromDp(ALPHA_MASK_HEIGHT_DP, dm);
305         Bitmap dst = Bitmap.createBitmap(width, mMaskHeight, Bitmap.Config.ALPHA_8);
306         Canvas c = new Canvas(dst);
307         Paint paint = new Paint(Paint.DITHER_FLAG);
308         LinearGradient lg = new LinearGradient(0, 0, 0, gradientHeight,
309                 new int[]{
310                         0x00FFFFFF,
311                         ColorUtils.setAlphaComponent(Color.WHITE, (int) (0xFF * 0.95)),
312                         0xFFFFFFFF},
313                 new float[]{0f, 0.8f, 1f},
314                 Shader.TileMode.CLAMP);
315         paint.setShader(lg);
316         c.drawRect(0, 0, width, gradientHeight, paint);
317         return dst;
318     }
319 }
320