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

Commit a2977268 authored by Vishnu Nair's avatar Vishnu Nair
Browse files

Replace setFinalCrop with temporary layer

- Creates a temporary animation bounds layer when setting up the
  animation in AppWindowToken
- The leash is parented to this new layer if the appStackClipMode
   is set to STACK_CLIP_AFTER_ANIM

Test: Manual test - open activity in split screen
Test: atest FrameworksServicesTests:AppWindowTokenAnimationTests
Change-Id: I6a9cf99832ee868a6e65da0150291d521f5eca35
parent 98d76705
Loading
Loading
Loading
Loading
+51 −1
Original line number Original line Diff line number Diff line
@@ -78,6 +78,7 @@ import static com.android.server.wm.AppWindowTokenProto.STARTING_MOVED;
import static com.android.server.wm.AppWindowTokenProto.STARTING_WINDOW;
import static com.android.server.wm.AppWindowTokenProto.STARTING_WINDOW;
import static com.android.server.wm.AppWindowTokenProto.THUMBNAIL;
import static com.android.server.wm.AppWindowTokenProto.THUMBNAIL;
import static com.android.server.wm.AppWindowTokenProto.WINDOW_TOKEN;
import static com.android.server.wm.AppWindowTokenProto.WINDOW_TOKEN;
import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM;


