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

Commit 99e072b0 authored by Riddle Hsu's avatar Riddle Hsu Committed by Android (Google) Code Review
Browse files

Merge "Remove WindowContainerThumbnail" into main

parents 7c56658a 7ae77881
Loading
Loading
Loading
Loading
+0 −114
Original line number Diff line number Diff line
@@ -46,9 +46,6 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.app.WindowConfiguration.activityTypeToString;
import static android.app.admin.DevicePolicyResources.Drawables.Source.PROFILE_SWITCH_ANIMATION;
import static android.app.admin.DevicePolicyResources.Drawables.Style.OUTLINE;
import static android.app.admin.DevicePolicyResources.Drawables.WORK_PROFILE_ICON;
import static android.content.Context.CONTEXT_RESTRICTED;
import static android.content.Intent.ACTION_MAIN;
import static android.content.Intent.CATEGORY_HOME;
@@ -189,7 +186,6 @@ import static com.android.server.wm.ActivityRecordProto.STARTING_DISPLAYED;
import static com.android.server.wm.ActivityRecordProto.STARTING_MOVED;
import static com.android.server.wm.ActivityRecordProto.STARTING_WINDOW;
import static com.android.server.wm.ActivityRecordProto.STATE;
import static com.android.server.wm.ActivityRecordProto.THUMBNAIL;
import static com.android.server.wm.ActivityRecordProto.TRANSLUCENT;
import static com.android.server.wm.ActivityRecordProto.VISIBLE;
import static com.android.server.wm.ActivityRecordProto.VISIBLE_REQUESTED;
@@ -265,7 +261,6 @@ import android.app.PictureInPictureParams;
import android.app.ResultInfo;
import android.app.WaitResult;
import android.app.WindowConfiguration;
import android.app.admin.DevicePolicyManager;
import android.app.assist.ActivityId;
import android.app.compat.CompatChanges;
import android.app.servertransaction.ActivityConfigurationChangeItem;
@@ -300,7 +295,6 @@ import android.graphics.Insets;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.gui.DropInputMode;
import android.hardware.HardwareBuffer;
import android.net.Uri;
@@ -340,7 +334,6 @@ import android.view.WindowInsets.Type;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import android.view.WindowManager.TransitionOldType;
import android.view.animation.Animation;
import android.window.ActivityWindowInfo;
import android.window.ITaskFragmentOrganizer;
import android.window.RemoteTransition;
@@ -4699,8 +4692,6 @@ final class ActivityRecord extends WindowToken {
            return true;
        }

        // TODO: Transfer thumbnail

        return false;
    }

@@ -7595,9 +7586,6 @@ final class ActivityRecord extends WindowToken {
                mActivityRecordInputSink.applyChangesToSurfaceIfChanged(getPendingTransaction());
            }
        }
        if (mThumbnail != null) {
            mThumbnail.setShowing(getPendingTransaction(), show);
        }
        mLastSurfaceShowing = show;
        super.prepareSurfaces();
    }
