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
17package com.android.systemui.statusbar.phone;
18
19import android.content.res.Resources;
20import android.graphics.Path;
21import android.view.animation.AccelerateInterpolator;
22import android.view.animation.PathInterpolator;
23
24import com.android.systemui.R;
25
26/**
27 * Utility class to calculate the clock position and top padding of notifications on Keyguard.
28 */
29public 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     */
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
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
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
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
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
132    private float getClockYFraction() {
133        float t = getNotificationAmountT();
134        t = Math.min(t, 1.0f);
135        return (1 - t) * mClockYFractionMax + t * mClockYFractionMin;
136    }
137
138    private int getClockY() {
139        return (int) (getClockYFraction() * mHeight);
140    }
141
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
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
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
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     */
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