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 float getMinStackScrollerPadding(int height, int keyguardStatusHeight) {
100        return mClockYFractionMin * height + keyguardStatusHeight / 2
101                + mClockNotificationsMarginMin;
102    }
103
104    public void run(Result result) {
105        int y = getClockY() - mKeyguardStatusHeight / 2;
106        float clockAdjustment = getClockYExpansionAdjustment();
107        float topPaddingAdjMultiplier = getTopPaddingAdjMultiplier();
108        result.stackScrollerPaddingAdjustment = (int) (clockAdjustment*topPaddingAdjMultiplier);
109        int clockNotificationsPadding = getClockNotificationsPadding()
110                + result.stackScrollerPaddingAdjustment;
111        int padding = y + clockNotificationsPadding;
112        result.clockY = y;
113        result.stackScrollerPadding = mKeyguardStatusHeight + padding;
114        result.clockScale = getClockScale(result.stackScrollerPadding,
115                result.clockY,
116                y + getClockNotificationsPadding() + mKeyguardStatusHeight);
117        result.clockAlpha = getClockAlpha(result.clockScale);
118    }
119
120    private float getClockScale(int notificationPadding, int clockY, int startPadding) {
121        float scaleMultiplier = getNotificationAmountT() == 0 ? 6.0f : 5.0f;
122        float scaleEnd = clockY - mKeyguardStatusHeight * scaleMultiplier;
123        float distanceToScaleEnd = notificationPadding - scaleEnd;
124        float progress = distanceToScaleEnd / (startPadding - scaleEnd);
125        progress = Math.max(0.0f, Math.min(progress, 1.0f));
126        progress = mAccelerateInterpolator.getInterpolation(progress);
127        progress *= Math.pow(1 + mEmptyDragAmount / mDensity / 300, 0.3f);
128        return progress;
129    }
130
131    private int getClockNotificationsPadding() {
132        float t = getNotificationAmountT();
133        t = Math.min(t, 1.0f);
134        return (int) (t * mClockNotificationsMarginMin + (1 - t) * mClockNotificationsMarginMax);
135    }
136
137    private float getClockYFraction() {
138        float t = getNotificationAmountT();
139        t = Math.min(t, 1.0f);
140        return (1 - t) * mClockYFractionMax + t * mClockYFractionMin;
141    }
142
143    private int getClockY() {
144        return (int) (getClockYFraction() * mHeight);
145    }
146
147    private float getClockYExpansionAdjustment() {
148        float rubberbandFactor = getClockYExpansionRubberbandFactor();
149        float value = (rubberbandFactor * (mMaxPanelHeight - mExpandedHeight));
150        float t = value / mMaxPanelHeight;
151        float slowedDownValue = -sSlowDownInterpolator.getInterpolation(t) * SLOW_DOWN_FACTOR
152                * mMaxPanelHeight;
153        if (mNotificationCount == 0) {
154            return (-2*value + slowedDownValue)/3;
155        } else {
156            return slowedDownValue;
157        }
158    }
159
160    private float getClockYExpansionRubberbandFactor() {
161        float t = getNotificationAmountT();
162        t = Math.min(t, 1.0f);
163        t = (float) Math.pow(t, 0.3f);
164        return (1 - t) * CLOCK_RUBBERBAND_FACTOR_MAX + t * CLOCK_RUBBERBAND_FACTOR_MIN;
165    }
166
167    private float getTopPaddingAdjMultiplier() {
168        float t = getNotificationAmountT();
169        t = Math.min(t, 1.0f);
170        return (1 - t) * CLOCK_ADJ_TOP_PADDING_MULTIPLIER_MIN
171                + t * CLOCK_ADJ_TOP_PADDING_MULTIPLIER_MAX;
172    }
173
174    private float getClockAlpha(float scale) {
175        float fadeEnd = getNotificationAmountT() == 0.0f
176                ? CLOCK_SCALE_FADE_END_NO_NOTIFS
177                : CLOCK_SCALE_FADE_END;
178        float alpha = (scale - fadeEnd)
179                / (CLOCK_SCALE_FADE_START - fadeEnd);
180        return Math.max(0, Math.min(1, alpha));
181    }
182
183    /**
184     * @return a value from 0 to 1 depending on how many notification there are
185     */
186    private float getNotificationAmountT() {
187        return mNotificationCount
188                / (mMaxKeyguardNotifications + mMoreCardNotificationAmount);
189    }
190
191    public static class Result {
192
193        /**
194         * The y translation of the clock.
195         */
196        public int clockY;
197
198        /**
199         * The scale of the Clock
200         */
201        public float clockScale;
202
203        /**
204         * The alpha value of the clock.
205         */
206        public float clockAlpha;
207
208        /**
209         * The top padding of the stack scroller, in pixels.
210         */
211        public int stackScrollerPadding;
212
213        /**
214         * The top padding adjustment of the stack scroller, in pixels. This value is used to adjust
215         * the padding, but not the overall panel size.
216         */
217        public int stackScrollerPaddingAdjustment;
218    }
219}
220