@@ -7609,84 +7597,6 @@ final class ActivityRecord extends WindowToken {
        return mLastSurfaceShowing;
    }

    void attachThumbnailAnimation() {
        if (!isAnimating(PARENTS, ANIMATION_TYPE_APP_TRANSITION)) {
            return;
        }
        final HardwareBuffer thumbnailHeader =
                getDisplayContent().mAppTransition.getAppTransitionThumbnailHeader(task);
        if (thumbnailHeader == null) {
            ProtoLog.d(WM_DEBUG_APP_TRANSITIONS, "No thumbnail header bitmap for: %s", task);
            return;
        }
        clearThumbnail();
        final Transaction transaction = getAnimatingContainer().getPendingTransaction();
        mThumbnail = new WindowContainerThumbnail(transaction, getAnimatingContainer(),
                thumbnailHeader);
        mThumbnail.startAnimation(transaction, loadThumbnailAnimation(thumbnailHeader));
    }

    /**
     * Attaches a surface with a thumbnail for the
     * {@link android.app.ActivityOptions#ANIM_OPEN_CROSS_PROFILE_APPS} animation.
     */
    void attachCrossProfileAppsThumbnailAnimation() {
        if (!isAnimating(PARENTS, ANIMATION_TYPE_APP_TRANSITION)) {
            return;
        }
        clearThumbnail();

        final WindowState win = findMainWindow();
        if (win == null) {
            return;
        }
        final Rect frame = win.getRelativeFrame();
        final Context context = mAtmService.getUiContext();
        final Drawable thumbnailDrawable;
        if (task.mUserId == mWmService.mCurrentUserId) {
            thumbnailDrawable = context.getDrawable(R.drawable.ic_account_circle);
        } else {
            final DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
            thumbnailDrawable = dpm.getResources().getDrawable(
                    WORK_PROFILE_ICON, OUTLINE, PROFILE_SWITCH_ANIMATION,
                    () -> context.getDrawable(R.drawable.ic_corp_badge));
        }
        final HardwareBuffer thumbnail = getDisplayContent().mAppTransition
                .createCrossProfileAppsThumbnail(thumbnailDrawable, frame);
        if (thumbnail == null) {
            return;
        }
        final Transaction transaction = getPendingTransaction();
        mThumbnail = new WindowContainerThumbnail(transaction, getTask(), thumbnail);
        final Animation animation =
                getDisplayContent().mAppTransition.createCrossProfileAppsThumbnailAnimationLocked(
                        frame);
        mThumbnail.startAnimation(transaction, animation, new Point(frame.left, frame.top));
    }

    private Animation loadThumbnailAnimation(HardwareBuffer thumbnailHeader) {
        final DisplayInfo displayInfo = mDisplayContent.getDisplayInfo();

        // If this is a multi-window scenario, we use the windows frame as
        // destination of the thumbnail header animation. If this is a full screen
        // window scenario, we use the whole display as the target.
        WindowState win = findMainWindow();
        Rect insets;
        Rect appRect;
        if (win != null) {
            insets = win.getInsetsStateWithVisibilityOverride().calculateInsets(
                    win.getFrame(), Type.systemBars(), false /* ignoreVisibility */).toRect();
            appRect = new Rect(win.getFrame());
            appRect.inset(insets);
        } else {
            insets = null;
            appRect = new Rect(0, 0, displayInfo.appWidth, displayInfo.appHeight);
        }
        final Configuration displayConfig = mDisplayContent.getConfiguration();
        return getDisplayContent().mAppTransition.createThumbnailAspectScaleAnimationLocked(
                appRect, insets, thumbnailHeader, task, displayConfig.orientation);
    }

    @Override
    public void onAnimationLeashLost(Transaction t) {
        super.onAnimationLeashLost(t);
@@ -7713,7 +7623,6 @@ final class ActivityRecord extends WindowToken {
        setAppLayoutChanges(FINISH_LAYOUT_REDO_ANIM | FINISH_LAYOUT_REDO_WALLPAPER,
                "ActivityRecord");

        clearThumbnail();
        setClientVisible(isVisible() || mVisibleRequested);

        getDisplayContent().computeImeTargetIfNeeded(this);
@@ -7723,12 +7632,6 @@ final class ActivityRecord extends WindowToken {
                this, reportedVisible, okToDisplay(), okToAnimate(),
                isStartingWindowDisplayed());

        // clean up thumbnail window
        if (mThumbnail != null) {
            mThumbnail.destroy();
            mThumbnail = null;
        }

        // WindowState.onExitAnimationDone might modify the children list, so make a copy and then
        // traverse the copy.
        final ArrayList<WindowState> children = new ArrayList<>(mChildren);
@@ -7767,20 +7670,6 @@ final class ActivityRecord extends WindowToken {
        }
    }

    @Override
    void cancelAnimation() {
        super.cancelAnimation();
        clearThumbnail();
    }

    private void clearThumbnail() {
        if (mThumbnail == null) {
            return;
        }
        mThumbnail.destroy();
        mThumbnail = null;
    }

    public @TransitionOldType int getTransit() {
        return mTransit;
    }
@@ -9726,9 +9615,6 @@ final class ActivityRecord extends WindowToken {
        proto.write(IS_WAITING_FOR_TRANSITION_START, isWaitingForTransitionStart());
        proto.write(IS_ANIMATING, isAnimating(TRANSITION | PARENTS | CHILDREN,
                ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_WINDOW_ANIMATION));
        if (mThumbnail != null){
            mThumbnail.dumpDebug(proto, THUMBNAIL);
        }
        proto.write(FILLS_PARENT, fillsParent());
        proto.write(APP_STOPPED, mAppStopped);
        proto.write(TRANSLUCENT, !occludesParent());
+0 −1
Original line number Diff line number Diff line
@@ -302,7 +302,6 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
     * This gets used during some open/close transitions as well as during a change transition
     * where it represents the starting-state snapshot.
     */
    WindowContainerThumbnail mThumbnail;
    final Point mTmpPoint = new Point();
    protected final Rect mTmpRect = new Rect();
    final Rect mTmpPrevBounds = new Rect();
+0 −218
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 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.view.SurfaceControl.METADATA_OWNER_UID;
import static android.view.SurfaceControl.METADATA_WINDOW_TYPE;

import static com.android.internal.protolog.WmProtoLogGroups.WM_SHOW_TRANSACTIONS;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
import static com.android.server.wm.WindowContainerThumbnailProto.HEIGHT;
import static com.android.server.wm.WindowContainerThumbnailProto.SURFACE_ANIMATOR;
import static com.android.server.wm.WindowContainerThumbnailProto.WIDTH;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.MAX_ANIMATION_DURATION;

import android.graphics.ColorSpace;
import android.graphics.GraphicBuffer;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.hardware.HardwareBuffer;
import android.util.proto.ProtoOutputStream;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Builder;
import android.view.SurfaceControl.Transaction;
import android.view.animation.Animation;

import com.android.internal.protolog.ProtoLog;
import com.android.server.wm.SurfaceAnimator.Animatable;
import com.android.server.wm.SurfaceAnimator.AnimationType;

/**
 * Represents a surface that is displayed over a subclass of {@link WindowContainer}
 */
class WindowContainerThumbnail implements Animatable {

    private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowContainerThumbnail" : TAG_WM;

    private final WindowContainer mWindowContainer;
    private SurfaceControl mSurfaceControl;
    private final SurfaceAnimator mSurfaceAnimator;
    private final int mWidth;
    private final int mHeight;

    /**
     * @param t Transaction to create the thumbnail in.
     * @param container The sub-class of {@link WindowContainer} to associate this thumbnail with.
     * @param thumbnailHeader A thumbnail or placeholder for thumbnail to initialize with.
     */
    WindowContainerThumbnail(Transaction t, WindowContainer container,
            HardwareBuffer thumbnailHeader) {
        this(t, container, thumbnailHeader, null /* animator */);
    }

    WindowContainerThumbnail(Transaction t, WindowContainer container,
            HardwareBuffer thumbnailHeader, SurfaceAnimator animator) {
        mWindowContainer = container;
        if (animator != null) {
            mSurfaceAnimator = animator;
        } else {
            // We can't use a delegating constructor since we need to
            // reference this::onAnimationFinished
            mSurfaceAnimator =
                new SurfaceAnimator(this, this::onAnimationFinished /* animationFinishedCallback */,
                        container.mWmService);
        }
        mWidth = thumbnailHeader.getWidth();
        mHeight = thumbnailHeader.getHeight();

        // Create a new surface for the thumbnail
        // TODO: This should be attached as a child to the app token, once the thumbnail animations
        // use relative coordinates. Once we start animating task we can also consider attaching
        // this to the task.
        mSurfaceControl = mWindowContainer.makeChildSurface(mWindowContainer.getTopChild())
                .setName("thumbnail anim: " + mWindowContainer.toString())
                .setBLASTLayer()
                .setFormat(PixelFormat.TRANSLUCENT)
                .setMetadata(METADATA_WINDOW_TYPE, mWindowContainer.getWindowingMode())
                .setMetadata(METADATA_OWNER_UID, WindowManagerService.MY_UID)
                .setCallsite("WindowContainerThumbnail")
                .build();

        ProtoLog.i(WM_SHOW_TRANSACTIONS, "  THUMBNAIL %s: CREATE", mSurfaceControl);

        GraphicBuffer graphicBuffer = GraphicBuffer.createFromHardwareBuffer(thumbnailHeader);
        t.setBuffer(mSurfaceControl, graphicBuffer);
        t.setColorSpace(mSurfaceControl, ColorSpace.get(ColorSpace.Named.SRGB));
        t.show(mSurfaceControl);

        // We parent the thumbnail to the container, and just place it on top of anything else in
        // the container.
        t.setLayer(mSurfaceControl, Integer.MAX_VALUE);
    }

    void startAnimation(Transaction t, Animation anim) {
        startAnimation(t, anim, null /* position */);
    }

    void startAnimation(Transaction t, Animation anim, Point position) {
        anim.restrictDuration(MAX_ANIMATION_DURATION);
        anim.scaleCurrentDuration(mWindowContainer.mWmService.getTransitionAnimationScaleLocked());
        mSurfaceAnimator.startAnimation(t, new LocalAnimationAdapter(
                new WindowAnimationSpec(anim, position,
                        mWindowContainer.getDisplayContent().mAppTransition.canSkipFirstFrame(),
                        mWindowContainer.getDisplayContent().getWindowCornerRadius()),
                mWindowContainer.mWmService.mSurfaceAnimationRunner), false /* hidden */,
                ANIMATION_TYPE_APP_TRANSITION);
    }

    private void onAnimationFinished(@AnimationType int type, AnimationAdapter anim) {
    }

    void setShowing(Transaction pendingTransaction, boolean show) {
        // TODO: Not needed anymore once thumbnail is attached to the app.
        if (show) {
            pendingTransaction.show(mSurfaceControl);
        } else {
            pendingTransaction.hide(mSurfaceControl);
        }
    }

    void destroy() {
        mSurfaceAnimator.cancelAnimation();
        getPendingTransaction().remove(mSurfaceControl);
        mSurfaceControl = null;
    }

    /**
     * Write to a protocol buffer output stream. Protocol buffer message definition is at {@link
     * com.android.server.wm.WindowContainerThumbnailProto}.
     *
     * @param proto Stream to write the WindowContainerThumbnailProto object to.
     * @param fieldId Field Id of the WindowContainerThumbnailProto as defined in the parent
     *                message.
     * @hide
     */
    void dumpDebug(ProtoOutputStream proto, long fieldId) {
        final long token = proto.start(fieldId);
        proto.write(WIDTH, mWidth);
        proto.write(HEIGHT, mHeight);
        if (mSurfaceAnimator.isAnimating()) {
            mSurfaceAnimator.dumpDebug(proto, SURFACE_ANIMATOR);
        }
        proto.end(token);
    }

    @Override
    public Transaction getSyncTransaction() {
        return mWindowContainer.getSyncTransaction();
    }

    @Override
    public Transaction getPendingTransaction() {
        return mWindowContainer.getPendingTransaction();
    }

    @Override
    public void commitPendingTransaction() {
        mWindowContainer.commitPendingTransaction();
    }

    @Override
    public void onAnimationLeashCreated(Transaction t, SurfaceControl leash) {
        t.setLayer(leash, Integer.MAX_VALUE);
    }

    @Override
    public void onAnimationLeashLost(Transaction t) {

        // TODO: Once attached to app token, we don't need to hide it immediately if thumbnail
        // became visible.
        t.hide(mSurfaceControl);
    }

    @Override
    public Builder makeAnimationLeash() {
        return mWindowContainer.makeChildSurface(mWindowContainer.getTopChild());
    }

    @Override
    public SurfaceControl getSurfaceControl() {
        return mSurfaceControl;
    }

    @Override
    public SurfaceControl getAnimationLeashParent() {
        return mWindowContainer.getAnimationLeashParent();
    }

    @Override
    public SurfaceControl getParentSurfaceControl() {
        return mWindowContainer.getParentSurfaceControl();
    }

    @Override
    public int getSurfaceWidth() {
        return mWidth;
    }

    @Override
    public int getSurfaceHeight() {
        return mHeight;
    }
}
+0 −65
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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 com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;

import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.any;

import android.hardware.HardwareBuffer;
import android.platform.test.annotations.Presubmit;

import androidx.test.filters.SmallTest;

import com.android.server.testutils.StubTransaction;

import org.junit.Test;
import org.junit.runner.RunWith;

/**
 * Test class for {@link TaskSnapshotSurface}.
 *
 * Build/Install/Run:
 *  atest WmTests:WindowContainerThumbnailTest
 *
 */
@SmallTest
@Presubmit
@RunWith(WindowTestRunner.class)
public class WindowContainerThumbnailTest extends WindowTestsBase {
    private WindowContainerThumbnail buildThumbnail() {
        final HardwareBuffer buffer = HardwareBuffer.create(1, 1, HardwareBuffer.RGBA_8888,
                1, HardwareBuffer.USAGE_CPU_READ_RARELY);
        final ActivityRecord mockAr = mock(ActivityRecord.class);
        when(mockAr.getPendingTransaction()).thenReturn(new StubTransaction());
        when(mockAr.makeChildSurface(any())).thenReturn(new MockSurfaceControlBuilder());
        when(mockAr.makeSurface()).thenReturn(new MockSurfaceControlBuilder());
        return new WindowContainerThumbnail(new StubTransaction(), mockAr, buffer,
                mock(SurfaceAnimator.class));
    }

    @Test
    public void testDestroy_nullsSurface() {
        final WindowContainerThumbnail t = buildThumbnail();
        assertNotNull(t.getSurfaceControl());
        t.destroy();
        assertNull(t.getSurfaceControl());
    }
}