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