Merge "Update clock burn-in so it doesn't clash with udpfs" into sc-qpr1-dev

This commit is contained in:
TreeHugger Robot
2021-09-27 13:53:44 +00:00
committed by Android (Google) Code Review
8 changed files with 337 additions and 28 deletions

View File

@@ -16,7 +16,7 @@
<resources> <resources>
<!-- Minimum margin between clock and top of screen or ambient indication --> <!-- 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) --> <!-- Large clock maximum font size (dp is intentional, to prevent any further scaling) -->
<dimen name="large_clock_text_size">200dp</dimen> <dimen name="large_clock_text_size">200dp</dimen>

View File

@@ -743,9 +743,7 @@
<!-- The margin between the status view and the notifications on Keyguard.--> <!-- The margin between the status view and the notifications on Keyguard.-->
<dimen name="keyguard_status_view_bottom_margin">20dp</dimen> <dimen name="keyguard_status_view_bottom_margin">20dp</dimen>
<!-- Minimum margin between clock and status bar --> <!-- Minimum margin between clock and status bar -->
<dimen name="keyguard_clock_top_margin">36dp</dimen> <dimen name="keyguard_clock_top_margin">18dp</dimen>
<!-- The margin between top of clock and bottom of lock icon. -->
<dimen name="keyguard_clock_lock_margin">16dp</dimen>
<!-- The amount to shift the clocks during a small/large transition --> <!-- The amount to shift the clocks during a small/large transition -->
<dimen name="keyguard_clock_switch_y_shift">10dp</dimen> <dimen name="keyguard_clock_switch_y_shift">10dp</dimen>
<!-- When large clock is showing, offset the smartspace by this amount --> <!-- When large clock is showing, offset the smartspace by this amount -->
@@ -1149,9 +1147,9 @@
<dimen name="default_burn_in_prevention_offset">15dp</dimen> <dimen name="default_burn_in_prevention_offset">15dp</dimen>
<!-- The maximum offset for the under-display fingerprint sensor (UDFPS) icon in either <!-- The maximum offset for the under-display fingerprint sensor (UDFPS) icon in either
direction that elements aer moved to prevent burn-in on AOD--> direction that elements are moved to prevent burn-in on AOD-->
<dimen name="udfps_burn_in_offset_x">2dp</dimen> <dimen name="udfps_burn_in_offset_x">7px</dimen>
<dimen name="udfps_burn_in_offset_y">8dp</dimen> <dimen name="udfps_burn_in_offset_y">28px</dimen>
<dimen name="corner_size">8dp</dimen> <dimen name="corner_size">8dp</dimen>
<dimen name="top_padding">0dp</dimen> <dimen name="top_padding">0dp</dimen>

View File

