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

Commit 75e94750 authored by jorgegil@google.com's avatar jorgegil@google.com
Browse files

Take PIP snapshot in the shell and use bounds state size for crossfading

Since the width/height of the SC should not be used anymore,
we'll use the pre-resize bounds to scale the surface before
animating the crossfade.

Bug: 186669773
Test: Enter PIP in seamless resize demo, resize with seamless
turned off uses a crossfade animation with the correct size

Change-Id: I1405be9208ac9e7cc34283e3d97dc431f0cb643c
parent 63e732e8
Loading
Loading
Loading
Loading
+0 −11
Original line number Original line Diff line number Diff line
@@ -77,17 +77,6 @@ interface IWindowOrganizerController {
    /** @return An interface enabling the management of display area organizers. */
    /** @return An interface enabling the management of display area organizers. */
    IDisplayAreaOrganizerController getDisplayAreaOrganizerController();
    IDisplayAreaOrganizerController getDisplayAreaOrganizerController();


    /**
     * Take a screenshot of the requested Window token and place the content of the screenshot into
     * outSurfaceControl. The SurfaceControl will be a child of the token's parent, so it will be
     * a sibling of the token's window
     * @param token The token for the WindowContainer that should get a screenshot taken.
     * @param outSurfaceControl The SurfaceControl where the screenshot will be attached.
     *
     * @return true if the screenshot was successful, false otherwise.
     */
    boolean takeScreenshot(in WindowContainerToken token, out SurfaceControl outSurfaceControl);

    /**
    /**
     * Registers a transition player with Core. There is only one of these at a time and calling
     * Registers a transition player with Core. There is only one of these at a time and calling
     * this will replace the existing one if set.
     * this will replace the existing one if set.
+0 −23
Original line number Original line Diff line number Diff line
@@ -25,7 +25,6 @@ import android.app.ActivityTaskManager;
import android.os.IBinder;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.RemoteException;
import android.util.Singleton;
import android.util.Singleton;
import android.view.SurfaceControl;


/**
/**
 * Base class for organizing specific types of windows like Tasks and DisplayAreas
 * Base class for organizing specific types of windows like Tasks and DisplayAreas
@@ -111,28 +110,6 @@ public class WindowOrganizer {
        }
        }
    }
    }


    /**
     * Take a screenshot for a specified Window
     * @param token The token for the WindowContainer that should get a screenshot taken.
     * @return A SurfaceControl where the screenshot will be attached, or null if failed.
     *
     * @hide
     */
    @Nullable
    @RequiresPermission(android.Manifest.permission.READ_FRAME_BUFFER)
    public SurfaceControl takeScreenshot(@NonNull WindowContainerToken token) {
        try {
            SurfaceControl surfaceControl = new SurfaceControl();
            if (getWindowOrganizerController().takeScreenshot(token, surfaceControl)) {
                return surfaceControl;
            } else {
                return null;
            }
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
    /**
     * Register an ITransitionPlayer to handle transition animations.
     * Register an ITransitionPlayer to handle transition animations.
     * @hide
     * @hide
+68 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2021 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.wm.shell.common;

import android.graphics.GraphicBuffer;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.view.SurfaceControl;

/**
 * Helpers for working with screenshots.
 */
public class ScreenshotUtils {

    /**
     * Take a screenshot of the specified SurfaceControl.
     *
     * @param t the transaction used to set changes on the resulting screenshot.
     * @param sc the SurfaceControl to take a screenshot of
     * @param crop the crop to use when capturing the screenshot
     *
     * @return A SurfaceControl where the screenshot will be attached, or null if failed.
     */
    public static SurfaceControl takeScreenshot(SurfaceControl.Transaction t, SurfaceControl sc,
            Rect crop) {
        final SurfaceControl.ScreenshotHardwareBuffer buffer = SurfaceControl.captureLayers(
                new SurfaceControl.LayerCaptureArgs.Builder(sc)
                        .setSourceCrop(crop)
                        .setCaptureSecureLayers(true)
                        .setAllowProtected(true)
                        .build()
        );
        if (buffer == null || buffer.getHardwareBuffer() == null) {
            return null;
        }
        final GraphicBuffer graphicBuffer = GraphicBuffer.createFromHardwareBuffer(
                buffer.getHardwareBuffer());
        final SurfaceControl screenshot = new SurfaceControl.Builder()
                .setName("ScreenshotUtils screenshot")
                .setFormat(PixelFormat.TRANSLUCENT)
                .setSecure(buffer.containsSecureLayers())
                .setCallsite("ScreenshotUtils.takeScreenshot")
                .setBLASTLayer()
                .build();

        t.setBuffer(screenshot, graphicBuffer);
        t.setColorSpace(screenshot, buffer.getColorSpace());
        t.reparent(screenshot, sc);
        t.setLayer(screenshot, Integer.MAX_VALUE);
        t.show(screenshot);
        t.apply();
        return screenshot;
    }
}
+22 −17
Original line number Original line Diff line number Diff line
@@ -70,6 +70,7 @@ import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.animation.Interpolators;
import com.android.wm.shell.animation.Interpolators;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.ScreenshotUtils;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.common.annotations.ShellMainThread;
@@ -1119,6 +1120,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
    private void finishResize(SurfaceControl.Transaction tx, Rect destinationBounds,
    private void finishResize(SurfaceControl.Transaction tx, Rect destinationBounds,
            @PipAnimationController.TransitionDirection int direction,
            @PipAnimationController.TransitionDirection int direction,
            @PipAnimationController.AnimationType int type) {
            @PipAnimationController.AnimationType int type) {
        final Rect preResizeBounds = new Rect(mPipBoundsState.getBounds());
        mPipBoundsState.setBounds(destinationBounds);
        mPipBoundsState.setBounds(destinationBounds);
        if (direction == TRANSITION_DIRECTION_REMOVE_STACK) {
        if (direction == TRANSITION_DIRECTION_REMOVE_STACK) {
            removePipImmediately();
            removePipImmediately();
@@ -1142,19 +1144,19 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
                && mPictureInPictureParams != null
                && mPictureInPictureParams != null
                && !mPictureInPictureParams.isSeamlessResizeEnabled();
                && !mPictureInPictureParams.isSeamlessResizeEnabled();
        if (animateCrossFadeResize) {
        if (animateCrossFadeResize) {
            // Take a snapshot of the PIP task and hide it. We'll show it and fade it out after
            // Take a snapshot of the PIP task and show it. We'll fade it out after the wct
            // the wct transaction is applied and the activity is laid out again.
            // transaction is applied and the activity is laid out again.
            final SurfaceControl snapshotSurface = mTaskOrganizer.takeScreenshot(mToken);
            preResizeBounds.offsetTo(0, 0);
            mSurfaceTransactionHelper.reparentAndShowSurfaceSnapshot(
            final Rect snapshotDest = new Rect(0, 0, destinationBounds.width(),
                    mSurfaceControlTransactionFactory.getTransaction(), mLeash, snapshotSurface);
                    destinationBounds.height());
            final SurfaceControl snapshotSurface = ScreenshotUtils.takeScreenshot(
                    mSurfaceControlTransactionFactory.getTransaction(), mLeash, preResizeBounds);
            if (snapshotSurface != null) {
                mSyncTransactionQueue.queue(wct);
                mSyncTransactionQueue.queue(wct);
                mSyncTransactionQueue.runInSync(t -> {
                mSyncTransactionQueue.runInSync(t -> {
                    // Scale the snapshot from its pre-resize bounds to the post-resize bounds.
                    // Scale the snapshot from its pre-resize bounds to the post-resize bounds.
                final Rect snapshotSrc = new Rect(0, 0, snapshotSurface.getWidth(),
                    mSurfaceTransactionHelper.scale(t, snapshotSurface, preResizeBounds,
                        snapshotSurface.getHeight());
                            snapshotDest);
                final Rect snapshotDest = new Rect(0, 0, destinationBounds.width(),
                        destinationBounds.height());
                mSurfaceTransactionHelper.scale(t, snapshotSurface, snapshotSrc, snapshotDest);


                    // Start animation to fade out the snapshot.
                    // Start animation to fade out the snapshot.
                    fadeOutAndRemoveOverlay(snapshotSurface);
                    fadeOutAndRemoveOverlay(snapshotSurface);
@@ -1162,6 +1164,9 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
            } else {
            } else {
                applyFinishBoundsResize(wct, direction);
                applyFinishBoundsResize(wct, direction);
            }
            }
        } else {
            applyFinishBoundsResize(wct, direction);
        }


        finishResizeForMenu(destinationBounds);
        finishResizeForMenu(destinationBounds);
    }
    }
+0 −42
Original line number Original line Diff line number Diff line
@@ -16,7 +16,6 @@


package com.android.server.wm;
package com.android.server.wm;


import static android.Manifest.permission.READ_FRAME_BUFFER;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_LAUNCH_TASK;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_LAUNCH_TASK;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER;
@@ -37,8 +36,6 @@ import android.annotation.Nullable;
import android.app.WindowConfiguration;
import android.app.WindowConfiguration;
import android.content.pm.ActivityInfo;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.content.res.Configuration;
import android.graphics.GraphicBuffer;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.Rect;
import android.os.Binder;
import android.os.Binder;
import android.os.Bundle;
import android.os.Bundle;
@@ -53,7 +50,6 @@ import android.window.ITaskOrganizerController;
import android.window.ITransitionPlayer;
import android.window.ITransitionPlayer;
import android.window.IWindowContainerTransactionCallback;
import android.window.IWindowContainerTransactionCallback;
import android.window.IWindowOrganizerController;
import android.window.IWindowOrganizerController;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
import android.window.WindowContainerTransaction;


import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting;
@@ -786,44 +782,6 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
        mTransactionCallbacksByPendingSyncId.remove(syncId);
        mTransactionCallbacksByPendingSyncId.remove(syncId);
    }
    }


    @Override
    public boolean takeScreenshot(WindowContainerToken token, SurfaceControl outSurfaceControl) {
        mService.mAmInternal.enforceCallingPermission(READ_FRAME_BUFFER, "takeScreenshot()");
        final WindowContainer wc = WindowContainer.fromBinder(token.asBinder());
        if (wc == null) {
            throw new RuntimeException("Invalid token in screenshot transaction");
        }

        final Rect bounds = new Rect();
        wc.getBounds(bounds);
        bounds.offsetTo(0, 0);
        SurfaceControl.ScreenshotHardwareBuffer buffer = SurfaceControl.captureLayers(
                wc.getSurfaceControl(), bounds, 1);

        if (buffer == null || buffer.getHardwareBuffer() == null) {
            return false;
        }

        GraphicBuffer graphicBuffer = GraphicBuffer.createFromHardwareBuffer(
                buffer.getHardwareBuffer());
        SurfaceControl screenshot = mService.mWindowManager.mSurfaceControlFactory.apply(null)
                .setName(wc.getName() + " - Organizer Screenshot")
                .setFormat(PixelFormat.TRANSLUCENT)
                .setParent(wc.getParentSurfaceControl())
                .setSecure(buffer.containsSecureLayers())
                .setCallsite("WindowOrganizerController.takeScreenshot")
                .setBLASTLayer()
                .build();

        SurfaceControl.Transaction transaction = mService.mWindowManager.mTransactionFactory.get();
        transaction.setBuffer(screenshot, graphicBuffer);
        transaction.setColorSpace(screenshot, buffer.getColorSpace());
        transaction.apply();

        outSurfaceControl.copyFrom(screenshot, "WindowOrganizerController.takeScreenshot");
        return true;
    }

    @Override
    @Override
    public void registerTransitionPlayer(ITransitionPlayer player) {
    public void registerTransitionPlayer(ITransitionPlayer player) {
        enforceTaskPermission("registerTransitionPlayer()");
        enforceTaskPermission("registerTransitionPlayer()");