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 android.content.res.Resources;
20 import android.graphics.Path;
21 import android.view.animation.AccelerateInterpolator;
22 import android.view.animation.PathInterpolator;
23 
24 import com.android.systemui.R;
25 import com.android.systemui.statusbar.notification.NotificationUtils;
26 
27 /**
28  * Utility class to calculate the clock position and top padding of notifications on Keyguard.
29  */
30 public class KeyguardClockPositionAlgorithm {
31 
32     private static final float SLOW_DOWN_FACTOR = 0.4f;
33 
34     private static final float CLOCK_RUBBERBAND_FACTOR_MIN = 0.08f;
35     private static final float CLOCK_RUBBERBAND_FACTOR_MAX = 0.8f;
36     private static final float CLOCK_SCALE_FADE_START = 0.95f;
37     private static final float CLOCK_SCALE_FADE_END = 0.75f;
38     private static final float CLOCK_SCALE_FADE_END_NO_NOTIFS = 0.5f;
39 
40     private static final float CLOCK_ADJ_TOP_PADDING_MULTIPLIER_MIN = 1.4f;
41     private static final float CLOCK_ADJ_TOP_PADDING_MULTIPLIER_MAX = 3.2f;
42 
43     private int mClockNotificationsMarginMin;
44     private int mClockNotificationsMarginMax;
45     private float mClockYFractionMin;
46     private float mClockYFractionMax;
47     private int mMaxKeyguardNotifications;
48     private int mMaxPanelHeight;
49     private float mExpandedHeight;
50     private int mNotificationCount;
51     private int mHeight;
52     private int mKeyguardStatusHeight;
53     private float mEmptyDragAmount;
54     private float mDensity;
55 
56     /**
57      * The number (fractional) of notifications the "more" card counts when calculating how many
58      * notifications are currently visible for the y positioning of the clock.
59      */
60     private float mMoreCardNotificationAmount;
61 
62     private static final PathInterpolator sSlowDownInterpolator;
63 
64     static {
65         Path path = new Path();
66         path.moveTo(0, 0);
67         path.cubicTo(0.3f, 0.875f, 0.6f, 1f, 1f, 1f);
68         sSlowDownInterpolator = new PathInterpolator(path);
69     }
70 
71     private AccelerateInterpolator mAccelerateInterpolator = new AccelerateInterpolator();
72     private int mClockBottom;
73     private float mDarkAmount;
74 
75     /**
76      * Refreshes the dimension values.
77      */
loadDimens(Resources res)78     public void loadDimens(Resources res) {
79         mClockNotificationsMarginMin = res.getDimensionPixelSize(
80                 R.dimen.keyguard_clock_notifications_margin_min);
81         mClockNotificationsMarginMax = res.getDimensionPixelSize(
82                 R.dimen.keyguard_clock_notifications_margin_max);
83         mClockYFractionMin = res.getFraction(R.fraction.keyguard_clock_y_fraction_min, 1, 1);
84         mClockYFractionMax = res.getFraction(R.fraction.keyguard_clock_y_fraction_max, 1, 1);
85         mMoreCardNotificationAmount =
86                 (float) res.getDimensionPixelSize(R.dimen.notification_shelf_height) /
87                         res.getDimensionPixelSize(R.dimen.notification_min_height);
88         mDensity = res.getDisplayMetrics().density;
89     }
90 
setup(int maxKeyguardNotifications, int maxPanelHeight, float expandedHeight, int notificationCount, int height, int keyguardStatusHeight, float emptyDragAmount, int clockBottom, float dark)91     public void setup(int maxKeyguardNotifications, int maxPanelHeight, float expandedHeight,
92             int notificationCount, int height, int keyguardStatusHeight, float emptyDragAmount,
93             int clockBottom, float dark) {
94         mMaxKeyguardNotifications = maxKeyguardNotifications;
95         mMaxPanelHeight = maxPanelHeight;
96         mExpandedHeight = expandedHeight;
97         mNotificationCount = notificationCount;
98         mHeight = height;
99         mKeyguardStatusHeight = keyguardStatusHeight;
100         mEmptyDragAmount = emptyDragAmount;
101         mClockBottom = clockBottom;
102         mDarkAmount = dark;
103     }
104 
getMinStackScrollerPadding(int height, int keyguardStatusHeight)105     public float getMinStackScrollerPadding(int height, int keyguardStatusHeight) {
106         return mClockYFractionMin * height + keyguardStatusHeight / 2
107                 + mClockNotificationsMarginMin;
108     }
109 
run(Result result)110     public void run(Result result) {
111         int y = getClockY() - mKeyguardStatusHeight / 2;
112         float clockAdjustment = getClockYExpansionAdjustment();
113         float topPaddingAdjMultiplier = getTopPaddingAdjMultiplier();
114         result.stackScrollerPaddingAdjustment = (int) (clockAdjustment*topPaddingAdjMultiplier);
115         int clockNotificationsPadding = getClockNotificationsPadding()
116                 + result.stackScrollerPaddingAdjustment;
117         int padding = y + clockNotificationsPadding;
118         result.clockY = y;
119         result.stackScrollerPadding = mKeyguardStatusHeight + padding;
120         result.clockScale = getClockScale(result.stackScrollerPadding,
121                 result.clockY,
122                 y + getClockNotificationsPadding() + mKeyguardStatusHeight);
123         result.clockAlpha = getClockAlpha(result.clockScale);
124 
125         result.stackScrollerPadding = (int) NotificationUtils.interpolate(
126                 result.stackScrollerPadding,
127                 mClockBottom + y,
128                 mDarkAmount);
129     }
130 
getClockScale(int notificationPadding, int clockY, int startPadding)131     private float getClockScale(int notificationPadding, int clockY, int startPadding) {
132         float scaleMultiplier = getNotificationAmountT() == 0 ? 6.0f : 5.0f;
133         float scaleEnd = clockY - mKeyguardStatusHeight * scaleMultiplier;
134         float distanceToScaleEnd = notificationPadding - scaleEnd;
135         float progress = distanceToScaleEnd / (startPadding - scaleEnd);
136         progress = Math.max(0.0f, Math.min(progress, 1.0f));
137         progress = mAccelerateInterpolator.getInterpolation(progress);
138         progress *= Math.pow(1 + mEmptyDragAmount / mDensity / 300, 0.3f);
139         return progress;
140     }
141 
getClockNotificationsPadding()142     private int getClockNotificationsPadding() {
143         float t = getNotificationAmountT();
144         t = Math.min(t, 1.0f);
145         return (int) (t * mClockNotificationsMarginMin + (1 - t) * mClockNotificationsMarginMax);
146     }
147 
getClockYFraction()148     private float getClockYFraction() {
149         float t = getNotificationAmountT();
150         t = Math.min(t, 1.0f);
151         return (1 - t) * mClockYFractionMax + t * mClockYFractionMin;
152     }
153 
getClockY()154     private int getClockY() {
155         // Dark: Align the bottom edge of the clock at one third:
156         // clockBottomEdge = result - mKeyguardStatusHeight / 2 + mClockBottom
157         float clockYDark = (0.33f * mHeight + (float) mKeyguardStatusHeight / 2 - mClockBottom);
158         float clockYRegular = getClockYFraction() * mHeight;
159         return (int) NotificationUtils.interpolate(clockYRegular, clockYDark, mDarkAmount);
160     }
161 
getClockYExpansionAdjustment()162     private float getClockYExpansionAdjustment() {
163         float rubberbandFactor = getClockYExpansionRubberbandFactor();
164         float value = (rubberbandFactor * (mMaxPanelHeight - mExpandedHeight));
165         float t = value / mMaxPanelHeight;
166         float slowedDownValue = -sSlowDownInterpolator.getInterpolation(t) * SLOW_DOWN_FACTOR
167                 * mMaxPanelHeight;
168         if (mNotificationCount == 0) {
169             return (-2*value + slowedDownValue)/3;
170         } else {
171             return slowedDownValue;
172         }
173     }
174 
getClockYExpansionRubberbandFactor()175     private float getClockYExpansionRubberbandFactor() {
176         float t = getNotificationAmountT();
177         t = Math.min(t, 1.0f);
178         t = (float) Math.pow(t, 0.3f);
179         return (1 - t) * CLOCK_RUBBERBAND_FACTOR_MAX + t * CLOCK_RUBBERBAND_FACTOR_MIN;
180     }
181 
getTopPaddingAdjMultiplier()182     private float getTopPaddingAdjMultiplier() {
183         float t = getNotificationAmountT();
184         t = Math.min(t, 1.0f);
185         return (1 - t) * CLOCK_ADJ_TOP_PADDING_MULTIPLIER_MIN
186                 + t * CLOCK_ADJ_TOP_PADDING_MULTIPLIER_MAX;
187     }
188 
getClockAlpha(float scale)189     private float getClockAlpha(float scale) {
190         float fadeEnd = getNotificationAmountT() == 0.0f
191                 ? CLOCK_SCALE_FADE_END_NO_NOTIFS
192                 : CLOCK_SCALE_FADE_END;
193         float alpha = (scale - fadeEnd)
194                 / (CLOCK_SCALE_FADE_START - fadeEnd);
195         return Math.max(0, Math.min(1, alpha));
196     }
197 
198     /**
199      * @return a value from 0 to 1 depending on how many notification there are
200      */
getNotificationAmountT()201     private float getNotificationAmountT() {
202         return mNotificationCount
203                 / (mMaxKeyguardNotifications + mMoreCardNotificationAmount);
204     }
205 
206     public static class Result {
207 
208         /**
209          * The y translation of the clock.
210          */
211         public int clockY;
212 
213         /**
214          * The scale of the Clock
215          */
216         public float clockScale;
217 
218         /**
219          * The alpha value of the clock.
220          */
221         public float clockAlpha;
222 
223         /**
224          * The top padding of the stack scroller, in pixels.
225          */
226         public int stackScrollerPadding;
227 
228         /**
229          * The top padding adjustment of the stack scroller, in pixels. This value is used to adjust
230          * the padding, but not the overall panel size.
231          */
232         public int stackScrollerPaddingAdjustment;
233     }
234 }
235