Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8692fcd0 authored by Will's avatar Will
Browse files

Fix a bug with jittering dream overlay.

The first jitter causes all the dream overlay views to jump noticeably.
The issues was that the first jitter happens after the views have
appeared on screen. The solution is to jitter immediately so that the
views are already in their initial jitter position before they become
visible.

Test: atest DreamOverlayContainerViewControllerTest
Bug: 220182090
Change-Id: Ia62a5cf8d314a8617eaa522b51be99f61aef8b99
parent d0cb324a
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -695,4 +695,7 @@

    <!-- How often in milliseconds to jitter the dream overlay in order to avoid burn-in. -->
    <integer name="config_dreamOverlayBurnInProtectionUpdateIntervalMillis">500</integer>

    <!-- How long in milliseconds before full burn-in protection is achieved. -->
    <integer name="config_dreamOverlayMillisUntilFullJitter">240000</integer>
</resources>
+27 −6
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.systemui.dreams;
import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;

import android.os.Handler;
import android.util.MathUtils;
import android.view.View;
import android.view.ViewGroup;

@@ -56,9 +57,15 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve
    // The interval in milliseconds between burn-in protection updates.
    private final long mBurnInProtectionUpdateInterval;

    // Amount of time in milliseconds to linear interpolate toward the final jitter offset. Once
    // this time is achieved, the normal jitter algorithm applies in full.
    private final long mMillisUntilFullJitter;

    // Main thread handler used to schedule periodic tasks (e.g. burn-in protection updates).
    private final Handler mHandler;

    private long mJitterStartTimeMillis;

    @Inject
    public DreamOverlayContainerViewController(
            DreamOverlayContainerView containerView,
@@ -68,7 +75,8 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve
            @Main Handler handler,
            @Named(DreamOverlayModule.MAX_BURN_IN_OFFSET) int maxBurnInOffset,
            @Named(DreamOverlayModule.BURN_IN_PROTECTION_UPDATE_INTERVAL) long
                    burnInProtectionUpdateInterval) {
                    burnInProtectionUpdateInterval,
            @Named(DreamOverlayModule.MILLIS_UNTIL_FULL_JITTER) long millisUntilFullJitter) {
        super(containerView);
        mDreamOverlayContentView = contentView;
        mStatusBarViewController = statusBarViewController;
@@ -86,6 +94,7 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve
        mHandler = handler;
        mMaxBurnInOffset = maxBurnInOffset;
        mBurnInProtectionUpdateInterval = burnInProtectionUpdateInterval;
        mMillisUntilFullJitter = millisUntilFullJitter;
    }

    @Override
@@ -96,6 +105,7 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve

    @Override
    protected void onViewAttached() {
        mJitterStartTimeMillis = System.currentTimeMillis();
        mHandler.postDelayed(this::updateBurnInOffsets, mBurnInProtectionUpdateInterval);
    }

@@ -114,13 +124,24 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve
    }

    private void updateBurnInOffsets() {
        int burnInOffset = mMaxBurnInOffset;

        // Make sure the offset starts at zero, to avoid a big jump in the overlay when it first
        // appears.
        long millisSinceStart = System.currentTimeMillis() - mJitterStartTimeMillis;
        if (millisSinceStart < mMillisUntilFullJitter) {
            float lerpAmount = (float) millisSinceStart / (float) mMillisUntilFullJitter;
            burnInOffset = Math.round(MathUtils.lerp(0f, burnInOffset, lerpAmount));
        }

        // These translation values change slowly, and the set translation methods are idempotent,
        // so no translation occurs when the values don't change.
        mView.setTranslationX(getBurnInOffset(mMaxBurnInOffset * 2, true)
                - mMaxBurnInOffset);

        mView.setTranslationY(getBurnInOffset(mMaxBurnInOffset * 2, false)
                - mMaxBurnInOffset);
        int burnInOffsetX = getBurnInOffset(burnInOffset * 2, true)
                - burnInOffset;
        int burnInOffsetY = getBurnInOffset(burnInOffset * 2, false)
                - burnInOffset;
        mView.setTranslationX(burnInOffsetX);
        mView.setTranslationY(burnInOffsetY);

        mHandler.postDelayed(this::updateBurnInOffsets, mBurnInProtectionUpdateInterval);
    }
+8 −0
Original line number Diff line number Diff line
@@ -46,6 +46,7 @@ public abstract class DreamOverlayModule {
    public static final String MAX_BURN_IN_OFFSET = "max_burn_in_offset";
    public static final String BURN_IN_PROTECTION_UPDATE_INTERVAL =
            "burn_in_protection_update_interval";
    public static final String MILLIS_UNTIL_FULL_JITTER = "millis_until_full_jitter";

    /** */
    @Provides
@@ -106,6 +107,13 @@ public abstract class DreamOverlayModule {
                R.integer.config_dreamOverlayBurnInProtectionUpdateIntervalMillis);
    }

    /** */
    @Provides
    @Named(MILLIS_UNTIL_FULL_JITTER)
    static long providesMillisUntilFullJitter(@Main Resources resources) {
        return resources.getInteger(R.integer.config_dreamOverlayMillisUntilFullJitter);
    }

    @Provides
    @DreamOverlayComponent.DreamOverlayScope
    static LifecycleOwner providesLifecycleOwner(Lazy<LifecycleRegistry> lifecycleRegistryLazy) {
+6 −5
Original line number Diff line number Diff line
@@ -18,7 +18,6 @@ package com.android.systemui.dreams;

import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -49,6 +48,7 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase {
    private static final int DREAM_OVERLAY_NOTIFICATIONS_DRAG_AREA_HEIGHT = 100;
    private static final int MAX_BURN_IN_OFFSET = 20;
    private static final long BURN_IN_PROTECTION_UPDATE_INTERVAL = 10;
    private static final long MILLIS_UNTIL_FULL_JITTER = 240 * 1000;

    @Mock
    Resources mResources;
@@ -100,7 +100,8 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase {
                mDreamOverlayStatusBarViewController,
                mHandler,
                MAX_BURN_IN_OFFSET,
                BURN_IN_PROTECTION_UPDATE_INTERVAL);
                BURN_IN_PROTECTION_UPDATE_INTERVAL,
                MILLIS_UNTIL_FULL_JITTER);
    }

    @Test
@@ -129,14 +130,14 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase {
    }

    @Test
    public void testBurnInProtectionUpdatesPeriodically() {
    public void testBurnInProtectionOffsetsStartAtZero() {
        ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
        mController.onViewAttached();
        verify(mHandler).postDelayed(
                runnableCaptor.capture(), eq(BURN_IN_PROTECTION_UPDATE_INTERVAL));
        runnableCaptor.getValue().run();
        verify(mDreamOverlayContainerView).setTranslationX(anyFloat());
        verify(mDreamOverlayContainerView).setTranslationY(anyFloat());
        verify(mDreamOverlayContainerView).setTranslationX(0.f);
        verify(mDreamOverlayContainerView).setTranslationY(0.f);
    }

    @Test