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 */ 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