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

Commit d9eaf48f authored by Lucas Silva's avatar Lucas Silva
Browse files

Implement dream to bouncer animation.

We blur the dream and fade out complications as the bouncer is swiped
upwards. Currently using a linear curve, pending final specs from
designers.

Test: locally on device
Bug: 220307939
Change-Id: Id756a68094a5286426995a6c1d02ad435250f69e
parent 0cee085f
Loading
Loading
Loading
Loading
+65 −0
Original line number Diff line number Diff line
@@ -27,6 +27,9 @@ import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dreams.complication.ComplicationHostViewController;
import com.android.systemui.dreams.dagger.DreamOverlayComponent;
import com.android.systemui.dreams.dagger.DreamOverlayModule;
import com.android.systemui.statusbar.BlurUtils;
import com.android.systemui.statusbar.phone.KeyguardBouncer;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.util.ViewController;

import javax.inject.Inject;
@@ -38,6 +41,8 @@ import javax.inject.Named;
@DreamOverlayComponent.DreamOverlayScope
public class DreamOverlayContainerViewController extends ViewController<DreamOverlayContainerView> {
    private final DreamOverlayStatusBarViewController mStatusBarViewController;
    private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
    private final BlurUtils mBlurUtils;

    private final ComplicationHostViewController mComplicationHostViewController;

@@ -60,12 +65,57 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve

    private long mJitterStartTimeMillis;

    private boolean mBouncerAnimating;

    private final KeyguardBouncer.BouncerExpansionCallback mBouncerExpansionCallback =
            new KeyguardBouncer.BouncerExpansionCallback() {

                @Override
                public void onStartingToShow() {
                    mBouncerAnimating = true;
                }

                @Override
                public void onStartingToHide() {
                    mBouncerAnimating = true;
                }

                @Override
                public void onFullyHidden() {
                    mBouncerAnimating = false;
                }

                @Override
                public void onFullyShown() {
                    mBouncerAnimating = false;
                }

                @Override
                public void onExpansionChanged(float bouncerHideAmount) {
                    if (!mBouncerAnimating) return;
                    final int blurRadius =
                            (int) mBlurUtils.blurRadiusOfRatio(1 - bouncerHideAmount);
                    updateTransitionState(blurRadius, bouncerHideAmount);
                }

                @Override
                public void onVisibilityChanged(boolean isVisible) {
                    // The bouncer may be hidden abruptly without triggering onExpansionChanged.
                    // In this case, we should reset the transition state.
                    if (!isVisible) {
                        updateTransitionState(0, 1f);
                    }
                }
            };

    @Inject
    public DreamOverlayContainerViewController(
            DreamOverlayContainerView containerView,
            ComplicationHostViewController complicationHostViewController,
            @Named(DreamOverlayModule.DREAM_OVERLAY_CONTENT_VIEW) ViewGroup contentView,
            DreamOverlayStatusBarViewController statusBarViewController,
            StatusBarKeyguardViewManager statusBarKeyguardViewManager,
            BlurUtils blurUtils,
            @Main Handler handler,
            @Named(DreamOverlayModule.MAX_BURN_IN_OFFSET) int maxBurnInOffset,
            @Named(DreamOverlayModule.BURN_IN_PROTECTION_UPDATE_INTERVAL) long
@@ -74,6 +124,8 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve
        super(containerView);
        mDreamOverlayContentView = contentView;
        mStatusBarViewController = statusBarViewController;
        mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
        mBlurUtils = blurUtils;

        mComplicationHostViewController = complicationHostViewController;
        final View view = mComplicationHostViewController.getView();
@@ -98,11 +150,19 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve
    protected void onViewAttached() {
        mJitterStartTimeMillis = System.currentTimeMillis();
        mHandler.postDelayed(this::updateBurnInOffsets, mBurnInProtectionUpdateInterval);
        final KeyguardBouncer bouncer = mStatusBarKeyguardViewManager.getBouncer();
        if (bouncer != null) {
            bouncer.addBouncerExpansionCallback(mBouncerExpansionCallback);
        }
    }

    @Override
    protected void onViewDetached() {
        mHandler.removeCallbacks(this::updateBurnInOffsets);
        final KeyguardBouncer bouncer = mStatusBarKeyguardViewManager.getBouncer();
        if (bouncer != null) {
            bouncer.removeBouncerExpansionCallback(mBouncerExpansionCallback);
        }
    }

    View getContainerView() {
@@ -131,4 +191,9 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve

        mHandler.postDelayed(this::updateBurnInOffsets, mBurnInProtectionUpdateInterval);
    }

    private void updateTransitionState(int blurRadiusPixels, float alpha) {
        mBlurUtils.applyBlur(mView.getViewRootImpl(), blurRadiusPixels, false);
        mView.setAlpha(alpha);
    }
}
+40 −4
Original line number Diff line number Diff line
@@ -606,11 +606,47 @@ public class KeyguardBouncer {
        mResetCallbacks.remove(callback);
    }