import android.annotation.CallSuper;
import android.annotation.CallSuper;
import android.app.Activity;
import android.app.Activity;
@@ -264,6 +265,12 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
     */
     */
    private boolean mWillCloseOrEnterPip;
    private boolean mWillCloseOrEnterPip;


    /** Layer used to constrain the animation to a token's stack bounds. */
    SurfaceControl mAnimationBoundsLayer;

    /** Whether this token needs to create mAnimationBoundsLayer for cropping animations. */
    boolean mNeedsAnimationBoundsLayer;

    AppWindowToken(WindowManagerService service, IApplicationToken token, boolean voiceInteraction,
    AppWindowToken(WindowManagerService service, IApplicationToken token, boolean voiceInteraction,
            DisplayContent dc, long inputDispatchingTimeoutNanos, boolean fullscreen,
            DisplayContent dc, long inputDispatchingTimeoutNanos, boolean fullscreen,
            boolean showForAllUsers, int targetSdk, int orientation, int rotationAnimationHint,
            boolean showForAllUsers, int targetSdk, int orientation, int rotationAnimationHint,
@@ -1720,6 +1727,20 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
        return !isSplitScreenPrimary || allowSplitScreenPrimaryAnimation;
        return !isSplitScreenPrimary || allowSplitScreenPrimaryAnimation;
    }
    }


    /**
     * Creates a layer to apply crop to an animation.
     */
    private SurfaceControl createAnimationBoundsLayer(Transaction t) {
        if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.i(TAG, "Creating animation bounds layer");
        final SurfaceControl.Builder builder = makeAnimationLeash()
                .setParent(getAnimationLeashParent())
                .setName(getSurfaceControl() + " - animation-bounds")
                .setSize(getSurfaceWidth(), getSurfaceHeight());
        final SurfaceControl boundsLayer = builder.build();
        t.show(boundsLayer);
        return boundsLayer;
    }

    boolean applyAnimationLocked(WindowManager.LayoutParams lp, int transit, boolean enter,
    boolean applyAnimationLocked(WindowManager.LayoutParams lp, int transit, boolean enter,
            boolean isVoiceInteraction) {
            boolean isVoiceInteraction) {


@@ -1753,12 +1774,15 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
                adapter = mService.mAppTransition.getRemoteAnimationController()
                adapter = mService.mAppTransition.getRemoteAnimationController()
                        .createAnimationAdapter(this, mTmpPoint, mTmpRect);
                        .createAnimationAdapter(this, mTmpPoint, mTmpRect);
            } else {
            } else {
                final int appStackClipMode = mService.mAppTransition.getAppStackClipMode();
                mNeedsAnimationBoundsLayer = (appStackClipMode == STACK_CLIP_AFTER_ANIM);

                final Animation a = loadAnimation(lp, transit, enter, isVoiceInteraction);
                final Animation a = loadAnimation(lp, transit, enter, isVoiceInteraction);
                if (a != null) {
                if (a != null) {
                    adapter = new LocalAnimationAdapter(
                    adapter = new LocalAnimationAdapter(
                            new WindowAnimationSpec(a, mTmpPoint, mTmpRect,
                            new WindowAnimationSpec(a, mTmpPoint, mTmpRect,
                                    mService.mAppTransition.canSkipFirstFrame(),
                                    mService.mAppTransition.canSkipFirstFrame(),
                                    mService.mAppTransition.getAppStackClipMode(),
                                    appStackClipMode,
                                    true /* isAppAnimation */),
                                    true /* isAppAnimation */),
                            mService.mSurfaceAnimationRunner);
                            mService.mSurfaceAnimationRunner);
                    if (a.getZAdjustment() == Animation.ZORDER_TOP) {
                    if (a.getZAdjustment() == Animation.ZORDER_TOP) {
@@ -1858,6 +1882,11 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
    @Override
    @Override
    public void onAnimationLeashDestroyed(Transaction t) {
    public void onAnimationLeashDestroyed(Transaction t) {
        super.onAnimationLeashDestroyed(t);
        super.onAnimationLeashDestroyed(t);
        if (mAnimationBoundsLayer != null) {
            t.destroy(mAnimationBoundsLayer);
            mAnimationBoundsLayer = null;
        }

        if (mAnimatingAppWindowTokenRegistry != null) {
        if (mAnimatingAppWindowTokenRegistry != null) {
            mAnimatingAppWindowTokenRegistry.notifyFinished(this);
            mAnimatingAppWindowTokenRegistry.notifyFinished(this);
        }
        }
@@ -1908,6 +1937,26 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
        if (mAnimatingAppWindowTokenRegistry != null) {
        if (mAnimatingAppWindowTokenRegistry != null) {
            mAnimatingAppWindowTokenRegistry.notifyStarting(this);
            mAnimatingAppWindowTokenRegistry.notifyStarting(this);
        }
        }

        // If the animation needs to be cropped then an animation bounds layer is created as a child
        // of the pinned stack or animation layer. The leash is then reparented to this new layer.
        if (mNeedsAnimationBoundsLayer) {
            final TaskStack stack = getStack();
            if (stack == null) {
                return;
            }
            mAnimationBoundsLayer = createAnimationBoundsLayer(t);

            // Set clip rect to stack bounds.
            mTmpRect.setEmpty();
            stack.getBounds(mTmpRect);

            // Crop to stack bounds.
            t.setWindowCrop(mAnimationBoundsLayer, mTmpRect);

            // Reparent leash to animation bounds layer.
            t.reparent(leash, mAnimationBoundsLayer.getHandle());
        }
    }
    }


    /**
    /**
@@ -1927,6 +1976,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
        mTransit = TRANSIT_UNSET;
        mTransit = TRANSIT_UNSET;
        mTransitFlags = 0;
        mTransitFlags = 0;
        mNeedsZBoost = false;
        mNeedsZBoost = false;
        mNeedsAnimationBoundsLayer = false;


        setAppLayoutChanges(FINISH_LAYOUT_REDO_ANIM | FINISH_LAYOUT_REDO_WALLPAPER,
        setAppLayoutChanges(FINISH_LAYOUT_REDO_ANIM | FINISH_LAYOUT_REDO_WALLPAPER,
                "AppWindowToken");
                "AppWindowToken");
+1 −7
Original line number Original line Diff line number Diff line
@@ -99,13 +99,7 @@ public class WindowAnimationSpec implements AnimationSpec {
        tmp.transformation.getMatrix().postTranslate(mPosition.x, mPosition.y);
        tmp.transformation.getMatrix().postTranslate(mPosition.x, mPosition.y);
        t.setMatrix(leash, tmp.transformation.getMatrix(), tmp.floats);
        t.setMatrix(leash, tmp.transformation.getMatrix(), tmp.floats);
        t.setAlpha(leash, tmp.transformation.getAlpha());
        t.setAlpha(leash, tmp.transformation.getAlpha());
        if (mStackClipMode == STACK_CLIP_NONE) {
        if (mStackClipMode == STACK_CLIP_NONE || mStackClipMode == STACK_CLIP_AFTER_ANIM) {
            t.setWindowCrop(leash, tmp.transformation.getClipRect());
        } else if (mStackClipMode == STACK_CLIP_AFTER_ANIM) {
            mTmpRect.set(mStackBounds);
            // Offset stack bounds to stack position so the final crop is in screen space.
            mTmpRect.offsetTo(mPosition.x, mPosition.y);
            t.setFinalCrop(leash, mTmpRect);
            t.setWindowCrop(leash, tmp.transformation.getClipRect());
            t.setWindowCrop(leash, tmp.transformation.getClipRect());
        } else {
        } else {
            mTmpRect.set(mStackBounds);
            mTmpRect.set(mStackBounds);
+118 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License
 */

package com.android.server.wm;

import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.view.SurfaceControl.Transaction;

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

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;

import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.view.SurfaceControl;

import com.android.server.wm.WindowTestUtils.TestAppWindowToken;

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

/**
 * Animation related tests for the {@link AppWindowToken} class.
 *
 * Build/Install/Run:
 * atest FrameworksServicesTests:com.android.server.wm.AppWindowTokenAnimationTests
 */
@SmallTest
@RunWith(AndroidJUnit4.class)
public class AppWindowTokenAnimationTests extends WindowTestsBase {

    private TestAppWindowToken mToken;

    @Mock
    private Transaction mTransaction;
    @Mock
    private AnimationAdapter mSpec;

    @Override
    public void setUp() throws Exception {
        super.setUp();
        MockitoAnnotations.initMocks(this);

        mToken = createTestAppWindowToken(mDisplayContent, WINDOWING_MODE_FULLSCREEN,
                ACTIVITY_TYPE_STANDARD);
        mToken.setPendingTransaction(mTransaction);
    }

    @Test
    public void clipAfterAnim_boundsLayerIsCreated() throws Exception {
        mToken.mNeedsAnimationBoundsLayer = true;

        mToken.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
        verify(mTransaction).reparent(eq(mToken.getSurfaceControl()),
                eq(mToken.mSurfaceAnimator.mLeash.getHandle()));
        verify(mTransaction).reparent(eq(mToken.mSurfaceAnimator.mLeash),
                eq(mToken.mAnimationBoundsLayer.getHandle()));
    }

    @Test
    public void clipAfterAnim_boundsLayerIsDestroyed() throws Exception {
        mToken.mNeedsAnimationBoundsLayer = true;
        mToken.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
        final SurfaceControl leash = mToken.mSurfaceAnimator.mLeash;
        final SurfaceControl animationBoundsLayer = mToken.mAnimationBoundsLayer;
        final ArgumentCaptor<SurfaceAnimator.OnAnimationFinishedCallback> callbackCaptor =
                ArgumentCaptor.forClass(
                        SurfaceAnimator.OnAnimationFinishedCallback.class);
        verify(mSpec).startAnimation(any(), any(), callbackCaptor.capture());

        callbackCaptor.getValue().onAnimationFinished(mSpec);
        verify(mTransaction).destroy(eq(leash));
        verify(mTransaction).destroy(eq(animationBoundsLayer));
        assertThat(mToken.mNeedsAnimationBoundsLayer).isFalse();
    }

    @Test
    public void clipAfterAnimCancelled_boundsLayerIsDestroyed() throws Exception {
        mToken.mNeedsAnimationBoundsLayer = true;
        mToken.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
        final SurfaceControl leash = mToken.mSurfaceAnimator.mLeash;
        final SurfaceControl animationBoundsLayer = mToken.mAnimationBoundsLayer;

        mToken.mSurfaceAnimator.cancelAnimation();
        verify(mTransaction).destroy(eq(leash));
        verify(mTransaction).destroy(eq(animationBoundsLayer));
        assertThat(mToken.mNeedsAnimationBoundsLayer).isFalse();
    }

    @Test
    public void clipNoneAnim_boundsLayerIsNotCreated() throws Exception {
        mToken.mNeedsAnimationBoundsLayer = false;

        mToken.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
        verify(mTransaction).reparent(eq(mToken.getSurfaceControl()),
                eq(mToken.mSurfaceAnimator.mLeash.getHandle()));
        assertThat(mToken.mAnimationBoundsLayer).isNull();
    }
}
+0 −5
Original line number Original line Diff line number Diff line
@@ -70,8 +70,6 @@ public class WindowAnimationSpecTest {
                true /* isAppAnimation */);
                true /* isAppAnimation */);
        windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0);
        windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0);
        verify(mTransaction).setWindowCrop(eq(mSurfaceControl), argThat(Rect::isEmpty));
        verify(mTransaction).setWindowCrop(eq(mSurfaceControl), argThat(Rect::isEmpty));
        verify(mTransaction).setFinalCrop(eq(mSurfaceControl),
                argThat(rect -> rect.equals(mStackBounds)));
    }
    }


    @Test
    @Test
@@ -83,9 +81,6 @@ public class WindowAnimationSpecTest {
                true /* isAppAnimation */);
                true /* isAppAnimation */);
        windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0);
        windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0);
        verify(mTransaction).setWindowCrop(eq(mSurfaceControl), argThat(Rect::isEmpty));
        verify(mTransaction).setWindowCrop(eq(mSurfaceControl), argThat(Rect::isEmpty));
        verify(mTransaction).setFinalCrop(eq(mSurfaceControl),
                argThat(rect -> rect.left == 20 && rect.top == 40 && rect.right == 30
                        && rect.bottom == 50));
    }
    }


    @Test
    @Test
