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 static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset; 20 import static com.android.systemui.statusbar.notification.NotificationUtils.interpolate; 21 22 import android.content.res.Resources; 23 import android.util.MathUtils; 24 25 import com.android.keyguard.KeyguardStatusView; 26 import com.android.systemui.Interpolators; 27 import com.android.systemui.R; 28 29 /** 30 * Utility class to calculate the clock position and top padding of notifications on Keyguard. 31 */ 32 public class KeyguardClockPositionAlgorithm { 33 34 /** 35 * How much the clock height influences the shade position. 36 * 0 means nothing, 1 means move the shade up by the height of the clock 37 * 0.5f means move the shade up by half of the size of the clock. 38 */ 39 private static float CLOCK_HEIGHT_WEIGHT = 0.7f; 40 41 /** 42 * Margin between the bottom of the clock and the notification shade. 43 */ 44 private int mClockNotificationsMargin; 45 46 /** 47 * Height of the parent view - display size in px. 48 */ 49 private int mHeight; 50 51 /** 52 * Height of {@link KeyguardStatusView}. 53 */ 54 private int mKeyguardStatusHeight; 55 56 /** 57 * Preferred Y position of clock. 58 */ 59 private int mClockPreferredY; 60 61 /** 62 * Whether or not there is a custom clock face on keyguard. 63 */ 64 private boolean mHasCustomClock; 65 66 /** 67 * Whether or not the NSSL contains any visible notifications. 68 */ 69 private boolean mHasVisibleNotifs; 70 71 /** 72 * Height of notification stack: Sum of height of each notification. 73 */ 74 private int mNotificationStackHeight; 75 76 /** 77 * Minimum top margin to avoid overlap with status bar. 78 */ 79 private int mMinTopMargin; 80 81 /** 82 * Maximum bottom padding to avoid overlap with {@link KeyguardBottomAreaView} or 83 * the ambient indication. 84 */ 85 private int mMaxShadeBottom; 86 87 /** 88 * Minimum distance from the status bar. 89 */ 90 private int mContainerTopPadding; 91 92 /** 93 * @see NotificationPanelView#getExpandedFraction() 94 */ 95 private float mPanelExpansion; 96 97 /** 98 * Burn-in prevention x translation. 99 */ 100 private int mBurnInPreventionOffsetX; 101 102 /** 103 * Burn-in prevention y translation. 104 */ 105 private int mBurnInPreventionOffsetY; 106 107 /** 108 * Doze/AOD transition amount. 109 */ 110 private float mDarkAmount; 111 112 private float mEmptyDragAmount; 113 114 /** 115 * Refreshes the dimension values. 116 */ loadDimens(Resources res)117 public void loadDimens(Resources res) { 118 mClockNotificationsMargin = res.getDimensionPixelSize( 119 R.dimen.keyguard_clock_notifications_margin); 120 // Consider the lock icon when determining the minimum top padding between the status bar 121 // and top of the clock. 122 mContainerTopPadding = Math.max(res.getDimensionPixelSize( 123 R.dimen.keyguard_clock_top_margin), 124 res.getDimensionPixelSize(R.dimen.keyguard_lock_height) 125 + res.getDimensionPixelSize(R.dimen.keyguard_lock_padding) 126 + res.getDimensionPixelSize(R.dimen.keyguard_clock_lock_margin)); 127 mBurnInPreventionOffsetX = res.getDimensionPixelSize( 128 R.dimen.burn_in_prevention_offset_x); 129 mBurnInPreventionOffsetY = res.getDimensionPixelSize( 130 R.dimen.burn_in_prevention_offset_y); 131 } 132 setup(int minTopMargin, int maxShadeBottom, int notificationStackHeight, float panelExpansion, int parentHeight, int keyguardStatusHeight, int clockPreferredY, boolean hasCustomClock, boolean hasVisibleNotifs, float dark, float emptyDragAmount)133 public void setup(int minTopMargin, int maxShadeBottom, int notificationStackHeight, 134 float panelExpansion, int parentHeight, int keyguardStatusHeight, int clockPreferredY, 135 boolean hasCustomClock, boolean hasVisibleNotifs, float dark, float emptyDragAmount) { 136 mMinTopMargin = minTopMargin + mContainerTopPadding; 137 mMaxShadeBottom = maxShadeBottom; 138 mNotificationStackHeight = notificationStackHeight; 139 mPanelExpansion = panelExpansion; 140 mHeight = parentHeight; 141 mKeyguardStatusHeight = keyguardStatusHeight; 142 mClockPreferredY = clockPreferredY; 143 mHasCustomClock = hasCustomClock; 144 mHasVisibleNotifs = hasVisibleNotifs; 145 mDarkAmount = dark; 146 mEmptyDragAmount = emptyDragAmount; 147 } 148 run(Result result)149 public void run(Result result) { 150 final int y = getClockY(); 151 result.clockY = y; 152 result.clockAlpha = getClockAlpha(y); 153 result.stackScrollerPadding = y + mKeyguardStatusHeight; 154 result.clockX = (int) interpolate(0, burnInPreventionOffsetX(), mDarkAmount); 155 } 156 getMinStackScrollerPadding()157 public float getMinStackScrollerPadding() { 158 return mMinTopMargin + mKeyguardStatusHeight + mClockNotificationsMargin; 159 } 160 getMaxClockY()161 private int getMaxClockY() { 162 return mHeight / 2 - mKeyguardStatusHeight - mClockNotificationsMargin; 163 } 164 getPreferredClockY()165 private int getPreferredClockY() { 166 return mClockPreferredY; 167 } 168 getExpandedPreferredClockY()169 private int getExpandedPreferredClockY() { 170 return (mHasCustomClock && !mHasVisibleNotifs) ? getPreferredClockY() 171 : getExpandedClockPosition(); 172 } 173 174 /** 175 * Vertically align the clock and the shade in the available space considering only 176 * a percentage of the clock height defined by {@code CLOCK_HEIGHT_WEIGHT}. 177 * @return Clock Y in pixels. 178 */ getExpandedClockPosition()179 public int getExpandedClockPosition() { 180 final int availableHeight = mMaxShadeBottom - mMinTopMargin; 181 final int containerCenter = mMinTopMargin + availableHeight / 2; 182 183 float y = containerCenter - mKeyguardStatusHeight * CLOCK_HEIGHT_WEIGHT 184 - mClockNotificationsMargin - mNotificationStackHeight / 2; 185 if (y < mMinTopMargin) { 186 y = mMinTopMargin; 187 } 188 189 // Don't allow the clock base to be under half of the screen 190 final float maxClockY = getMaxClockY(); 191 if (y > maxClockY) { 192 y = maxClockY; 193 } 194 195 return (int) y; 196 } 197 getClockY()198 private int getClockY() { 199 // Dark: Align the bottom edge of the clock at about half of the screen: 200 float clockYDark = (mHasCustomClock ? getPreferredClockY() : getMaxClockY()) 201 + burnInPreventionOffsetY(); 202 clockYDark = MathUtils.max(0, clockYDark); 203 204 float clockYRegular = getExpandedPreferredClockY(); 205 float clockYBouncer = -mKeyguardStatusHeight; 206 207 // Move clock up while collapsing the shade 208 float shadeExpansion = Interpolators.FAST_OUT_LINEAR_IN.getInterpolation(mPanelExpansion); 209 float clockY = MathUtils.lerp(clockYBouncer, clockYRegular, shadeExpansion); 210 clockYDark = MathUtils.lerp(clockYBouncer, clockYDark, shadeExpansion); 211 212 return (int) (MathUtils.lerp(clockY, clockYDark, mDarkAmount) + mEmptyDragAmount); 213 } 214 215 /** 216 * We might want to fade out the clock when the user is swiping up. 217 * One exception is when the bouncer will become visible, in this cause the clock 218 * should always persist. 219 * 220 * @param y Current clock Y. 221 * @return Alpha from 0 to 1. 222 */ getClockAlpha(int y)223 private float getClockAlpha(int y) { 224 float alphaKeyguard = Math.max(0, y / Math.max(1f, getExpandedPreferredClockY())); 225 alphaKeyguard = Interpolators.ACCELERATE.getInterpolation(alphaKeyguard); 226 return MathUtils.lerp(alphaKeyguard, 1f, mDarkAmount); 227 } 228 burnInPreventionOffsetY()229 private float burnInPreventionOffsetY() { 230 return getBurnInOffset(mBurnInPreventionOffsetY * 2, false /* xAxis */) 231 - mBurnInPreventionOffsetY; 232 } 233 burnInPreventionOffsetX()234 private float burnInPreventionOffsetX() { 235 return getBurnInOffset(mBurnInPreventionOffsetX * 2, true /* xAxis */) 236 - mBurnInPreventionOffsetX; 237 } 238 239 public static class Result { 240 241 /** 242 * The x translation of the clock. 243 */ 244 public int clockX; 245 246 /** 247 * The y translation of the clock. 248 */ 249 public int clockY; 250 251 /** 252 * The alpha value of the clock. 253 */ 254 public float clockAlpha; 255 256 /** 257 * The top padding of the stack scroller, in pixels. 258 */ 259 public int stackScrollerPadding; 260 } 261 } 262