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.doze.util.BurnInHelperKt.getBurnInScale; 21 import static com.android.systemui.statusbar.notification.NotificationUtils.interpolate; 22 23 import android.content.res.Resources; 24 import android.util.MathUtils; 25 26 import com.android.keyguard.KeyguardStatusView; 27 import com.android.systemui.R; 28 import com.android.systemui.animation.Interpolators; 29 import com.android.systemui.statusbar.policy.KeyguardUserSwitcherListView; 30 31 /** 32 * Utility class to calculate the clock position and top padding of notifications on Keyguard. 33 */ 34 public class KeyguardClockPositionAlgorithm { 35 /** 36 * How much the clock height influences the shade position. 37 * 0 means nothing, 1 means move the shade up by the height of the clock 38 * 0.5f means move the shade up by half of the size of the clock. 39 */ 40 private static float CLOCK_HEIGHT_WEIGHT = 0.7f; 41 42 /** 43 * Margin between the bottom of the status view and the notification shade. 44 */ 45 private int mStatusViewBottomMargin; 46 47 /** 48 * Height of the parent view - display size in px. 49 */ 50 private int mHeight; 51 52 /** 53 * Height of {@link KeyguardStatusView}. 54 */ 55 private int mKeyguardStatusHeight; 56 57 /** 58 * Height of user avatar used by the multi-user switcher. This could either be the 59 * {@link KeyguardUserSwitcherListView} when it is closed and only the current user's icon is 60 * visible, or it could be height of the avatar used by the 61 * {@link com.android.systemui.statusbar.policy.KeyguardQsUserSwitchController}. 62 */ 63 private int mUserSwitchHeight; 64 65 /** 66 * Preferred Y position of user avatar used by the multi-user switcher. 67 */ 68 private int mUserSwitchPreferredY; 69 70 /** 71 * Whether or not there is a custom clock face on keyguard. 72 */ 73 private boolean mHasCustomClock; 74 75 /** 76 * Whether or not the NSSL contains any visible notifications. 77 */ 78 private boolean mHasVisibleNotifs; 79 80 /** 81 * Height of notification stack: Sum of height of each notification. 82 */ 83 private int mNotificationStackHeight; 84 85 /** 86 * Minimum top margin to avoid overlap with status bar, lock icon, or multi-user switcher 87 * avatar. 88 */ 89 private int mMinTopMargin; 90 91 /** 92 * Minimum top inset (in pixels) to avoid overlap with any display cutouts. 93 */ 94 private int mCutoutTopInset = 0; 95 96 /** 97 * Maximum bottom padding to avoid overlap with {@link KeyguardBottomAreaView} or 98 * the ambient indication. 99 */ 100 private int mMaxShadeBottom; 101 102 /** 103 * Recommended distance from the status bar. 104 */ 105 private int mContainerTopPadding; 106 107 /** 108 * @see NotificationPanelViewController#getExpandedFraction() 109 */ 110 private float mPanelExpansion; 111 112 /** 113 * Burn-in prevention x translation. 114 */ 115 private int mBurnInPreventionOffsetX; 116 117 /** 118 * Burn-in prevention y translation. 119 */ 120 private int mBurnInPreventionOffsetY; 121 122 /** 123 * Burn-in prevention y translation for large clock layouts. 124 */ 125 private int mBurnInPreventionOffsetYLargeClock; 126 127 /** 128 * Doze/AOD transition amount. 129 */ 130 private float mDarkAmount; 131 132 /** 133 * How visible the quick settings panel is. 134 */ 135 private float mQsExpansion; 136 137 private float mOverStretchAmount; 138 139 /** 140 * Setting if bypass is enabled. If true the clock should always be positioned like it's dark 141 * and other minor adjustments. 142 */ 143 private boolean mBypassEnabled; 144 145 /** 146 * The stackscroller padding when unlocked 147 */ 148 private int mUnlockedStackScrollerPadding; 149 150 private boolean mIsSplitShade; 151 152 /** 153 * Refreshes the dimension values. 154 */ loadDimens(Resources res)155 public void loadDimens(Resources res) { 156 mStatusViewBottomMargin = res.getDimensionPixelSize( 157 R.dimen.keyguard_status_view_bottom_margin); 158 159 mContainerTopPadding = 160 res.getDimensionPixelSize(R.dimen.keyguard_clock_top_margin) / 2; 161 mBurnInPreventionOffsetX = res.getDimensionPixelSize( 162 R.dimen.burn_in_prevention_offset_x); 163 mBurnInPreventionOffsetY = res.getDimensionPixelSize( 164 R.dimen.burn_in_prevention_offset_y); 165 mBurnInPreventionOffsetYLargeClock = res.getDimensionPixelSize( 166 R.dimen.burn_in_prevention_offset_y_large_clock); 167 } 168 169 /** 170 * Sets up algorithm values. 171 */ setup(int keyguardStatusBarHeaderHeight, int maxShadeBottom, int notificationStackHeight, float panelExpansion, int parentHeight, int keyguardStatusHeight, int userSwitchHeight, int userSwitchPreferredY, boolean hasCustomClock, boolean hasVisibleNotifs, float dark, float overStrechAmount, boolean bypassEnabled, int unlockedStackScrollerPadding, float qsExpansion, int cutoutTopInset, boolean isSplitShade)172 public void setup(int keyguardStatusBarHeaderHeight, int maxShadeBottom, 173 int notificationStackHeight, float panelExpansion, int parentHeight, 174 int keyguardStatusHeight, int userSwitchHeight, int userSwitchPreferredY, 175 boolean hasCustomClock, boolean hasVisibleNotifs, float dark, 176 float overStrechAmount, boolean bypassEnabled, int unlockedStackScrollerPadding, 177 float qsExpansion, int cutoutTopInset, boolean isSplitShade) { 178 mMinTopMargin = keyguardStatusBarHeaderHeight + Math.max(mContainerTopPadding, 179 userSwitchHeight); 180 mMaxShadeBottom = maxShadeBottom; 181 mNotificationStackHeight = notificationStackHeight; 182 mPanelExpansion = panelExpansion; 183 mHeight = parentHeight; 184 mKeyguardStatusHeight = keyguardStatusHeight + mStatusViewBottomMargin; 185 mUserSwitchHeight = userSwitchHeight; 186 mUserSwitchPreferredY = userSwitchPreferredY; 187 mHasCustomClock = hasCustomClock; 188 mHasVisibleNotifs = hasVisibleNotifs; 189 mDarkAmount = dark; 190 mOverStretchAmount = overStrechAmount; 191 mBypassEnabled = bypassEnabled; 192 mUnlockedStackScrollerPadding = unlockedStackScrollerPadding; 193 mQsExpansion = qsExpansion; 194 mCutoutTopInset = cutoutTopInset; 195 mIsSplitShade = isSplitShade; 196 } 197 run(Result result)198 public void run(Result result) { 199 final int y = getClockY(mPanelExpansion, mDarkAmount); 200 result.clockY = y; 201 result.userSwitchY = getUserSwitcherY(mPanelExpansion); 202 result.clockYFullyDozing = getClockY( 203 1.0f /* panelExpansion */, 1.0f /* darkAmount */); 204 result.clockAlpha = getClockAlpha(y); 205 result.stackScrollerPadding = getStackScrollerPadding(y); 206 result.stackScrollerPaddingExpanded = mBypassEnabled ? mUnlockedStackScrollerPadding 207 : getClockY(1.0f, mDarkAmount) + mKeyguardStatusHeight; 208 result.clockX = (int) interpolate(0, burnInPreventionOffsetX(), mDarkAmount); 209 result.clockScale = interpolate(getBurnInScale(), 1.0f, 1.0f - mDarkAmount); 210 } 211 getStackScrollerPadding(int clockYPosition)212 private int getStackScrollerPadding(int clockYPosition) { 213 if (mBypassEnabled) { 214 return (int) (mUnlockedStackScrollerPadding + mOverStretchAmount); 215 } else if (mIsSplitShade) { 216 return clockYPosition; 217 } else { 218 return clockYPosition + mKeyguardStatusHeight; 219 } 220 } 221 getMinStackScrollerPadding()222 public float getMinStackScrollerPadding() { 223 return mBypassEnabled ? mUnlockedStackScrollerPadding 224 : mMinTopMargin + mKeyguardStatusHeight; 225 } 226 getExpandedPreferredClockY()227 private int getExpandedPreferredClockY() { 228 return mMinTopMargin + mUserSwitchHeight; 229 } 230 getLockscreenStatusViewHeight()231 public int getLockscreenStatusViewHeight() { 232 return mKeyguardStatusHeight; 233 } 234 getClockY(float panelExpansion, float darkAmount)235 private int getClockY(float panelExpansion, float darkAmount) { 236 float clockYRegular = getExpandedPreferredClockY(); 237 238 // Dividing the height creates a smoother transition when the user swipes up to unlock 239 float clockYBouncer = -mKeyguardStatusHeight / 3.0f; 240 241 // Move clock up while collapsing the shade 242 float shadeExpansion = Interpolators.FAST_OUT_LINEAR_IN.getInterpolation(panelExpansion); 243 float clockY = MathUtils.lerp(clockYBouncer, clockYRegular, shadeExpansion); 244 245 // This will keep the clock at the top but out of the cutout area 246 float shift = 0; 247 if (clockY - mBurnInPreventionOffsetYLargeClock < mCutoutTopInset) { 248 shift = mCutoutTopInset - (clockY - mBurnInPreventionOffsetYLargeClock); 249 } 250 float clockYDark = clockY + burnInPreventionOffsetY() + shift; 251 252 return (int) (MathUtils.lerp(clockY, clockYDark, darkAmount) + mOverStretchAmount); 253 } 254 getUserSwitcherY(float panelExpansion)255 private int getUserSwitcherY(float panelExpansion) { 256 float userSwitchYRegular = mUserSwitchPreferredY; 257 float userSwitchYBouncer = -mKeyguardStatusHeight - mUserSwitchHeight; 258 259 // Move user-switch up while collapsing the shade 260 float shadeExpansion = Interpolators.FAST_OUT_LINEAR_IN.getInterpolation(panelExpansion); 261 float userSwitchY = MathUtils.lerp(userSwitchYBouncer, userSwitchYRegular, shadeExpansion); 262 263 return (int) (userSwitchY + mOverStretchAmount); 264 } 265 266 /** 267 * We might want to fade out the clock when the user is swiping up. 268 * One exception is when the bouncer will become visible, in this cause the clock 269 * should always persist. 270 * 271 * @param y Current clock Y. 272 * @return Alpha from 0 to 1. 273 */ getClockAlpha(int y)274 private float getClockAlpha(int y) { 275 float alphaKeyguard = Math.max(0, y / Math.max(1f, getClockY(1f, mDarkAmount))); 276 float qsAlphaFactor = MathUtils.saturate(mQsExpansion / 0.3f); 277 qsAlphaFactor = 1f - qsAlphaFactor; 278 alphaKeyguard *= qsAlphaFactor; 279 alphaKeyguard = Interpolators.ACCELERATE.getInterpolation(alphaKeyguard); 280 return MathUtils.lerp(alphaKeyguard, 1f, mDarkAmount); 281 } 282 burnInPreventionOffsetY()283 private float burnInPreventionOffsetY() { 284 int offset = mBurnInPreventionOffsetYLargeClock; 285 286 return getBurnInOffset(offset * 2, false /* xAxis */) - offset; 287 } 288 burnInPreventionOffsetX()289 private float burnInPreventionOffsetX() { 290 return getBurnInOffset(mBurnInPreventionOffsetX, true /* xAxis */); 291 } 292 293 public static class Result { 294 295 /** 296 * The x translation of the clock. 297 */ 298 public int clockX; 299 300 /** 301 * The y translation of the clock. 302 */ 303 public int clockY; 304 305 /** 306 * The y translation of the multi-user switch. 307 */ 308 public int userSwitchY; 309 310 /** 311 * The y translation of the clock when we're fully dozing. 312 */ 313 public int clockYFullyDozing; 314 315 /** 316 * The alpha value of the clock. 317 */ 318 public float clockAlpha; 319 320 /** 321 * Amount to scale the large clock (0.0 - 1.0) 322 */ 323 public float clockScale; 324 325 /** 326 * The top padding of the stack scroller, in pixels. 327 */ 328 public int stackScrollerPadding; 329 330 /** 331 * The top padding of the stack scroller, in pixels when fully expanded. 332 */ 333 public int stackScrollerPaddingExpanded; 334 } 335 } 336