• 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.statusbar.notification.NotificationUtils.interpolate;
21 
22 import android.content.res.Resources;
23 import android.util.MathUtils;
24 
25 import com.android.keyguard.KeyguardStatusView;
26 import com.android.systemui.Interpolators;
27 import com.android.systemui.R;
28 
29 /**
30  * Utility class to calculate the clock position and top padding of notifications on Keyguard.
31  */
32 public class KeyguardClockPositionAlgorithm {
33 
34     /**
35      * How much the clock height influences the shade position.
36      * 0 means nothing, 1 means move the shade up by the height of the clock
37      * 0.5f means move the shade up by half of the size of the clock.
38      */
39     private static float CLOCK_HEIGHT_WEIGHT = 0.7f;
40 
41     /**
42      * Margin between the bottom of the clock and the notification shade.
43      */
44     private int mClockNotificationsMargin;
45 
46     /**
47      * Height of the parent view - display size in px.
48      */
49     private int mHeight;
50 
51     /**
52      * Height of {@link KeyguardStatusView}.
53      */
54     private int mKeyguardStatusHeight;
55 
56     /**
57      * Preferred Y position of clock.
58      */
59     private int mClockPreferredY;
60 
61     /**
62      * Whether or not there is a custom clock face on keyguard.
63      */
64     private boolean mHasCustomClock;
65 
66     /**
67      * Whether or not the NSSL contains any visible notifications.
68      */
69     private boolean mHasVisibleNotifs;
70 
71     /**
72      * Height of notification stack: Sum of height of each notification.
73      */
74     private int mNotificationStackHeight;
75 
76     /**
77      * Minimum top margin to avoid overlap with status bar.
78      */
79     private int mMinTopMargin;
80 
81     /**
82      * Maximum bottom padding to avoid overlap with {@link KeyguardBottomAreaView} or
83      * the ambient indication.
84      */
85     private int mMaxShadeBottom;
86 
87     /**
88      * Minimum distance from the status bar.
89      */
90     private int mContainerTopPadding;
91 
92     /**
93      * @see NotificationPanelView#getExpandedFraction()
94      */
95     private float mPanelExpansion;
96 
97     /**
98      * Burn-in prevention x translation.
99      */
100     private int mBurnInPreventionOffsetX;
101 
102     /**
103      * Burn-in prevention y translation.
104      */
105     private int mBurnInPreventionOffsetY;
106 
107     /**
108      * Doze/AOD transition amount.
109      */
110     private float mDarkAmount;
111 
112     private float mEmptyDragAmount;
113 
114     /**
115      * Refreshes the dimension values.
116      */
loadDimens(Resources res)117     public void loadDimens(Resources res) {
118         mClockNotificationsMargin = res.getDimensionPixelSize(
119                 R.dimen.keyguard_clock_notifications_margin);
120         // Consider the lock icon when determining the minimum top padding between the status bar
121         // and top of the clock.
122         mContainerTopPadding = Math.max(res.getDimensionPixelSize(
123                 R.dimen.keyguard_clock_top_margin),
124                 res.getDimensionPixelSize(R.dimen.keyguard_lock_height)
125                         + res.getDimensionPixelSize(R.dimen.keyguard_lock_padding)
126                         + res.getDimensionPixelSize(R.dimen.keyguard_clock_lock_margin));
127         mBurnInPreventionOffsetX = res.getDimensionPixelSize(
128                 R.dimen.burn_in_prevention_offset_x);
129         mBurnInPreventionOffsetY = res.getDimensionPixelSize(
130                 R.dimen.burn_in_prevention_offset_y);
131     }
132 
setup(int minTopMargin, int maxShadeBottom, int notificationStackHeight, float panelExpansion, int parentHeight, int keyguardStatusHeight, int clockPreferredY, boolean hasCustomClock, boolean hasVisibleNotifs, float dark, float emptyDragAmount)133     public void setup(int minTopMargin, int maxShadeBottom, int notificationStackHeight,
134             float panelExpansion, int parentHeight, int keyguardStatusHeight, int clockPreferredY,
135             boolean hasCustomClock, boolean hasVisibleNotifs, float dark, float emptyDragAmount) {
136         mMinTopMargin = minTopMargin + mContainerTopPadding;
137         mMaxShadeBottom = maxShadeBottom;
138         mNotificationStackHeight = notificationStackHeight;
139         mPanelExpansion = panelExpansion;
140         mHeight = parentHeight;
141         mKeyguardStatusHeight = keyguardStatusHeight;
142         mClockPreferredY = clockPreferredY;
143         mHasCustomClock = hasCustomClock;
144         mHasVisibleNotifs = hasVisibleNotifs;
145         mDarkAmount = dark;
146         mEmptyDragAmount = emptyDragAmount;
147     }
148 
run(Result result)149     public void run(Result result) {
150         final int y = getClockY();
151         result.clockY = y;
152         result.clockAlpha = getClockAlpha(y);
153         result.stackScrollerPadding = y + mKeyguardStatusHeight;
154         result.clockX = (int) interpolate(0, burnInPreventionOffsetX(), mDarkAmount);
155     }
156 
getMinStackScrollerPadding()157     public float getMinStackScrollerPadding() {
158         return mMinTopMargin + mKeyguardStatusHeight + mClockNotificationsMargin;
159     }
160 
getMaxClockY()161     private int getMaxClockY() {
162         return mHeight / 2 - mKeyguardStatusHeight - mClockNotificationsMargin;
163     }
164 
getPreferredClockY()165     private int getPreferredClockY() {
166         return mClockPreferredY;
167     }
168 
getExpandedPreferredClockY()169     private int getExpandedPreferredClockY() {
170         return (mHasCustomClock && !mHasVisibleNotifs) ? getPreferredClockY()
171                 : getExpandedClockPosition();
172     }
173 
174     /**
175      * Vertically align the clock and the shade in the available space considering only
176      * a percentage of the clock height defined by {@code CLOCK_HEIGHT_WEIGHT}.
177      * @return Clock Y in pixels.
178      */
getExpandedClockPosition()179     public int getExpandedClockPosition() {
180         final int availableHeight = mMaxShadeBottom - mMinTopMargin;
181         final int containerCenter = mMinTopMargin + availableHeight / 2;
182 
183         float y = containerCenter - mKeyguardStatusHeight * CLOCK_HEIGHT_WEIGHT
184                 - mClockNotificationsMargin - mNotificationStackHeight / 2;
185         if (y < mMinTopMargin) {
186             y = mMinTopMargin;
187         }
188 
189         // Don't allow the clock base to be under half of the screen
190         final float maxClockY = getMaxClockY();
191         if (y > maxClockY) {
192             y = maxClockY;
193         }
194 
195         return (int) y;
196     }
197 
getClockY()198     private int getClockY() {
199         // Dark: Align the bottom edge of the clock at about half of the screen:
200         float clockYDark = (mHasCustomClock ? getPreferredClockY() : getMaxClockY())
201                 + burnInPreventionOffsetY();
202         clockYDark = MathUtils.max(0, clockYDark);
203 
204         float clockYRegular = getExpandedPreferredClockY();
205         float clockYBouncer = -mKeyguardStatusHeight;
206 
207         // Move clock up while collapsing the shade
208         float shadeExpansion = Interpolators.FAST_OUT_LINEAR_IN.getInterpolation(mPanelExpansion);
209         float clockY = MathUtils.lerp(clockYBouncer, clockYRegular, shadeExpansion);
210         clockYDark = MathUtils.lerp(clockYBouncer, clockYDark, shadeExpansion);
211 
212         return (int) (MathUtils.lerp(clockY, clockYDark, mDarkAmount) + mEmptyDragAmount);
213     }
214 
215     /**
216      * We might want to fade out the clock when the user is swiping up.
217      * One exception is when the bouncer will become visible, in this cause the clock
218      * should always persist.
219      *
220      * @param y Current clock Y.
221      * @return Alpha from 0 to 1.
222      */
getClockAlpha(int y)223     private float getClockAlpha(int y) {
224         float alphaKeyguard = Math.max(0, y / Math.max(1f, getExpandedPreferredClockY()));
225         alphaKeyguard = Interpolators.ACCELERATE.getInterpolation(alphaKeyguard);
226         return MathUtils.lerp(alphaKeyguard, 1f, mDarkAmount);
227     }
228 
burnInPreventionOffsetY()229     private float burnInPreventionOffsetY() {
230         return getBurnInOffset(mBurnInPreventionOffsetY * 2, false /* xAxis */)
231                 - mBurnInPreventionOffsetY;
232     }
233 
burnInPreventionOffsetX()234     private float burnInPreventionOffsetX() {
235         return getBurnInOffset(mBurnInPreventionOffsetX * 2, true /* xAxis */)
236                 - mBurnInPreventionOffsetX;
237     }
238 
239     public static class Result {
240 
241         /**
242          * The x translation of the clock.
243          */
244         public int clockX;
245 
246         /**
247          * The y translation of the clock.
248          */
249         public int clockY;
250 
251         /**
252          * The alpha value of the clock.
253          */
254         public float clockAlpha;
255 
256         /**
257          * The top padding of the stack scroller, in pixels.
258          */
259         public int stackScrollerPadding;
260     }
261 }
262