@@ -70,13 +70,16 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
* Clock for both small and large sizes * Clock for both small and large sizes
*/ */
private AnimatableClockController mClockViewController; private AnimatableClockController mClockViewController;
private FrameLayout mClockFrame; private FrameLayout mClockFrame; // top aligned clock
private AnimatableClockController mLargeClockViewController; private AnimatableClockController mLargeClockViewController;
private FrameLayout mLargeClockFrame; private FrameLayout mLargeClockFrame; // centered clock
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final KeyguardBypassController mBypassController; private final KeyguardBypassController mBypassController;
private int mLargeClockTopMargin = 0;
private int mKeyguardClockTopMargin = 0;
/** /**
* Listener for changes to the color palette. * Listener for changes to the color palette.
* *
@@ -175,6 +178,8 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
} }
mColorExtractor.addOnColorsChangedListener(mColorsListener); mColorExtractor.addOnColorsChangedListener(mColorsListener);
mView.updateColors(getGradientColors()); mView.updateColors(getGradientColors());
mKeyguardClockTopMargin =
mView.getResources().getDimensionPixelSize(R.dimen.keyguard_clock_top_margin);
if (mOnlyClock) { if (mOnlyClock) {
View ksa = mView.findViewById(R.id.keyguard_status_area); View ksa = mView.findViewById(R.id.keyguard_status_area);
@@ -249,6 +254,8 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
*/ */
public void onDensityOrFontScaleChanged() { public void onDensityOrFontScaleChanged() {
mView.onDensityOrFontScaleChanged(); mView.onDensityOrFontScaleChanged();
mKeyguardClockTopMargin =
mView.getResources().getDimensionPixelSize(R.dimen.keyguard_clock_top_margin);
updateClockLayout(); updateClockLayout();
} }
@@ -257,9 +264,12 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
if (mSmartspaceController.isEnabled()) { if (mSmartspaceController.isEnabled()) {
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(MATCH_PARENT, RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(MATCH_PARENT,
MATCH_PARENT); MATCH_PARENT);
lp.topMargin = getContext().getResources().getDimensionPixelSize( mLargeClockTopMargin = getContext().getResources().getDimensionPixelSize(
R.dimen.keyguard_large_clock_top_margin); R.dimen.keyguard_large_clock_top_margin);
lp.topMargin = mLargeClockTopMargin;
mLargeClockFrame.setLayoutParams(lp); mLargeClockFrame.setLayoutParams(lp);
} else {
mLargeClockTopMargin = 0;
} }
} }
@@ -369,6 +379,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() { private void updateAodIcons() {
NotificationIconContainer nic = (NotificationIconContainer) NotificationIconContainer nic = (NotificationIconContainer)
mView.findViewById( mView.findViewById(

View File

@@ -184,6 +184,20 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
return mView.getHeight() - mKeyguardClockSwitchController.getNotificationIconAreaHeight(); return mView.getHeight() - mKeyguardClockSwitchController.getNotificationIconAreaHeight();
} }
/**
* 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. * Set whether the view accessibility importance mode.
*/ */

View File

@@ -83,8 +83,7 @@ public class KeyguardClockPositionAlgorithm {
private int mNotificationStackHeight; private int mNotificationStackHeight;
/** /**
* Minimum top margin to avoid overlap with status bar, lock icon, or multi-user switcher * Minimum top margin to avoid overlap with status bar, or multi-user switcher avatar.
* avatar.
*/ */
private int mMinTopMargin; private int mMinTopMargin;
@@ -149,6 +148,25 @@ public class KeyguardClockPositionAlgorithm {
private boolean mIsSplitShade; 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. * Refreshes the dimension values.
*/ */
@@ -157,7 +175,7 @@ public class KeyguardClockPositionAlgorithm {
R.dimen.keyguard_status_view_bottom_margin); R.dimen.keyguard_status_view_bottom_margin);
mContainerTopPadding = mContainerTopPadding =
res.getDimensionPixelSize(R.dimen.keyguard_clock_top_margin) / 2; res.getDimensionPixelSize(R.dimen.keyguard_clock_top_margin);
mBurnInPreventionOffsetX = res.getDimensionPixelSize( mBurnInPreventionOffsetX = res.getDimensionPixelSize(
R.dimen.burn_in_prevention_offset_x); R.dimen.burn_in_prevention_offset_x);
mBurnInPreventionOffsetY = res.getDimensionPixelSize( mBurnInPreventionOffsetY = res.getDimensionPixelSize(
@@ -174,7 +192,8 @@ public class KeyguardClockPositionAlgorithm {
int keyguardStatusHeight, int userSwitchHeight, int userSwitchPreferredY, int keyguardStatusHeight, int userSwitchHeight, int userSwitchPreferredY,
boolean hasCustomClock, boolean hasVisibleNotifs, float dark, boolean hasCustomClock, boolean hasVisibleNotifs, float dark,
float overStrechAmount, boolean bypassEnabled, int unlockedStackScrollerPadding, float overStrechAmount, boolean bypassEnabled, int unlockedStackScrollerPadding,
float qsExpansion, int cutoutTopInset, boolean isSplitShade) { float qsExpansion, int cutoutTopInset, boolean isSplitShade, float udfpsTop,
float clockBottom, boolean isClockTopAligned) {
mMinTopMargin = keyguardStatusBarHeaderHeight + Math.max(mContainerTopPadding, mMinTopMargin = keyguardStatusBarHeaderHeight + Math.max(mContainerTopPadding,
userSwitchHeight); userSwitchHeight);
mMaxShadeBottom = maxShadeBottom; mMaxShadeBottom = maxShadeBottom;
@@ -193,6 +212,9 @@ public class KeyguardClockPositionAlgorithm {
mQsExpansion = qsExpansion; mQsExpansion = qsExpansion;
mCutoutTopInset = cutoutTopInset; mCutoutTopInset = cutoutTopInset;
mIsSplitShade = isSplitShade; mIsSplitShade = isSplitShade;
mUdfpsTop = udfpsTop;
mClockBottom = clockBottom;
mIsClockTopAligned = isClockTopAligned;
} }
public void run(Result result) { public void run(Result result) {
@@ -247,8 +269,34 @@ public class KeyguardClockPositionAlgorithm {
if (clockY - mBurnInPreventionOffsetYLargeClock < mCutoutTopInset) { if (clockY - mBurnInPreventionOffsetYLargeClock < mCutoutTopInset) {
shift = mCutoutTopInset - (clockY - mBurnInPreventionOffsetYLargeClock); shift = mCutoutTopInset - (clockY - mBurnInPreventionOffsetYLargeClock);
} }
float clockYDark = clockY + burnInPreventionOffsetY() + shift;
int burnInPreventionOffsetY = mBurnInPreventionOffsetYLargeClock; // 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 (mBurnInPreventionOffsetYLargeClock < burnInPreventionOffsetY) {
burnInPreventionOffsetY = mBurnInPreventionOffsetYLargeClock;
}
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 (mBurnInPreventionOffsetYLargeClock < burnInPreventionOffsetY) {
burnInPreventionOffsetY = mBurnInPreventionOffsetYLargeClock;
}
shift = (lowerSpace - upperSpace) / 2;
}
}
float clockYDark = clockY
+ burnInPreventionOffsetY(burnInPreventionOffsetY)
+ shift;
return (int) (MathUtils.lerp(clockY, clockYDark, darkAmount) + mOverStretchAmount); return (int) (MathUtils.lerp(clockY, clockYDark, darkAmount) + mOverStretchAmount);
} }
@@ -280,9 +328,7 @@ public class KeyguardClockPositionAlgorithm {
return MathUtils.lerp(alphaKeyguard, 1f, mDarkAmount); return MathUtils.lerp(alphaKeyguard, 1f, mDarkAmount);
} }
private float burnInPreventionOffsetY() { private float burnInPreventionOffsetY(int offset) {
int offset = mBurnInPreventionOffsetYLargeClock;
return getBurnInOffset(offset * 2, false /* xAxis */) - offset; return getBurnInOffset(offset * 2, false /* xAxis */) - offset;
} }

View File

@@ -51,6 +51,7 @@ import android.graphics.Rect;
import android.graphics.Region; import android.graphics.Region;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.hardware.biometrics.BiometricSourceType; import android.hardware.biometrics.BiometricSourceType;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.PowerManager; import android.os.PowerManager;
@@ -621,6 +622,8 @@ public class NotificationPanelViewController extends PanelViewController {
*/ */
private float mKeyguardOnlyContentAlpha = 1.0f; private float mKeyguardOnlyContentAlpha = 1.0f;
private float mUdfpsMaxYBurnInOffset;
/** /**
* Are we currently in gesture navigation * Are we currently in gesture navigation
*/ */
@@ -957,6 +960,7 @@ public class NotificationPanelViewController extends PanelViewController {
mScreenCornerRadius = (int) ScreenDecorationsUtils.getWindowCornerRadius(mResources); mScreenCornerRadius = (int) ScreenDecorationsUtils.getWindowCornerRadius(mResources);
mLockscreenNotificationQSPadding = mResources.getDimensionPixelSize( mLockscreenNotificationQSPadding = mResources.getDimensionPixelSize(
R.dimen.notification_side_paddings); R.dimen.notification_side_paddings);
mUdfpsMaxYBurnInOffset = mResources.getDimensionPixelSize(R.dimen.udfps_burn_in_offset_y);
} }
private void updateViewControllers(KeyguardStatusView keyguardStatusView, private void updateViewControllers(KeyguardStatusView keyguardStatusView,
@@ -1301,7 +1305,16 @@ public class NotificationPanelViewController extends PanelViewController {
float darkamount = float darkamount =
mUnlockedScreenOffAnimationController.isScreenOffAnimationPlaying() mUnlockedScreenOffAnimationController.isScreenOffAnimationPlaying()
? 1.0f : mInterpolatedDarkAmount; ? 1.0f : mInterpolatedDarkAmount;
mClockPositionAlgorithm.setup(mStatusBarHeaderHeightKeyguard,
float udfpsAodTopLocation = -1f;
if (mUpdateMonitor.isUdfpsEnrolled() && mAuthController.getUdfpsProps().size() > 0) {
FingerprintSensorPropertiesInternal props = mAuthController.getUdfpsProps().get(0);
udfpsAodTopLocation = props.sensorLocationY - props.sensorRadius
- mUdfpsMaxYBurnInOffset;
}
mClockPositionAlgorithm.setup(
mStatusBarHeaderHeightKeyguard,
totalHeight - bottomPadding, totalHeight - bottomPadding,
mNotificationStackScrollLayoutController.getIntrinsicContentHeight(), mNotificationStackScrollLayoutController.getIntrinsicContentHeight(),
expandedFraction, expandedFraction,
@@ -1313,7 +1326,10 @@ public class NotificationPanelViewController extends PanelViewController {
bypassEnabled, getUnlockedStackScrollerPadding(), bypassEnabled, getUnlockedStackScrollerPadding(),
computeQsExpansionFraction(), computeQsExpansionFraction(),
mDisplayTopInset, mDisplayTopInset,
mShouldUseSplitNotificationShade); mShouldUseSplitNotificationShade,
udfpsAodTopLocation,
mKeyguardStatusViewController.getClockBottom(mStatusBarHeaderHeightKeyguard),
mKeyguardStatusViewController.isClockTopAligned());
mClockPositionAlgorithm.run(mClockPositionResult); mClockPositionAlgorithm.run(mClockPositionResult);
boolean animate = mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending(); boolean animate = mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending();
boolean animateClock = animate || mAnimateNextPositionUpdate; boolean animateClock = animate || mAnimateNextPositionUpdate;

View File

@@ -119,6 +119,7 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase {
when(mNotificationIcons.getLayoutParams()).thenReturn( when(mNotificationIcons.getLayoutParams()).thenReturn(
mock(RelativeLayout.LayoutParams.class)); mock(RelativeLayout.LayoutParams.class));
when(mView.getContext()).thenReturn(getContext()); 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)).thenReturn(mClockView);
when(mView.findViewById(R.id.animatable_clock_view_large)).thenReturn(mLargeClockView); 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(mLargeClockView.getContext()).thenReturn(getContext());
when(mView.isAttachedToWindow()).thenReturn(true); when(mView.isAttachedToWindow()).thenReturn(true);
when(mResources.getString(anyInt())).thenReturn("h:mm");
when(mSmartspaceController.buildAndConnectView(any())).thenReturn(mFakeSmartspaceView); when(mSmartspaceController.buildAndConnectView(any())).thenReturn(mFakeSmartspaceView);
mController = new KeyguardClockSwitchController( mController = new KeyguardClockSwitchController(
mView, mView,

View File

@@ -16,24 +16,37 @@
package com.android.systemui.statusbar.phone; 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 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 android.testing.AndroidTestingRunner;
import androidx.test.filters.SmallTest; import androidx.test.filters.SmallTest;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase; import com.android.systemui.SysuiTestCase;
import com.android.systemui.doze.util.BurnInHelperKt;
import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.MockitoSession;
@SmallTest @SmallTest
@RunWith(AndroidTestingRunner.class) @RunWith(AndroidTestingRunner.class)
public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase { public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase {
private static final int SCREEN_HEIGHT = 2000; private static final int SCREEN_HEIGHT = 2000;
private static final int EMPTY_MARGIN = 0;
private static final int EMPTY_HEIGHT = 0; private static final int EMPTY_HEIGHT = 0;
private static final float ZERO_DRAG = 0.f; private static final float ZERO_DRAG = 0.f;
private static final float OPAQUE = 1.f; private static final float OPAQUE = 1.f;
@@ -41,10 +54,15 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase {
private static final boolean HAS_CUSTOM_CLOCK = false; private static final boolean HAS_CUSTOM_CLOCK = false;
private static final boolean HAS_VISIBLE_NOTIFS = false; private static final boolean HAS_VISIBLE_NOTIFS = false;
@Mock
private Resources mResources;
private KeyguardClockPositionAlgorithm mClockPositionAlgorithm; private KeyguardClockPositionAlgorithm mClockPositionAlgorithm;
private KeyguardClockPositionAlgorithm.Result mClockPosition; private KeyguardClockPositionAlgorithm.Result mClockPosition;
private MockitoSession mStaticMockSession;
private int mNotificationStackHeight; private int mNotificationStackHeight;
private float mPanelExpansion; private float mPanelExpansion;
private int mKeyguardStatusBarHeaderHeight;
private int mKeyguardStatusHeight; private int mKeyguardStatusHeight;
private float mDark; private float mDark;
private boolean mHasCustomClock; private boolean mHasCustomClock;
@@ -52,16 +70,32 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase {
private float mQsExpansion; private float mQsExpansion;
private int mCutoutTopInset = 0; // in pixels private int mCutoutTopInset = 0; // in pixels
private boolean mIsSplitShade = false; private boolean mIsSplitShade = false;
private float mUdfpsTop = -1;
private float mClockBottom = SCREEN_HEIGHT / 2;
private boolean mClockTopAligned;
@Before @Before
public void setUp() { public void setUp() {
MockitoAnnotations.initMocks(this);
mStaticMockSession = mockitoSession()
.mockStatic(BurnInHelperKt.class)
.startMocking();
mClockPositionAlgorithm = new KeyguardClockPositionAlgorithm(); mClockPositionAlgorithm = new KeyguardClockPositionAlgorithm();
when(mResources.getDimensionPixelSize(anyInt())).thenReturn(0);
mClockPositionAlgorithm.loadDimens(mResources);
mClockPosition = new KeyguardClockPositionAlgorithm.Result(); mClockPosition = new KeyguardClockPositionAlgorithm.Result();
mHasCustomClock = HAS_CUSTOM_CLOCK; mHasCustomClock = HAS_CUSTOM_CLOCK;
mHasVisibleNotifs = HAS_VISIBLE_NOTIFS; mHasVisibleNotifs = HAS_VISIBLE_NOTIFS;
} }
@After
public void tearDown() {
mStaticMockSession.finishMocking();
}
@Test @Test
public void clockPositionTopOfScreenOnAOD() { public void clockPositionTopOfScreenOnAOD() {
// GIVEN on AOD and both stack scroll and clock have 0 height // GIVEN on AOD and both stack scroll and clock have 0 height
@@ -338,6 +372,155 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase {
assertThat(mClockPosition.clockAlpha).isEqualTo(TRANSPARENT); 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 mCutoutTopInset
assertThat(mClockPosition.clockY).isEqualTo(mCutoutTopInset);
}
@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_large_clock))
.thenReturn(offset);
mClockPositionAlgorithm.loadDimens(mResources);
}
private void givenAOD() { private void givenAOD() {
mPanelExpansion = 1.f; mPanelExpansion = 1.f;
mDark = 1.f; mDark = 1.f;
@@ -348,13 +531,33 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase {
mDark = 0.f; 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() { private void positionClock() {
mClockPositionAlgorithm.setup(EMPTY_MARGIN, SCREEN_HEIGHT, mNotificationStackHeight, mClockPositionAlgorithm.setup(
mPanelExpansion, SCREEN_HEIGHT, mKeyguardStatusHeight, mKeyguardStatusBarHeaderHeight,
0 /* userSwitchHeight */, 0 /* userSwitchPreferredY */, SCREEN_HEIGHT,
mHasCustomClock, mHasVisibleNotifs, mDark, ZERO_DRAG, false /* bypassEnabled */, mNotificationStackHeight,
0 /* unlockedStackScrollerPadding */, mQsExpansion, mPanelExpansion,
mCutoutTopInset, mIsSplitShade); SCREEN_HEIGHT,
mKeyguardStatusHeight,
0 /* userSwitchHeight */,
0 /* userSwitchPreferredY */,
mHasCustomClock,
mHasVisibleNotifs,
mDark,
ZERO_DRAG,
false /* bypassEnabled */,
0 /* unlockedStackScrollerPadding */,
mQsExpansion,
mCutoutTopInset,
mIsSplitShade,
mUdfpsTop,
mClockBottom,
mClockTopAligned);
mClockPositionAlgorithm.run(mClockPosition); mClockPositionAlgorithm.run(mClockPosition);
} }
} }