diff options
| author | 2021-09-18 06:20:47 +0000 | |
|---|---|---|
| committer | 2021-09-18 06:20:47 +0000 | |
| commit | 15686a37fc56ea0d7b67d3db08b09b84035df069 (patch) | |
| tree | 0c109b7d8cb990c41e888e949bba0e7b574f2f56 | |
| parent | d13ce08d6bf5b274d92bcf859133360aace2a6bd (diff) | |
| parent | 9b62051f88a70fa8bdd8dfacf747cb05a801359d (diff) | |
Merge "Update clock burn-in so it doesn't clash with udpfs" into sc-v2-dev
8 files changed, 354 insertions, 27 deletions
diff --git a/packages/SystemUI/res/values-h800dp/dimens.xml b/packages/SystemUI/res/values-h800dp/dimens.xml index 19ec8cee18a8..f057603e2cd0 100644 --- a/packages/SystemUI/res/values-h800dp/dimens.xml +++ b/packages/SystemUI/res/values-h800dp/dimens.xml @@ -16,7 +16,7 @@ <resources> <!-- Minimum margin between clock and top of screen or ambient indication --> - <dimen name="keyguard_clock_top_margin">76dp</dimen> + <dimen name="keyguard_clock_top_margin">38dp</dimen> <!-- Large clock maximum font size (dp is intentional, to prevent any further scaling) --> <dimen name="large_clock_text_size">200dp</dimen> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index c432395139bc..79581e183c4d 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -750,9 +750,7 @@ <!-- The margin between the status view and the notifications on Keyguard.--> <dimen name="keyguard_status_view_bottom_margin">20dp</dimen> <!-- Minimum margin between clock and status bar --> - <dimen name="keyguard_clock_top_margin">36dp</dimen> - <!-- The margin between top of clock and bottom of lock icon. --> - <dimen name="keyguard_clock_lock_margin">16dp</dimen> + <dimen name="keyguard_clock_top_margin">18dp</dimen> <!-- The amount to shift the clocks during a small/large transition --> <dimen name="keyguard_clock_switch_y_shift">10dp</dimen> <!-- When large clock is showing, offset the smartspace by this amount --> @@ -1157,9 +1155,9 @@ <dimen name="default_burn_in_prevention_offset">15dp</dimen> <!-- The maximum offset for the under-display fingerprint sensor (UDFPS) icon in either - direction that elements aer moved to prevent burn-in on AOD--> - <dimen name="udfps_burn_in_offset_x">2dp</dimen> - <dimen name="udfps_burn_in_offset_y">8dp</dimen> + direction that elements are moved to prevent burn-in on AOD--> + <dimen name="udfps_burn_in_offset_x">7px</dimen> + <dimen name="udfps_burn_in_offset_y">28px</dimen> <dimen name="corner_size">8dp</dimen> <dimen name="top_padding">0dp</dimen> diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java index 11c4b6a27968..260b39378485 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java @@ -72,13 +72,16 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS * Clock for both small and large sizes */ private AnimatableClockController mClockViewController; - private FrameLayout mClockFrame; + private FrameLayout mClockFrame; // top aligned clock private AnimatableClockController mLargeClockViewController; - private FrameLayout mLargeClockFrame; + private FrameLayout mLargeClockFrame; // centered clock private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; private final KeyguardBypassController mBypassController; + private int mLargeClockTopMargin = 0; + private int mKeyguardClockTopMargin = 0; + /** * Listener for changes to the color palette. * @@ -177,6 +180,8 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS } mColorExtractor.addOnColorsChangedListener(mColorsListener); mView.updateColors(getGradientColors()); + mKeyguardClockTopMargin = + mView.getResources().getDimensionPixelSize(R.dimen.keyguard_clock_top_margin); if (mOnlyClock) { View ksa = mView.findViewById(R.id.keyguard_status_area); @@ -241,6 +246,8 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS */ public void onDensityOrFontScaleChanged() { mView.onDensityOrFontScaleChanged(); + mKeyguardClockTopMargin = + mView.getResources().getDimensionPixelSize(R.dimen.keyguard_clock_top_margin); updateClockLayout(); } @@ -249,9 +256,12 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS if (mSmartspaceController.isEnabled()) { RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT); - lp.topMargin = getContext().getResources().getDimensionPixelSize( + mLargeClockTopMargin = getContext().getResources().getDimensionPixelSize( R.dimen.keyguard_large_clock_top_margin); + lp.topMargin = mLargeClockTopMargin; mLargeClockFrame.setLayoutParams(lp); + } else { + mLargeClockTopMargin = 0; } } @@ -363,6 +373,28 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS } } + /** + * Get y-bottom position of the currently visible clock on the keyguard. + * We can't directly getBottom() because clock changes positions in AOD for burn-in + */ + int getClockBottom(int statusBarHeaderHeight) { + if (mLargeClockFrame.getVisibility() == View.VISIBLE) { + View clock = mLargeClockFrame.findViewById( + com.android.systemui.R.id.animatable_clock_view_large); + int frameHeight = mLargeClockFrame.getHeight(); + int clockHeight = clock.getHeight(); + return frameHeight / 2 + clockHeight / 2; + } else { + return mClockFrame.findViewById( + com.android.systemui.R.id.animatable_clock_view).getHeight() + + statusBarHeaderHeight + mKeyguardClockTopMargin; + } + } + + boolean isClockTopAligned() { + return mLargeClockFrame.getVisibility() != View.VISIBLE; + } + private void updateAodIcons() { NotificationIconContainer nic = (NotificationIconContainer) mView.findViewById( diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java index 8bf8e0926095..d23b1c88b345 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java @@ -187,6 +187,20 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV } /** + * Get y-bottom position of the currently visible clock. + */ + public int getClockBottom(int statusBarHeaderHeight) { + return mKeyguardClockSwitchController.getClockBottom(statusBarHeaderHeight); + } + + /** + * @return true if the currently displayed clock is top aligned (as opposed to center aligned) + */ + public boolean isClockTopAligned() { + return mKeyguardClockSwitchController.isClockTopAligned(); + } + + /** * Set whether the view accessibility importance mode. */ public void setStatusAccessibilityImportance(int mode) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java index d348954e49bb..e368aad31ac8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java @@ -57,6 +57,21 @@ public class KeyguardClockPositionAlgorithm { private int mUserSwitchPreferredY; /** + * Whether or not there is a custom clock face on keyguard. + */ + private boolean mHasCustomClock; + + /** + * Whether or not the NSSL contains any visible notifications. + */ + private boolean mHasVisibleNotifs; + + /** + * Height of notification stack: Sum of height of each notification. + */ + private int mNotificationStackHeight; + + /** * Minimum top margin to avoid overlap with status bar, lock icon, or multi-user switcher * avatar. */ @@ -113,6 +128,25 @@ public class KeyguardClockPositionAlgorithm { private boolean mIsSplitShade; /** + * Top location of the udfps icon. This includes the worst case (highest) burn-in + * offset that would make the top physically highest on the screen. + * + * Set to -1 if udfps is not enrolled on the device. + */ + private float mUdfpsTop; + + /** + * Bottom y-position of the currently visible clock + */ + private float mClockBottom; + + /** + * If true, try to keep clock aligned to the top of the display. Else, assume the clock + * is center aligned. + */ + private boolean mIsClockTopAligned; + + /** * Refreshes the dimension values. */ public void loadDimens(Resources res) { @@ -120,7 +154,7 @@ public class KeyguardClockPositionAlgorithm { R.dimen.keyguard_status_view_bottom_margin); mContainerTopPadding = - res.getDimensionPixelSize(R.dimen.keyguard_clock_top_margin) / 2; + res.getDimensionPixelSize(R.dimen.keyguard_clock_top_margin); mBurnInPreventionOffsetX = res.getDimensionPixelSize( R.dimen.burn_in_prevention_offset_x); mBurnInPreventionOffsetYClock = res.getDimensionPixelSize( @@ -134,7 +168,7 @@ public class KeyguardClockPositionAlgorithm { int keyguardStatusHeight, int userSwitchHeight, int userSwitchPreferredY, float dark, float overStretchAmount, boolean bypassEnabled, int unlockedStackScrollerPadding, float qsExpansion, int cutoutTopInset, - boolean isSplitShade) { + boolean isSplitShade, float udfpsTop, float clockBottom, boolean isClockTopAligned) { mMinTopMargin = keyguardStatusBarHeaderHeight + Math.max(mContainerTopPadding, userSwitchHeight); mPanelExpansion = panelExpansion; @@ -148,6 +182,9 @@ public class KeyguardClockPositionAlgorithm { mQsExpansion = qsExpansion; mCutoutTopInset = cutoutTopInset; mIsSplitShade = isSplitShade; + mUdfpsTop = udfpsTop; + mClockBottom = clockBottom; + mIsClockTopAligned = isClockTopAligned; } public void run(Result result) { @@ -202,8 +239,34 @@ public class KeyguardClockPositionAlgorithm { if (clockY - mBurnInPreventionOffsetYClock < mCutoutTopInset) { shift = mCutoutTopInset - (clockY - mBurnInPreventionOffsetYClock); } - float clockYDark = clockY + burnInPreventionOffsetY() + shift; + int burnInPreventionOffsetY = mBurnInPreventionOffsetYClock; // requested offset + final boolean hasUdfps = mUdfpsTop > -1; + if (hasUdfps && !mIsClockTopAligned) { + // ensure clock doesn't overlap with the udfps icon + if (mUdfpsTop < mClockBottom) { + // sometimes the clock textView extends beyond udfps, so let's just use the + // space above the KeyguardStatusView/clock as our burn-in offset + burnInPreventionOffsetY = (int) (clockY - mCutoutTopInset) / 2; + if (mBurnInPreventionOffsetYClock < burnInPreventionOffsetY) { + burnInPreventionOffsetY = mBurnInPreventionOffsetYClock; + } + shift = -burnInPreventionOffsetY; + } else { + float upperSpace = clockY - mCutoutTopInset; + float lowerSpace = mUdfpsTop - mClockBottom; + // center the burn-in offset within the upper + lower space + burnInPreventionOffsetY = (int) (lowerSpace + upperSpace) / 2; + if (mBurnInPreventionOffsetYClock < burnInPreventionOffsetY) { + burnInPreventionOffsetY = mBurnInPreventionOffsetYClock; + } + shift = (lowerSpace - upperSpace) / 2; + } + } + + float clockYDark = clockY + + burnInPreventionOffsetY(burnInPreventionOffsetY) + + shift; return (int) (MathUtils.lerp(clockY, clockYDark, darkAmount) + mOverStretchAmount); } @@ -235,9 +298,7 @@ public class KeyguardClockPositionAlgorithm { return MathUtils.lerp(alphaKeyguard, 1f, mDarkAmount); } - private float burnInPreventionOffsetY() { - int offset = mBurnInPreventionOffsetYClock; - + private float burnInPreventionOffsetY(int offset) { return getBurnInOffset(offset * 2, false /* xAxis */) - offset; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java index 2dd60fbd0a5a..1e4d97d268b3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -57,6 +57,9 @@ import android.graphics.PointF; import android.graphics.Rect; import android.graphics.Region; import android.graphics.drawable.Drawable; +import android.hardware.biometrics.BiometricSourceType; +import android.hardware.biometrics.SensorLocationInternal; +import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.os.Bundle; import android.os.Handler; import android.os.PowerManager; @@ -568,6 +571,8 @@ public class NotificationPanelViewController extends PanelViewController { */ private float mKeyguardOnlyContentAlpha = 1.0f; + private float mUdfpsMaxYBurnInOffset; + /** * Are we currently in gesture navigation */ @@ -907,6 +912,7 @@ public class NotificationPanelViewController extends PanelViewController { mView.getContext()); mLockscreenNotificationQSPadding = mResources.getDimensionPixelSize( R.dimen.notification_side_paddings); + mUdfpsMaxYBurnInOffset = mResources.getDimensionPixelSize(R.dimen.udfps_burn_in_offset_y); } private void updateViewControllers(KeyguardStatusView keyguardStatusView, @@ -1265,7 +1271,17 @@ public class NotificationPanelViewController extends PanelViewController { float darkamount = mUnlockedScreenOffAnimationController.isScreenOffAnimationPlaying() ? 1.0f : mInterpolatedDarkAmount; - mClockPositionAlgorithm.setup(mStatusBarHeaderHeightKeyguard, + + float udfpsAodTopLocation = -1f; + if (mUpdateMonitor.isUdfpsEnrolled() && mAuthController.getUdfpsProps().size() > 0) { + FingerprintSensorPropertiesInternal props = mAuthController.getUdfpsProps().get(0); + final SensorLocationInternal location = props.getLocation(); + udfpsAodTopLocation = location.sensorLocationY - location.sensorRadius + - mUdfpsMaxYBurnInOffset; + } + + mClockPositionAlgorithm.setup( + mStatusBarHeaderHeightKeyguard, expandedFraction, mKeyguardStatusViewController.getLockscreenHeight(), userIconHeight, @@ -1274,7 +1290,10 @@ public class NotificationPanelViewController extends PanelViewController { bypassEnabled, getUnlockedStackScrollerPadding(), computeQsExpansionFraction(), mDisplayTopInset, - mShouldUseSplitNotificationShade); + mShouldUseSplitNotificationShade, + udfpsAodTopLocation, + mKeyguardStatusViewController.getClockBottom(mStatusBarHeaderHeightKeyguard), + mKeyguardStatusViewController.isClockTopAligned()); mClockPositionAlgorithm.run(mClockPositionResult); boolean animate = mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending(); boolean animateClock = animate || mAnimateNextPositionUpdate; diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java index 8d615f050af9..88b596c26c0f 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java @@ -119,6 +119,7 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase { when(mNotificationIcons.getLayoutParams()).thenReturn( mock(RelativeLayout.LayoutParams.class)); when(mView.getContext()).thenReturn(getContext()); + when(mView.getResources()).thenReturn(mResources); when(mView.findViewById(R.id.animatable_clock_view)).thenReturn(mClockView); when(mView.findViewById(R.id.animatable_clock_view_large)).thenReturn(mLargeClockView); @@ -127,7 +128,6 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase { when(mLargeClockView.getContext()).thenReturn(getContext()); when(mView.isAttachedToWindow()).thenReturn(true); - when(mResources.getString(anyInt())).thenReturn("h:mm"); when(mSmartspaceController.buildAndConnectView(any())).thenReturn(mFakeSmartspaceView); mController = new KeyguardClockSwitchController( mView, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java index f34f21bde803..d098e1a0b8a5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java @@ -16,42 +16,80 @@ package com.android.systemui.statusbar.phone; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; + import static com.google.common.truth.Truth.assertThat; +import static org.mockito.AdditionalAnswers.returnsFirstArg; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.when; + +import android.content.res.Resources; import android.testing.AndroidTestingRunner; import androidx.test.filters.SmallTest; +import com.android.systemui.R; import com.android.systemui.SysuiTestCase; +import com.android.systemui.doze.util.BurnInHelperKt; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.MockitoSession; @SmallTest @RunWith(AndroidTestingRunner.class) public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase { private static final int SCREEN_HEIGHT = 2000; - private static final int EMPTY_MARGIN = 0; private static final int EMPTY_HEIGHT = 0; private static final float ZERO_DRAG = 0.f; private static final float OPAQUE = 1.f; private static final float TRANSPARENT = 0.f; + + @Mock + private Resources mResources; + private KeyguardClockPositionAlgorithm mClockPositionAlgorithm; private KeyguardClockPositionAlgorithm.Result mClockPosition; + + private MockitoSession mStaticMockSession; + private int mNotificationStackHeight; + private float mPanelExpansion; + private int mKeyguardStatusBarHeaderHeight; private int mKeyguardStatusHeight; private float mDark; private float mQsExpansion; - private int mCutoutTopInsetPx = 0; + private int mCutoutTopInset = 0; private boolean mIsSplitShade = false; + private float mUdfpsTop = -1; + private float mClockBottom = SCREEN_HEIGHT / 2; + private boolean mClockTopAligned; @Before public void setUp() { + MockitoAnnotations.initMocks(this); + mStaticMockSession = mockitoSession() + .mockStatic(BurnInHelperKt.class) + .startMocking(); + mClockPositionAlgorithm = new KeyguardClockPositionAlgorithm(); + when(mResources.getDimensionPixelSize(anyInt())).thenReturn(0); + mClockPositionAlgorithm.loadDimens(mResources); + mClockPosition = new KeyguardClockPositionAlgorithm.Result(); } + @After + public void tearDown() { + mStaticMockSession.finishMocking(); + } + @Test public void clockPositionTopOfScreenOnAOD() { // GIVEN on AOD and clock has 0 height @@ -71,7 +109,7 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase { // GIVEN on AOD and clock has 0 height givenAOD(); mKeyguardStatusHeight = EMPTY_HEIGHT; - mCutoutTopInsetPx = 300; + mCutoutTopInset = 300; // WHEN the clock position algorithm is run positionClock(); // THEN the clock Y position is below the cutout @@ -271,6 +309,155 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase { assertThat(mClockPosition.clockAlpha).isEqualTo(TRANSPARENT); } + @Test + public void clockPositionMinimizesBurnInMovementToAvoidUdfpsOnAOD() { + // GIVEN a center aligned clock + mClockTopAligned = false; + + // GIVEN the clock + udfps are 100px apart + mClockBottom = SCREEN_HEIGHT - 500; + mUdfpsTop = SCREEN_HEIGHT - 400; + + // GIVEN it's AOD and the burn-in y value is 200 + givenAOD(); + givenMaxBurnInOffset(200); + + // WHEN the clock position algorithm is run with the highest burn in offset + givenHighestBurnInOffset(); + positionClock(); + + // THEN the worst-case clock Y position is shifted only by 100 (not the full 200), + // so that it's at the same location as mUdfpsTop + assertThat(mClockPosition.clockY).isEqualTo(100); + + // WHEN the clock position algorithm is run with the lowest burn in offset + givenLowestBurnInOffset(); + positionClock(); + + // THEN lowest case starts at 0 + assertThat(mClockPosition.clockY).isEqualTo(0); + } + + @Test + public void clockPositionShiftsToAvoidUdfpsOnAOD_usesSpaceAboveClock() { + // GIVEN a center aligned clock + mClockTopAligned = false; + + // GIVEN there's space at the top of the screen on LS (that's available to be used for + // burn-in on AOD) + mKeyguardStatusBarHeaderHeight = 150; + + // GIVEN the bottom of the clock is beyond the top of UDFPS + mClockBottom = SCREEN_HEIGHT - 300; + mUdfpsTop = SCREEN_HEIGHT - 400; + + // GIVEN it's AOD and the burn-in y value is 200 + givenAOD(); + givenMaxBurnInOffset(200); + + // WHEN the clock position algorithm is run with the highest burn in offset + givenHighestBurnInOffset(); + positionClock(); + + // THEN the algo should shift the clock up and use the area above the clock for + // burn-in since the burn in offset > space above clock + assertThat(mClockPosition.clockY).isEqualTo(mKeyguardStatusBarHeaderHeight); + + // WHEN the clock position algorithm is run with the lowest burn in offset + givenLowestBurnInOffset(); + positionClock(); + + // THEN lowest case starts at mCutoutTopInset (0 in this case) + assertThat(mClockPosition.clockY).isEqualTo(mCutoutTopInset); + } + + @Test + public void clockPositionShiftsToAvoidUdfpsOnAOD_usesMaxBurnInOffset() { + // GIVEN a center aligned clock + mClockTopAligned = false; + + // GIVEN there's 200px space at the top of the screen on LS (that's available to be used for + // burn-in on AOD) but 50px are taken up by the cutout + mKeyguardStatusBarHeaderHeight = 200; + mCutoutTopInset = 50; + + // GIVEN the bottom of the clock is beyond the top of UDFPS + mClockBottom = SCREEN_HEIGHT - 300; + mUdfpsTop = SCREEN_HEIGHT - 400; + + // GIVEN it's AOD and the burn-in y value is only 25px (less than space above clock) + givenAOD(); + int maxYBurnInOffset = 25; + givenMaxBurnInOffset(maxYBurnInOffset); + + // WHEN the clock position algorithm is run with the highest burn in offset + givenHighestBurnInOffset(); + positionClock(); + + // THEN the algo should shift the clock up and use the area above the clock for + // burn-in + assertThat(mClockPosition.clockY).isEqualTo(mKeyguardStatusBarHeaderHeight); + + // WHEN the clock position algorithm is run with the lowest burn in offset + givenLowestBurnInOffset(); + positionClock(); + + // THEN lowest case starts above mKeyguardStatusBarHeaderHeight + assertThat(mClockPosition.clockY).isEqualTo( + mKeyguardStatusBarHeaderHeight - 2 * maxYBurnInOffset); + } + + @Test + public void clockPositionShiftsToMaximizeUdfpsBurnInMovement() { + // GIVEN a center aligned clock + mClockTopAligned = false; + + // GIVEN there's 200px space at the top of the screen on LS (that's available to be used for + // burn-in on AOD) but 50px are taken up by the cutout + mKeyguardStatusBarHeaderHeight = 200; + mCutoutTopInset = 50; + int upperSpaceAvailable = mKeyguardStatusBarHeaderHeight - mCutoutTopInset; + + // GIVEN the bottom of the clock and the top of UDFPS are 100px apart + mClockBottom = SCREEN_HEIGHT - 500; + mUdfpsTop = SCREEN_HEIGHT - 400; + float lowerSpaceAvailable = mUdfpsTop - mClockBottom; + + // GIVEN it's AOD and the burn-in y value is 200 + givenAOD(); + givenMaxBurnInOffset(200); + + // WHEN the clock position algorithm is run with the highest burn in offset + givenHighestBurnInOffset(); + positionClock(); + + // THEN the algo should shift the clock up and use both the area above + // the clock and below the clock (vertically centered in its allowed area) + assertThat(mClockPosition.clockY).isEqualTo( + (int) (mCutoutTopInset + upperSpaceAvailable + lowerSpaceAvailable)); + + // WHEN the clock position algorithm is run with the lowest burn in offset + givenLowestBurnInOffset(); + positionClock(); + + // THEN lowest case starts at mCutoutTopInset + assertThat(mClockPosition.clockY).isEqualTo(mCutoutTopInset); + } + + private void givenHighestBurnInOffset() { + when(BurnInHelperKt.getBurnInOffset(anyInt(), anyBoolean())).then(returnsFirstArg()); + } + + private void givenLowestBurnInOffset() { + when(BurnInHelperKt.getBurnInOffset(anyInt(), anyBoolean())).thenReturn(0); + } + + private void givenMaxBurnInOffset(int offset) { + when(mResources.getDimensionPixelSize(R.dimen.burn_in_prevention_offset_y_clock)) + .thenReturn(offset); + mClockPositionAlgorithm.loadDimens(mResources); + } + private void givenAOD() { mPanelExpansion = 1.f; mDark = 1.f; @@ -281,12 +468,28 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase { mDark = 0.f; } + /** + * Setup and run the clock position algorithm. + * + * mClockPosition.clockY will contain the top y-coordinate for the clock position + */ private void positionClock() { - mClockPositionAlgorithm.setup(EMPTY_MARGIN, mPanelExpansion, mKeyguardStatusHeight, - 0 /* userSwitchHeight */, 0 /* userSwitchPreferredY */, - mDark, ZERO_DRAG, false /* bypassEnabled */, - 0 /* unlockedStackScrollerPadding */, mQsExpansion, - mCutoutTopInsetPx, mIsSplitShade); + mClockPositionAlgorithm.setup( + mKeyguardStatusBarHeaderHeight, + mPanelExpansion, + mKeyguardStatusHeight, + 0 /* userSwitchHeight */, + 0 /* userSwitchPreferredY */, + mDark, + ZERO_DRAG, + false /* bypassEnabled */, + 0 /* unlockedStackScrollerPadding */, + mQsExpansion, + mCutoutTopInset, + mIsSplitShade, + mUdfpsTop, + mClockBottom, + mClockTopAligned); mClockPositionAlgorithm.run(mClockPosition); } } |