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

Commit 4369d0ba authored by Cairn Overturf's avatar Cairn Overturf
Browse files

Add box shadows to PiP 2

See go/box-shadows-integration

Dark Theme: snipit/BCddicDn33NPXyV
Light Theme: snipit/3oNGdtsmtaTK4Ud

Bug: b/367464660
Test: with the flag enabled, PiP shadows should match the spec above.
Flag: com.android.wm.shell.enable_pip_box_shadows
Change-Id: I41b1a2c2a2c1f4579e83b97120e25095bd2c796a
parent 8760a9c5
Loading
Loading
Loading
Loading
+46 −1
Original line number Diff line number Diff line
@@ -19,10 +19,15 @@ package com.android.wm.shell.pip2;
import android.content.Context;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.gui.BorderSettings;
import android.gui.BoxShadowSettings;
import android.view.Choreographer;
import android.view.SurfaceControl;

import com.android.wm.shell.Flags;
import com.android.wm.shell.R;
import com.android.wm.shell.common.BoxShadowHelper;
import com.android.wm.shell.common.pip.PipUtils;

/**
 * Abstracts the common operations on {@link SurfaceControl.Transaction} for PiP transition.
@@ -36,12 +41,42 @@ public class PipSurfaceTransactionHelper {
    private final int mShadowRadius;
    private final float mMirrorOpacity;

    private BoxShadowSettings mBoxShadowSettings;
    private BorderSettings mBorderSettings;

    public PipSurfaceTransactionHelper(Context context) {
        mCornerRadius = context.getResources().getDimensionPixelSize(R.dimen.pip_corner_radius);
        mShadowRadius = context.getResources().getDimensionPixelSize(R.dimen.pip_shadow_radius);
        mMirrorOpacity = context.getResources().getFloat(
                R.dimen.config_pipDraggingAcrossDisplaysOpacity);
        onThemeChanged(context);
    }

    /**
     * Called when theme changes.
     *
     * @param context the current context
     */
    public void onThemeChanged(Context context) {
        if (Flags.enablePipBoxShadows()) {
            if (PipUtils.isDarkSystemTheme(context)) {
                mBoxShadowSettings = BoxShadowHelper.getBoxShadowSettings(context,
                        new int[]{R.style.BoxShadowParamsPIPDark1,
                                R.style.BoxShadowParamsPIPDark2});
                mBorderSettings = BoxShadowHelper.getBorderSettings(context,
                        R.style.BorderSettingsPIPDark);
            } else {
                mBoxShadowSettings = BoxShadowHelper.getBoxShadowSettings(context,
                        new int[]{R.style.BoxShadowParamsPIPLight1,
                                R.style.BoxShadowParamsPIPLight2});

                mBorderSettings = BoxShadowHelper.getBorderSettings(context,
                        R.style.BorderSettingsPIPLight);
            }
        }
    }



    /**
     * Gets corner radius which is loaded from resources.
@@ -162,7 +197,17 @@ public class PipSurfaceTransactionHelper {
     */
    public PipSurfaceTransactionHelper shadow(SurfaceControl.Transaction tx, SurfaceControl leash,
            boolean applyShadowRadius) {
        if (Flags.enablePipBoxShadows()) {
            if (applyShadowRadius) {
                tx.setBoxShadowSettings(leash, mBoxShadowSettings);
                tx.setBorderSettings(leash, mBorderSettings);
            } else {
                tx.setBoxShadowSettings(leash, new BoxShadowSettings());
                tx.setBorderSettings(leash, new BorderSettings());
            }
        } else {
            tx.setShadowRadius(leash, applyShadowRadius ? mShadowRadius : 0);
        }
        return this;
    }

+14 −0
Original line number Diff line number Diff line
@@ -45,6 +45,7 @@ import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.ProtoLog;
import com.android.internal.util.Preconditions;
import com.android.wm.shell.Flags;
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayChangeController;
@@ -122,6 +123,8 @@ public class PipController implements ConfigurationChangeListener,
    // Wrapper for making Binder calls into PiP animation listener hosted in launcher's Recents.
    @Nullable private PipAnimationListener mPipRecentsAnimationListener;

    private final PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
            mSurfaceControlTransactionFactory;
    private final PipSurfaceTransactionHelper mPipSurfaceTransactionHelper;

    private boolean mWaitingToPlayDisplayChangeBoundsUpdate;
