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