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