• 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 package com.android.quickstep.views;
17 
18 import static com.android.launcher3.LauncherState.ALL_APPS_HEADER_EXTRA;
19 import static com.android.launcher3.LauncherState.BACKGROUND_APP;
20 import static com.android.launcher3.LauncherState.OVERVIEW;
21 import static com.android.launcher3.anim.Interpolators.ACCEL;
22 import static com.android.launcher3.anim.Interpolators.ACCEL_2;
23 import static com.android.launcher3.anim.Interpolators.LINEAR;
24 import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
25 
26 import android.content.Context;
27 import android.graphics.Canvas;
28 import android.graphics.Color;
29 import android.graphics.Paint;
30 import android.graphics.Path;
31 import android.graphics.Path.Direction;
32 import android.graphics.Path.Op;
33 import android.graphics.Rect;
34 import android.util.AttributeSet;
35 import android.view.animation.Interpolator;
36 
37 import com.android.launcher3.DeviceProfile;
38 import com.android.launcher3.R;
39 import com.android.launcher3.Utilities;
40 import com.android.launcher3.anim.Interpolators;
41 import com.android.launcher3.uioverrides.states.OverviewState;
42 import com.android.launcher3.util.Themes;
43 import com.android.launcher3.views.ScrimView;
44 import com.android.quickstep.SysUINavigationMode;
45 import com.android.quickstep.SysUINavigationMode.Mode;
46 import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
47 
48 /**
49  * Scrim used for all-apps and shelf in Overview
50  * In transposed layout, it behaves as a simple color scrim.
51  * In portrait layout, it draws a rounded rect such that
52  *    From normal state to overview state, the shelf just fades in and does not move
53  *    From overview state to all-apps state the shelf moves up and fades in to cover the screen
54  */
55 public class ShelfScrimView extends ScrimView implements NavigationModeChangeListener {
56 
57     // If the progress is more than this, shelf follows the finger, otherwise it moves faster to
58     // cover the whole screen
59     private static final float SCRIM_CATCHUP_THRESHOLD = 0.2f;
60 
61     // Temporarily needed until android.R.attr.bottomDialogCornerRadius becomes public
62     private static final float BOTTOM_CORNER_RADIUS_RATIO = 2f;
63 
64     // In transposed layout, we simply draw a flat color.
65     private boolean mDrawingFlatColor;
66 
67     // For shelf mode
68     private final int mEndAlpha;
69     private final float mRadius;
70     private final int mMaxScrimAlpha;
71     private final Paint mPaint;
72 
73     // Mid point where the alpha changes
74     private int mMidAlpha;
75     private float mMidProgress;
76 
77     private Interpolator mBeforeMidProgressColorInterpolator = ACCEL;
78     private Interpolator mAfterMidProgressColorInterpolator = ACCEL;
79 
80     private float mShiftRange;
81 
82     private final float mShelfOffset;
83     private float mTopOffset;
84     private float mShelfTop;
85     private float mShelfTopAtThreshold;
86 
87     private int mShelfColor;
88     private int mRemainingScreenColor;
89 
90     private final Path mTempPath = new Path();
91     private final Path mRemainingScreenPath = new Path();
92     private boolean mRemainingScreenPathValid = false;
93 
94     private Mode mSysUINavigationMode;
95 
ShelfScrimView(Context context, AttributeSet attrs)96     public ShelfScrimView(Context context, AttributeSet attrs) {
97         super(context, attrs);
98         mMaxScrimAlpha = Math.round(OVERVIEW.getWorkspaceScrimAlpha(mLauncher) * 255);
99 
100         mEndAlpha = Color.alpha(mEndScrim);
101         mRadius = BOTTOM_CORNER_RADIUS_RATIO * Themes.getDialogCornerRadius(context);
102         mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
103 
104         mShelfOffset = context.getResources().getDimension(R.dimen.shelf_surface_offset);
105         // Just assume the easiest UI for now, until we have the proper layout information.
106         mDrawingFlatColor = true;
107     }
108 
109     @Override
onSizeChanged(int w, int h, int oldw, int oldh)110     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
111         super.onSizeChanged(w, h, oldw, oldh);
112         mRemainingScreenPathValid = false;
113     }
114 
115     @Override
onAttachedToWindow()116     protected void onAttachedToWindow() {
117         super.onAttachedToWindow();
118         onNavigationModeChanged(SysUINavigationMode.INSTANCE.get(getContext())
119                 .addModeChangeListener(this));
120     }
121 
122     @Override
onDetachedFromWindow()123     protected void onDetachedFromWindow() {
124         super.onDetachedFromWindow();
125         SysUINavigationMode.INSTANCE.get(getContext()).removeModeChangeListener(this);
126     }
127 
128     @Override
onNavigationModeChanged(Mode newMode)129     public void onNavigationModeChanged(Mode newMode) {
130         mSysUINavigationMode = newMode;
131         // Note that these interpolators are inverted because progress goes 1 to 0.
132         if (mSysUINavigationMode == Mode.NO_BUTTON) {
133             // Show the shelf more quickly before reaching overview progress.
134             mBeforeMidProgressColorInterpolator = ACCEL_2;
135             mAfterMidProgressColorInterpolator = ACCEL;
136         } else {
137             mBeforeMidProgressColorInterpolator = ACCEL;
138             mAfterMidProgressColorInterpolator = Interpolators.clampToProgress(ACCEL, 0.5f, 1f);
139         }
140     }
141 
142     @Override
reInitUi()143     public void reInitUi() {
144         DeviceProfile dp = mLauncher.getDeviceProfile();
145         mDrawingFlatColor = dp.isVerticalBarLayout();
146 
147         if (!mDrawingFlatColor) {
148             mRemainingScreenPathValid = false;
149             mShiftRange = mLauncher.getAllAppsController().getShiftRange();
150 
151             if ((OVERVIEW.getVisibleElements(mLauncher) & ALL_APPS_HEADER_EXTRA) == 0) {
152                 mMidProgress = 1;
153                 mMidAlpha = 0;
154             } else {
155                 mMidAlpha = Themes.getAttrInteger(getContext(), R.attr.allAppsInterimScrimAlpha);
156                 Rect hotseatPadding = dp.getHotseatLayoutPadding();
157                 int hotseatSize = dp.hotseatBarSizePx + dp.getInsets().bottom
158                         - hotseatPadding.bottom - hotseatPadding.top;
159                 float arrowTop = Math.min(hotseatSize, OverviewState.getDefaultSwipeHeight(dp));
160                 mMidProgress =  1 - (arrowTop / mShiftRange);
161 
162             }
163             mTopOffset = dp.getInsets().top - mShelfOffset;
164             mShelfTopAtThreshold = mShiftRange * SCRIM_CATCHUP_THRESHOLD + mTopOffset;
165         }
166         updateColors();
167         updateDragHandleAlpha();
168         invalidate();
169     }
170 
171     @Override
updateColors()172     public void updateColors() {
173         super.updateColors();
174         if (mDrawingFlatColor) {
175             mDragHandleOffset = 0;
176             return;
177         }
178 
179         mDragHandleOffset = mShelfOffset - mDragHandleSize;
180         if (mProgress >= SCRIM_CATCHUP_THRESHOLD) {
181             mShelfTop = mShiftRange * mProgress + mTopOffset;
182         } else {
183             mShelfTop = Utilities.mapRange(mProgress / SCRIM_CATCHUP_THRESHOLD, -mRadius,
184                     mShelfTopAtThreshold);
185         }
186 
187         if (mProgress >= 1) {
188             mRemainingScreenColor = 0;
189             mShelfColor = 0;
190             if (mSysUINavigationMode == Mode.NO_BUTTON
191                     && mLauncher.getStateManager().getState() == BACKGROUND_APP) {
192                 // Show the shelf background when peeking during swipe up.
193                 mShelfColor = setColorAlphaBound(mEndScrim, mMidAlpha);
194             }
195         } else if (mProgress >= mMidProgress) {
196             mRemainingScreenColor = 0;
197 
198             int alpha = Math.round(Utilities.mapToRange(
199                     mProgress, mMidProgress, 1, mMidAlpha, 0, mBeforeMidProgressColorInterpolator));
200             mShelfColor = setColorAlphaBound(mEndScrim, alpha);
201         } else {
202             mDragHandleOffset += mShiftRange * (mMidProgress - mProgress);
203 
204             // Note that these ranges and interpolators are inverted because progress goes 1 to 0.
205             int alpha = Math.round(
206                     Utilities.mapToRange(mProgress, (float) 0, mMidProgress, (float) mEndAlpha,
207                             (float) mMidAlpha, mAfterMidProgressColorInterpolator));
208             mShelfColor = setColorAlphaBound(mEndScrim, alpha);
209 
210             int remainingScrimAlpha = Math.round(
211                     Utilities.mapToRange(mProgress, (float) 0, mMidProgress, mMaxScrimAlpha,
212                             (float) 0, LINEAR));
213             mRemainingScreenColor = setColorAlphaBound(mScrimColor, remainingScrimAlpha);
214         }
215     }
216 
217     @Override
onDraw(Canvas canvas)218     protected void onDraw(Canvas canvas) {
219         drawBackground(canvas);
220         drawDragHandle(canvas);
221     }
222 
drawBackground(Canvas canvas)223     private void drawBackground(Canvas canvas) {
224         if (mDrawingFlatColor) {
225             if (mCurrentFlatColor != 0) {
226                 canvas.drawColor(mCurrentFlatColor);
227             }
228             return;
229         }
230 
231         if (Color.alpha(mShelfColor) == 0) {
232             return;
233         } else if (mProgress <= 0) {
234             canvas.drawColor(mShelfColor);
235             return;
236         }
237 
238         int height = getHeight();
239         int width = getWidth();
240         // Draw the scrim over the remaining screen if needed.
241         if (mRemainingScreenColor != 0) {
242             if (!mRemainingScreenPathValid) {
243                 mTempPath.reset();
244                 // Using a arbitrary '+10' in the bottom to avoid any left-overs at the
245                 // corners due to rounding issues.
246                 mTempPath.addRoundRect(0, height - mRadius, width, height + mRadius + 10,
247                         mRadius, mRadius, Direction.CW);
248                 mRemainingScreenPath.reset();
249                 mRemainingScreenPath.addRect(0, 0, width, height, Direction.CW);
250                 mRemainingScreenPath.op(mTempPath, Op.DIFFERENCE);
251             }
252 
253             float offset = height - mRadius - mShelfTop;
254             canvas.translate(0, -offset);
255             mPaint.setColor(mRemainingScreenColor);
256             canvas.drawPath(mRemainingScreenPath, mPaint);
257             canvas.translate(0, offset);
258         }
259 
260         mPaint.setColor(mShelfColor);
261         canvas.drawRoundRect(0, mShelfTop, width, height + mRadius, mRadius, mRadius, mPaint);
262     }
263 }
264