    /**
     * Adds a callback to listen to bouncer expansion updates.
     */
    public void addBouncerExpansionCallback(BouncerExpansionCallback callback) {
        if (!mExpansionCallbacks.contains(callback)) {
            mExpansionCallbacks.add(callback);
        }
    }

    /**
     * Removes a previously added callback. If the callback was never added, this methood
     * does nothing.
     */
    public void removeBouncerExpansionCallback(BouncerExpansionCallback callback) {
        mExpansionCallbacks.remove(callback);
    }

    public interface BouncerExpansionCallback {
        void onFullyShown();
        void onStartingToHide();
        void onStartingToShow();
        void onFullyHidden();
        /**
         * Invoked when the bouncer expansion reaches {@link KeyguardBouncer#EXPANSION_VISIBLE}.
         */
        default void onFullyShown() {
        }

        /**
         * Invoked when the bouncer is starting to transition to a hidden state.
         */
        default void onStartingToHide() {
        }

        /**
         * Invoked when the bouncer is starting to transition to a visible state.
         */
        default void onStartingToShow() {
        }

        /**
         * Invoked when the bouncer expansion reaches {@link KeyguardBouncer#EXPANSION_HIDDEN}.
         */
        default void onFullyHidden() {
        }

        /**
         * From 0f {@link KeyguardBouncer#EXPANSION_VISIBLE} when fully visible
+54 −0
Original line number Diff line number Diff line
@@ -17,7 +17,10 @@
package com.android.systemui.dreams;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@@ -25,12 +28,17 @@ import android.content.res.Resources;
import android.os.Handler;
import android.testing.AndroidTestingRunner;
import android.view.ViewGroup;
import android.view.ViewRootImpl;
import android.view.ViewTreeObserver;

import androidx.test.filters.SmallTest;

import com.android.systemui.SysuiTestCase;
import com.android.systemui.dreams.complication.ComplicationHostViewController;
import com.android.systemui.statusbar.BlurUtils;
import com.android.systemui.statusbar.phone.KeyguardBouncer;
import com.android.systemui.statusbar.phone.KeyguardBouncer.BouncerExpansionCallback;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;

import org.junit.Before;
import org.junit.Test;
@@ -67,6 +75,18 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase {
    @Mock
    Handler mHandler;

    @Mock
    BlurUtils mBlurUtils;

    @Mock
    StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;

    @Mock
    KeyguardBouncer mBouncer;

    @Mock
    ViewRootImpl mViewRoot;

    DreamOverlayContainerViewController mController;

    @Before
@@ -75,12 +95,16 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase {

        when(mDreamOverlayContainerView.getResources()).thenReturn(mResources);
        when(mDreamOverlayContainerView.getViewTreeObserver()).thenReturn(mViewTreeObserver);
        when(mStatusBarKeyguardViewManager.getBouncer()).thenReturn(mBouncer);
        when(mDreamOverlayContainerView.getViewRootImpl()).thenReturn(mViewRoot);

        mController = new DreamOverlayContainerViewController(
                mDreamOverlayContainerView,
                mComplicationHostViewController,
                mDreamOverlayContentView,
                mDreamOverlayStatusBarViewController,
                mStatusBarKeyguardViewManager,
                mBlurUtils,
                mHandler,
                MAX_BURN_IN_OFFSET,
                BURN_IN_PROTECTION_UPDATE_INTERVAL,
@@ -125,4 +149,34 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase {
        runnableCaptor.getValue().run();
        verify(mHandler).postDelayed(runnableCaptor.getValue(), BURN_IN_PROTECTION_UPDATE_INTERVAL);
    }

    @Test
    public void testBouncerAnimation_doesNotApply() {
        final ArgumentCaptor<BouncerExpansionCallback> bouncerExpansionCaptor =
                ArgumentCaptor.forClass(BouncerExpansionCallback.class);
        mController.onViewAttached();
        verify(mBouncer).addBouncerExpansionCallback(bouncerExpansionCaptor.capture());

        bouncerExpansionCaptor.getValue().onExpansionChanged(0.5f);
        verify(mBlurUtils, never()).applyBlur(eq(mViewRoot), anyInt(), eq(false));
        verify(mDreamOverlayContainerView, never()).setAlpha(anyFloat());
    }

    @Test
    public void testBouncerAnimation_updateBlurAndAlpha() {
        final ArgumentCaptor<BouncerExpansionCallback> bouncerExpansionCaptor =
                ArgumentCaptor.forClass(BouncerExpansionCallback.class);
        mController.onViewAttached();
        verify(mBouncer).addBouncerExpansionCallback(bouncerExpansionCaptor.capture());

        final float blurRadius = 1337f;
        when(mBlurUtils.blurRadiusOfRatio(anyFloat())).thenReturn(blurRadius);

        bouncerExpansionCaptor.getValue().onStartingToShow();
        final float bouncerHideAmount = 0.1f;
        bouncerExpansionCaptor.getValue().onExpansionChanged(bouncerHideAmount);
        verify(mBlurUtils).blurRadiusOfRatio(1 - bouncerHideAmount);
        verify(mBlurUtils).applyBlur(mViewRoot, (int) blurRadius, false);
        verify(mDreamOverlayContainerView).setAlpha(bouncerHideAmount);
    }
}
+45 −5
Original line number Diff line number Diff line
@@ -16,6 +16,9 @@

package com.android.systemui.statusbar.phone;

import static com.android.systemui.statusbar.phone.KeyguardBouncer.EXPANSION_HIDDEN;
import static com.android.systemui.statusbar.phone.KeyguardBouncer.EXPANSION_VISIBLE;

import static com.google.common.truth.Truth.assertThat;

import static org.mockito.ArgumentMatchers.any;
@@ -24,9 +27,11 @@ import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
@@ -53,6 +58,7 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.keyguard.DismissCallbackRegistry;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import com.android.systemui.statusbar.phone.KeyguardBouncer.BouncerExpansionCallback;
import com.android.systemui.statusbar.policy.KeyguardStateController;

import org.junit.Assert;
@@ -62,6 +68,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;

@@ -79,7 +86,7 @@ public class KeyguardBouncerTest extends SysuiTestCase {
    @Mock
    private KeyguardHostViewController mKeyguardHostViewController;
    @Mock
    private KeyguardBouncer.BouncerExpansionCallback mExpansionCallback;
    private BouncerExpansionCallback mExpansionCallback;
    @Mock
    private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
    @Mock
@@ -197,11 +204,11 @@ public class KeyguardBouncerTest extends SysuiTestCase {
        mBouncer.ensureView();
        mBouncer.setExpansion(0.5f);

        mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
        mBouncer.setExpansion(EXPANSION_HIDDEN);
        verify(mFalsingCollector).onBouncerHidden();
        verify(mExpansionCallback).onFullyHidden();

        mBouncer.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE);
        mBouncer.setExpansion(EXPANSION_VISIBLE);
        verify(mFalsingCollector).onBouncerShown();
        verify(mExpansionCallback).onFullyShown();

@@ -410,11 +417,11 @@ public class KeyguardBouncerTest extends SysuiTestCase {
    @Test
    public void testInTransit_whenTranslation() {
        mBouncer.show(true);
        mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
        mBouncer.setExpansion(EXPANSION_HIDDEN);
        assertThat(mBouncer.inTransit()).isFalse();
        mBouncer.setExpansion(0.5f);
        assertThat(mBouncer.inTransit()).isTrue();
        mBouncer.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE);
        mBouncer.setExpansion(EXPANSION_VISIBLE);
        assertThat(mBouncer.inTransit()).isFalse();
    }

@@ -435,4 +442,37 @@ public class KeyguardBouncerTest extends SysuiTestCase {

        verify(mKeyguardHostViewController).updateKeyguardPosition(1.0f);
    }

    @Test
    public void testExpansion_notifiesCallback() {
        mBouncer.ensureView();
        mBouncer.setExpansion(0.5f);

        final BouncerExpansionCallback callback = mock(BouncerExpansionCallback.class);
        mBouncer.addBouncerExpansionCallback(callback);

        mBouncer.setExpansion(EXPANSION_HIDDEN);
        verify(callback).onFullyHidden();
        verify(callback).onExpansionChanged(EXPANSION_HIDDEN);

        Mockito.clearInvocations(callback);
        mBouncer.setExpansion(EXPANSION_VISIBLE);
        verify(callback).onFullyShown();
        verify(callback).onExpansionChanged(EXPANSION_VISIBLE);

        Mockito.clearInvocations(callback);
        float bouncerHideAmount = 0.9f;
        // Ensure the callback only triggers once despite multiple calls to setExpansion
        // with the same value.
        mBouncer.setExpansion(bouncerHideAmount);
        mBouncer.setExpansion(bouncerHideAmount);
        verify(callback, times(1)).onStartingToHide();
        verify(callback, times(1)).onExpansionChanged(bouncerHideAmount);

        Mockito.clearInvocations(callback);
        mBouncer.removeBouncerExpansionCallback(callback);
        bouncerHideAmount = 0.5f;
        mBouncer.setExpansion(bouncerHideAmount);
        verify(callback, never()).onExpansionChanged(bouncerHideAmount);
    }
}