@@ -193,6 +196,8 @@ public class PipController implements ConfigurationChangeListener,
        mPipSurfaceTransactionHelper = pipSurfaceTransactionHelper;
        mMainExecutor = mainExecutor;
        mImpl = new PipImpl();
        mSurfaceControlTransactionFactory =
                new PipSurfaceTransactionHelper.VsyncSurfaceControlTransactionFactory();

        if (PipUtils.isPip2ExperimentEnabled()) {
            shellInit.addInitCallback(this::onInit, this);
@@ -370,6 +375,15 @@ public class PipController implements ConfigurationChangeListener,
    @Override
    public void onThemeChanged() {
        setDisplayLayout(new DisplayLayout(mContext, mContext.getDisplay()));
        if (Flags.enablePipBoxShadows()) {
            if (mPipTransitionState.isInPip()) {
                SurfaceControl pipLeash = mPipTransitionState.getPinnedTaskLeash();
                mPipSurfaceTransactionHelper.onThemeChanged(mContext);
                SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
                mPipSurfaceTransactionHelper.shadow(tx, pipLeash, true /* applyShadowRadius */);
                tx.apply();
            }
        }
    }

    //
+108 −1
Original line number Diff line number Diff line
@@ -16,25 +16,38 @@

package com.android.wm.shell.pip2;

import static org.junit.Assert.assertEquals;
import static org.mockito.AdditionalMatchers.aryEq;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.content.Context;
import android.content.res.Resources;
import android.gui.BorderSettings;
import android.gui.BoxShadowSettings;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.SurfaceControl;

import androidx.test.filters.SmallTest;

import com.android.modules.utils.testing.ExtendedMockitoRule;
import com.android.wm.shell.Flags;
import com.android.wm.shell.R;
import com.android.wm.shell.common.BoxShadowHelper;
import com.android.wm.shell.common.pip.PipUtils;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

@@ -50,13 +63,34 @@ public class PipSurfaceTransactionHelperTest {
    private static final int SHADOW_RADIUS = 20;
    private static final float MIRROR_OPACITY = 0.5f;

    private final BoxShadowSettings mLightBoxShadowSettings = new BoxShadowSettings();
    private final BorderSettings mLightBorderSettings = new BorderSettings();
    private final BoxShadowSettings mDarkBoxShadowSettings = new BoxShadowSettings();
    private final BorderSettings mDarkBorderSettings = new BorderSettings();

    private static final int[] LIGHT_SHADOW_STYLES = {
            R.style.BoxShadowParamsPIPLight1, R.style.BoxShadowParamsPIPLight2};
    private static final int[] DARK_SHADOW_STYLES = {
            R.style.BoxShadowParamsPIPDark1, R.style.BoxShadowParamsPIPDark2};
    private static final int LIGHT_BORDER_STYLE = R.style.BorderSettingsPIPLight;
    private static final int DARK_BORDER_STYLE = R.style.BorderSettingsPIPDark;

    @Mock private Context mMockContext;
    @Mock private Resources mMockResources;
    @Mock private SurfaceControl.Transaction mMockTransaction;

    private PipSurfaceTransactionHelper mPipSurfaceTransactionHelper;
    private SurfaceControl mTestLeash;


    @Rule
    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();

    @Rule
    public final ExtendedMockitoRule mExtendedMockitoRule = new ExtendedMockitoRule.Builder(this)
            .mockStatic(BoxShadowHelper.class)
            .mockStatic(PipUtils.class)
            .build();

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
@@ -78,6 +112,30 @@ public class PipSurfaceTransactionHelperTest {
                .setName("PipSurfaceTransactionHelperTest")
                .setCallsite("PipSurfaceTransactionHelperTest")
                .build();

        when(mMockTransaction.setCornerRadius(any(SurfaceControl.class), anyFloat()))
                .thenReturn(mMockTransaction);
        when(mMockTransaction.setShadowRadius(any(SurfaceControl.class), anyFloat()))
                .thenReturn(mMockTransaction);
        when(mMockTransaction.setBoxShadowSettings(any(SurfaceControl.class),
                any(BoxShadowSettings.class)))
                .thenReturn(mMockTransaction);
        when(mMockTransaction.setBorderSettings(any(SurfaceControl.class),
                any(BorderSettings.class)))
                .thenReturn(mMockTransaction);

        when(BoxShadowHelper.getBoxShadowSettings(
                eq(mMockContext), aryEq(LIGHT_SHADOW_STYLES))).thenReturn(
                mLightBoxShadowSettings);
        when(BoxShadowHelper.getBorderSettings(
                eq(mMockContext), eq(LIGHT_BORDER_STYLE))).thenReturn(mLightBorderSettings);

        when(BoxShadowHelper.getBoxShadowSettings(
                eq(mMockContext), aryEq(DARK_SHADOW_STYLES))).thenReturn(mDarkBoxShadowSettings);
        when(BoxShadowHelper.getBorderSettings(
                eq(mMockContext), eq(DARK_BORDER_STYLE))).thenReturn(mDarkBorderSettings);


    }

    @Test
@@ -108,6 +166,55 @@ public class PipSurfaceTransactionHelperTest {
        verify(mMockTransaction).setShadowRadius(eq(mTestLeash), eq((float) SHADOW_RADIUS));
    }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_PIP_BOX_SHADOWS)
    public void shadow_flagEnabled_applyFalse_setsEmptyBoxShadowAndBorder() {
        mPipSurfaceTransactionHelper.shadow(mMockTransaction, mTestLeash, false /* apply */);

        ArgumentCaptor<BoxShadowSettings> boxShadow = ArgumentCaptor.forClass(
                BoxShadowSettings.class);
        ArgumentCaptor<BorderSettings> border = ArgumentCaptor.forClass(BorderSettings.class);

        verify(mMockTransaction).setBoxShadowSettings(eq(mTestLeash), boxShadow.capture());
        verify(mMockTransaction).setBorderSettings(eq(mTestLeash), border.capture());
        verify(mMockTransaction, never()).setShadowRadius(any(), anyFloat());

        assertEquals(0, boxShadow.getValue().boxShadows.length);
        assertEquals(0, border.getValue().strokeWidth, 0.0);
        assertEquals(0, border.getValue().color);
    }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_PIP_BOX_SHADOWS)
    public void onThemeChanged_switchToDarkTheme_usesDarkSettingsOnShadow() {
        when(PipUtils.isDarkSystemTheme(mMockContext)).thenReturn(true);

        mPipSurfaceTransactionHelper.onThemeChanged(mMockContext);

        mPipSurfaceTransactionHelper.shadow(mMockTransaction, mTestLeash, true /* apply */);

        verify(mMockTransaction).setBoxShadowSettings(eq(mTestLeash),
                eq(mDarkBoxShadowSettings));
        verify(mMockTransaction).setBorderSettings(eq(mTestLeash), eq(mDarkBorderSettings));
        verify(mMockTransaction, never()).setShadowRadius(any(), anyFloat());
    }


    @Test
    @EnableFlags(Flags.FLAG_ENABLE_PIP_BOX_SHADOWS)
    public void onThemeChanged_switchToLightTheme_usesLightSettingsOnShadow() {
        when(PipUtils.isDarkSystemTheme(mMockContext)).thenReturn(false);

        mPipSurfaceTransactionHelper.onThemeChanged(mMockContext);

        mPipSurfaceTransactionHelper.shadow(mMockTransaction, mTestLeash, true /* apply */);

        verify(mMockTransaction).setBoxShadowSettings(eq(mTestLeash),
                eq(mLightBoxShadowSettings));
        verify(mMockTransaction).setBorderSettings(eq(mTestLeash), eq(mLightBorderSettings));
        verify(mMockTransaction, never()).setShadowRadius(any(), anyFloat());
    }

    @Test
    public void setMirrorTransformations_setsAlphaAndLayer() {
        mPipSurfaceTransactionHelper.setMirrorTransformations(mMockTransaction, mTestLeash);