Jorim Jaggi | 7d16bb1 | 2014-05-22 04:24:50 +0200 | [diff] [blame] | 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; |
Selim Cinek | f99d000 | 2014-06-13 07:36:01 +0200 | [diff] [blame] | 21 | import android.view.animation.AccelerateInterpolator; |
Jorim Jaggi | 7d16bb1 | 2014-05-22 04:24:50 +0200 | [diff] [blame] | 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; |
Selim Cinek | f99d000 | 2014-06-13 07:36:01 +0200 | [diff] [blame] | 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 | |
Jorim Jaggi | 7d16bb1 | 2014-05-22 04:24:50 +0200 | [diff] [blame] | 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; |
Jorim Jaggi | 48bc36a | 2014-07-25 23:16:04 +0200 | [diff] [blame] | 52 | private float mEmptyDragAmount; |
| 53 | private float mDensity; |
Jorim Jaggi | 7d16bb1 | 2014-05-22 04:24:50 +0200 | [diff] [blame] | 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 | |
Selim Cinek | f99d000 | 2014-06-13 07:36:01 +0200 | [diff] [blame] | 70 | private AccelerateInterpolator mAccelerateInterpolator = new AccelerateInterpolator(); |
| 71 | |
Jorim Jaggi | 7d16bb1 | 2014-05-22 04:24:50 +0200 | [diff] [blame] | 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 = |
Selim Cinek | 281c202 | 2016-10-13 19:14:43 -0700 | [diff] [blame] | 83 | (float) res.getDimensionPixelSize(R.dimen.notification_shelf_height) / |
Jorim Jaggi | 7d16bb1 | 2014-05-22 04:24:50 +0200 | [diff] [blame] | 84 | res.getDimensionPixelSize(R.dimen.notification_min_height); |
Jorim Jaggi | 48bc36a | 2014-07-25 23:16:04 +0200 | [diff] [blame] | 85 | mDensity = res.getDisplayMetrics().density; |
Jorim Jaggi | 7d16bb1 | 2014-05-22 04:24:50 +0200 | [diff] [blame] | 86 | } |
| 87 | |
| 88 | public void setup(int maxKeyguardNotifications, int maxPanelHeight, float expandedHeight, |
Jorim Jaggi | 48bc36a | 2014-07-25 23:16:04 +0200 | [diff] [blame] | 89 | int notificationCount, int height, int keyguardStatusHeight, float emptyDragAmount) { |
Jorim Jaggi | 7d16bb1 | 2014-05-22 04:24:50 +0200 | [diff] [blame] | 90 | mMaxKeyguardNotifications = maxKeyguardNotifications; |
| 91 | mMaxPanelHeight = maxPanelHeight; |
| 92 | mExpandedHeight = expandedHeight; |
| 93 | mNotificationCount = notificationCount; |
| 94 | mHeight = height; |
| 95 | mKeyguardStatusHeight = keyguardStatusHeight; |
Jorim Jaggi | 48bc36a | 2014-07-25 23:16:04 +0200 | [diff] [blame] | 96 | mEmptyDragAmount = emptyDragAmount; |
Jorim Jaggi | 7d16bb1 | 2014-05-22 04:24:50 +0200 | [diff] [blame] | 97 | } |
| 98 | |
Selim Cinek | 5f71bee | 2015-11-18 10:25:23 -0800 | [diff] [blame] | 99 | public float getMinStackScrollerPadding(int height, int keyguardStatusHeight) { |
| 100 | return mClockYFractionMin * height + keyguardStatusHeight / 2 |
| 101 | + mClockNotificationsMarginMin; |
| 102 | } |
| 103 | |
Jorim Jaggi | 7d16bb1 | 2014-05-22 04:24:50 +0200 | [diff] [blame] | 104 | public void run(Result result) { |
Selim Cinek | f99d000 | 2014-06-13 07:36:01 +0200 | [diff] [blame] | 105 | int y = getClockY() - mKeyguardStatusHeight / 2; |
Jorim Jaggi | cc69324 | 2014-06-14 03:04:35 +0000 | [diff] [blame] | 106 | float clockAdjustment = getClockYExpansionAdjustment(); |
Jorim Jaggi | 7d16bb1 | 2014-05-22 04:24:50 +0200 | [diff] [blame] | 107 | float topPaddingAdjMultiplier = getTopPaddingAdjMultiplier(); |
Jorim Jaggi | cc69324 | 2014-06-14 03:04:35 +0000 | [diff] [blame] | 108 | result.stackScrollerPaddingAdjustment = (int) (clockAdjustment*topPaddingAdjMultiplier); |
Jorim Jaggi | 7d16bb1 | 2014-05-22 04:24:50 +0200 | [diff] [blame] | 109 | int clockNotificationsPadding = getClockNotificationsPadding() |
| 110 | + result.stackScrollerPaddingAdjustment; |
| 111 | int padding = y + clockNotificationsPadding; |
Jorim Jaggi | 7d16bb1 | 2014-05-22 04:24:50 +0200 | [diff] [blame] | 112 | result.clockY = y; |
| 113 | result.stackScrollerPadding = mKeyguardStatusHeight + padding; |
Selim Cinek | f99d000 | 2014-06-13 07:36:01 +0200 | [diff] [blame] | 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); |
Jorim Jaggi | 48bc36a | 2014-07-25 23:16:04 +0200 | [diff] [blame] | 127 | progress *= Math.pow(1 + mEmptyDragAmount / mDensity / 300, 0.3f); |
Selim Cinek | f99d000 | 2014-06-13 07:36:01 +0200 | [diff] [blame] | 128 | return progress; |
Jorim Jaggi | 7d16bb1 | 2014-05-22 04:24:50 +0200 | [diff] [blame] | 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 | |
Jorim Jaggi | cc69324 | 2014-06-14 03:04:35 +0000 | [diff] [blame] | 147 | private float getClockYExpansionAdjustment() { |
| 148 | float rubberbandFactor = getClockYExpansionRubberbandFactor(); |
Jorim Jaggi | 7d16bb1 | 2014-05-22 04:24:50 +0200 | [diff] [blame] | 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 | |
Jorim Jaggi | cc69324 | 2014-06-14 03:04:35 +0000 | [diff] [blame] | 160 | private float getClockYExpansionRubberbandFactor() { |
Jorim Jaggi | 7d16bb1 | 2014-05-22 04:24:50 +0200 | [diff] [blame] | 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 | |
Selim Cinek | f99d000 | 2014-06-13 07:36:01 +0200 | [diff] [blame] | 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); |
Jorim Jaggi | 7d16bb1 | 2014-05-22 04:24:50 +0200 | [diff] [blame] | 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 | /** |
Selim Cinek | f99d000 | 2014-06-13 07:36:01 +0200 | [diff] [blame] | 199 | * The scale of the Clock |
| 200 | */ |
| 201 | public float clockScale; |
| 202 | |
| 203 | /** |
Jorim Jaggi | 7d16bb1 | 2014-05-22 04:24:50 +0200 | [diff] [blame] | 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 | } |