• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 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 static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
20 import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInScale;
21 import static com.android.systemui.statusbar.notification.NotificationUtils.interpolate;
22 
23 import android.content.res.Resources;
24 import android.util.MathUtils;
25 
26 import com.android.keyguard.KeyguardStatusView;
27 import com.android.systemui.R;
28 import com.android.systemui.animation.Interpolators;
29 import com.android.systemui.statusbar.policy.KeyguardUserSwitcherListView;
30 
31 /**
32  * Utility class to calculate the clock position and top padding of notifications on Keyguard.
33  */
34 public class KeyguardClockPositionAlgorithm {
35     /**
36      * How much the clock height influences the shade position.
37      * 0 means nothing, 1 means move the shade up by the height of the clock
38      * 0.5f means move the shade up by half of the size of the clock.
39      */
40     private static float CLOCK_HEIGHT_WEIGHT = 0.7f;
41 
42     /**
43      * Margin between the bottom of the status view and the notification shade.
44      */
45     private int mStatusViewBottomMargin;
46 
47     /**
48      * Height of the parent view - display size in px.
49      */
50     private int mHeight;
51 
52     /**
53      * Height of {@link KeyguardStatusView}.
54      */
55     private int mKeyguardStatusHeight;
56 
57     /**
58      * Height of user avatar used by the multi-user switcher. This could either be the
59      * {@link KeyguardUserSwitcherListView} when it is closed and only the current user's icon is
60      * visible, or it could be height of the avatar used by the
61      * {@link com.android.systemui.statusbar.policy.KeyguardQsUserSwitchController}.
62      */
63     private int mUserSwitchHeight;
64 
65     /**
66      * Preferred Y position of user avatar used by the multi-user switcher.
67      */
68     private int mUserSwitchPreferredY;
69 
70     /**
71      * Whether or not there is a custom clock face on keyguard.
72      */
73     private boolean mHasCustomClock;
74 
75     /**
76      * Whether or not the NSSL contains any visible notifications.
77      */
78     private boolean mHasVisibleNotifs;
79 
80     /**
81      * Height of notification stack: Sum of height of each notification.
82      */
83     private int mNotificationStackHeight;
84 
85     /**
86      * Minimum top margin to avoid overlap with status bar, lock icon, or multi-user switcher
87      * avatar.
88      */
89     private int mMinTopMargin;
90 
91     /**
92      * Minimum top inset (in pixels) to avoid overlap with any display cutouts.
93      */
94     private int mCutoutTopInset = 0;
95 
96     /**
97      * Maximum bottom padding to avoid overlap with {@link KeyguardBottomAreaView} or
98      * the ambient indication.
99      */
100     private int mMaxShadeBottom;
101 
102     /**
103      * Recommended distance from the status bar.
104      */
105     private int mContainerTopPadding;
106 
107     /**
108      * @see NotificationPanelViewController#getExpandedFraction()
109      */
110     private float mPanelExpansion;
111 
112     /**
113      * Burn-in prevention x translation.
114      */
115     private int mBurnInPreventionOffsetX;
116 
117     /**
118      * Burn-in prevention y translation.
119      */
120     private int mBurnInPreventionOffsetY;
121 
122     /**
123      * Burn-in prevention y translation for large clock layouts.
124      */
125     private int mBurnInPreventionOffsetYLargeClock;
126 
127     /**
128      * Doze/AOD transition amount.
129      */
130     private float mDarkAmount;
131 
132     /**
133      * How visible the quick settings panel is.
134      */
135     private float mQsExpansion;
136 
137     private float mOverStretchAmount;
138 
139     /**
140      * Setting if bypass is enabled. If true the clock should always be positioned like it's dark
141      * and other minor adjustments.
142      */
143     private boolean mBypassEnabled;
144 
145     /**
146      * The stackscroller padding when unlocked
147      */
148     private int mUnlockedStackScrollerPadding;
149 
150     private boolean mIsSplitShade;
151 
152     /**
153      * Refreshes the dimension values.
154      */
loadDimens(Resources res)155     public void loadDimens(Resources res) {
156         mStatusViewBottomMargin = res.getDimensionPixelSize(
157                 R.dimen.keyguard_status_view_bottom_margin);
158 
159         mContainerTopPadding =
160                 res.getDimensionPixelSize(R.dimen.keyguard_clock_top_margin) / 2;
161         mBurnInPreventionOffsetX = res.getDimensionPixelSize(
162                 R.dimen.burn_in_prevention_offset_x);
163         mBurnInPreventionOffsetY = res.getDimensionPixelSize(
164                 R.dimen.burn_in_prevention_offset_y);
165         mBurnInPreventionOffsetYLargeClock = res.getDimensionPixelSize(
166                 R.dimen.burn_in_prevention_offset_y_large_clock);
167     }
168 
169     /**
170      * Sets up algorithm values.
171      */
setup(int keyguardStatusBarHeaderHeight, int maxShadeBottom, int notificationStackHeight, float panelExpansion, int parentHeight, int keyguardStatusHeight, int userSwitchHeight, int userSwitchPreferredY, boolean hasCustomClock, boolean hasVisibleNotifs, float dark, float overStrechAmount, boolean bypassEnabled, int unlockedStackScrollerPadding, float qsExpansion, int cutoutTopInset, boolean isSplitShade)172     public void setup(int keyguardStatusBarHeaderHeight, int maxShadeBottom,
173             int notificationStackHeight, float panelExpansion, int parentHeight,
174             int keyguardStatusHeight, int userSwitchHeight, int userSwitchPreferredY,
175             boolean hasCustomClock, boolean hasVisibleNotifs, float dark,
176             float overStrechAmount, boolean bypassEnabled, int unlockedStackScrollerPadding,
177             float qsExpansion, int cutoutTopInset, boolean isSplitShade) {
178         mMinTopMargin = keyguardStatusBarHeaderHeight + Math.max(mContainerTopPadding,
179                 userSwitchHeight);
180         mMaxShadeBottom = maxShadeBottom;
181         mNotificationStackHeight = notificationStackHeight;
182         mPanelExpansion = panelExpansion;
183         mHeight = parentHeight;
184         mKeyguardStatusHeight = keyguardStatusHeight + mStatusViewBottomMargin;
185         mUserSwitchHeight = userSwitchHeight;
186         mUserSwitchPreferredY = userSwitchPreferredY;
187         mHasCustomClock = hasCustomClock;
188         mHasVisibleNotifs = hasVisibleNotifs;
189         mDarkAmount = dark;
190         mOverStretchAmount = overStrechAmount;
191         mBypassEnabled = bypassEnabled;
192         mUnlockedStackScrollerPadding = unlockedStackScrollerPadding;
193         mQsExpansion = qsExpansion;
194         mCutoutTopInset = cutoutTopInset;
195         mIsSplitShade = isSplitShade;
196     }
197 
run(Result result)198     public void run(Result result) {
199         final int y = getClockY(mPanelExpansion, mDarkAmount);
200         result.clockY = y;
201         result.userSwitchY = getUserSwitcherY(mPanelExpansion);
202         result.clockYFullyDozing = getClockY(
203                 1.0f /* panelExpansion */, 1.0f /* darkAmount */);
204         result.clockAlpha = getClockAlpha(y);
205         result.stackScrollerPadding = getStackScrollerPadding(y);
206         result.stackScrollerPaddingExpanded = mBypassEnabled ? mUnlockedStackScrollerPadding
207                 : getClockY(1.0f, mDarkAmount) + mKeyguardStatusHeight;
208         result.clockX = (int) interpolate(0, burnInPreventionOffsetX(), mDarkAmount);
209         result.clockScale = interpolate(getBurnInScale(), 1.0f, 1.0f - mDarkAmount);
210     }
211 
getStackScrollerPadding(int clockYPosition)212     private int getStackScrollerPadding(int clockYPosition) {
213         if (mBypassEnabled) {
214             return (int) (mUnlockedStackScrollerPadding + mOverStretchAmount);
215         } else if (mIsSplitShade) {
216             return clockYPosition;
217         } else {
218             return clockYPosition + mKeyguardStatusHeight;
219         }
220     }
221 
getMinStackScrollerPadding()222     public float getMinStackScrollerPadding() {
223         return mBypassEnabled ? mUnlockedStackScrollerPadding
224                 : mMinTopMargin + mKeyguardStatusHeight;
225     }
226 
getExpandedPreferredClockY()227     private int getExpandedPreferredClockY() {
228         return mMinTopMargin + mUserSwitchHeight;
229     }
230 
getLockscreenStatusViewHeight()231     public int getLockscreenStatusViewHeight() {
232         return mKeyguardStatusHeight;
233     }
234 
getClockY(float panelExpansion, float darkAmount)235     private int getClockY(float panelExpansion, float darkAmount) {
236         float clockYRegular = getExpandedPreferredClockY();
237 
238         // Dividing the height creates a smoother transition when the user swipes up to unlock
239         float clockYBouncer = -mKeyguardStatusHeight / 3.0f;
240 
241         // Move clock up while collapsing the shade
242         float shadeExpansion = Interpolators.FAST_OUT_LINEAR_IN.getInterpolation(panelExpansion);
243         float clockY = MathUtils.lerp(clockYBouncer, clockYRegular, shadeExpansion);
244 
245         // This will keep the clock at the top but out of the cutout area
246         float shift = 0;
247         if (clockY - mBurnInPreventionOffsetYLargeClock < mCutoutTopInset) {
248             shift = mCutoutTopInset - (clockY - mBurnInPreventionOffsetYLargeClock);
249         }
250         float clockYDark = clockY + burnInPreventionOffsetY() + shift;
251 
252         return (int) (MathUtils.lerp(clockY, clockYDark, darkAmount) + mOverStretchAmount);
253     }
254 
getUserSwitcherY(float panelExpansion)255     private int getUserSwitcherY(float panelExpansion) {
256         float userSwitchYRegular = mUserSwitchPreferredY;
257         float userSwitchYBouncer = -mKeyguardStatusHeight - mUserSwitchHeight;
258 
259         // Move user-switch up while collapsing the shade
260         float shadeExpansion = Interpolators.FAST_OUT_LINEAR_IN.getInterpolation(panelExpansion);
261         float userSwitchY = MathUtils.lerp(userSwitchYBouncer, userSwitchYRegular, shadeExpansion);
262 
263         return (int) (userSwitchY + mOverStretchAmount);
264     }
265 
266     /**
267      * We might want to fade out the clock when the user is swiping up.
268      * One exception is when the bouncer will become visible, in this cause the clock
269      * should always persist.
270      *
271      * @param y Current clock Y.
272      * @return Alpha from 0 to 1.
273      */
getClockAlpha(int y)274     private float getClockAlpha(int y) {
275         float alphaKeyguard = Math.max(0, y / Math.max(1f, getClockY(1f, mDarkAmount)));
276         float qsAlphaFactor = MathUtils.saturate(mQsExpansion / 0.3f);
277         qsAlphaFactor = 1f - qsAlphaFactor;
278         alphaKeyguard *= qsAlphaFactor;
279         alphaKeyguard = Interpolators.ACCELERATE.getInterpolation(alphaKeyguard);
280         return MathUtils.lerp(alphaKeyguard, 1f, mDarkAmount);
281     }
282 
burnInPreventionOffsetY()283     private float burnInPreventionOffsetY() {
284         int offset = mBurnInPreventionOffsetYLargeClock;
285 
286         return getBurnInOffset(offset * 2, false /* xAxis */) - offset;
287     }
288 
burnInPreventionOffsetX()289     private float burnInPreventionOffsetX() {
290         return getBurnInOffset(mBurnInPreventionOffsetX, true /* xAxis */);
291     }
292 
293     public static class Result {
294 
295         /**
296          * The x translation of the clock.
297          */
298         public int clockX;
299 
300         /**
301          * The y translation of the clock.
302          */
303         public int clockY;
304 
305         /**
306          * The y translation of the multi-user switch.
307          */
308         public int userSwitchY;
309 
310         /**
311          * The y translation of the clock when we're fully dozing.
312          */
313         public int clockYFullyDozing;
314 
315         /**
316          * The alpha value of the clock.
317          */
318         public float clockAlpha;
319 
320         /**
321          * Amount to scale the large clock (0.0 - 1.0)
322          */
323         public float clockScale;
324 
325         /**
326          * The top padding of the stack scroller, in pixels.
327          */
328         public int stackScrollerPadding;
329 
330         /**
331          * The top padding of the stack scroller, in pixels when fully expanded.
332          */
333         public int stackScrollerPaddingExpanded;
334     }
335 }
336