+14 −0
Original line number Original line Diff line number Diff line
@@ -24,6 +24,8 @@ import android.os.Binder;
import android.os.IBinder;
import android.os.IBinder;
import android.view.IApplicationToken;
import android.view.IApplicationToken;
import android.view.IWindow;
import android.view.IWindow;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
import android.view.WindowManager;
import android.view.WindowManager;


import static android.app.AppOpsManager.OP_NONE;
import static android.app.AppOpsManager.OP_NONE;
@@ -113,6 +115,7 @@ public class WindowTestUtils {
    /** Used so we can gain access to some protected members of the {@link AppWindowToken} class. */
    /** Used so we can gain access to some protected members of the {@link AppWindowToken} class. */
    public static class TestAppWindowToken extends AppWindowToken {
    public static class TestAppWindowToken extends AppWindowToken {
        boolean mOnTop = false;
        boolean mOnTop = false;
        private Transaction mPendingTransactionOverride;


        private TestAppWindowToken(DisplayContent dc) {
        private TestAppWindowToken(DisplayContent dc) {
            super(dc.mService, new IApplicationToken.Stub() {
            super(dc.mService, new IApplicationToken.Stub() {
@@ -158,6 +161,17 @@ public class WindowTestUtils {
        boolean isOnTop() {
        boolean isOnTop() {
            return mOnTop;
            return mOnTop;
        }
        }

        void setPendingTransaction(Transaction transaction) {
            mPendingTransactionOverride = transaction;
        }

        @Override
        public Transaction getPendingTransaction() {
            return mPendingTransactionOverride == null
                    ? super.getPendingTransaction()
                    : mPendingTransactionOverride;
        }
    }
    }


    static TestWindowToken createTestWindowToken(int type, DisplayContent dc) {
    static TestWindowToken createTestWindowToken(int type, DisplayContent dc) {
Loading