Merge "Take PIP snapshot in the shell and use bounds state size for crossfading" into sc-dev

This commit is contained in:
TreeHugger Robot
2021-05-19 01:06:32 +00:00
committed by Android (Google) Code Review
5 changed files with 89 additions and 92 deletions

View File

@@ -77,17 +77,6 @@ interface IWindowOrganizerController {
/** @return An interface enabling the management of display area organizers. */
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
* this will replace the existing one if set.

View File

@@ -25,7 +25,6 @@ import android.app.ActivityTaskManager;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Singleton;
import android.view.SurfaceControl;
/**
* 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.
* @hide

View File

@@ -0,0 +1,68 @@
/*
* 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;
}
}

View File

@@ -70,6 +70,7 @@ import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.animation.Interpolators;
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.SyncTransactionQueue;
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,
@PipAnimationController.TransitionDirection int direction,
@PipAnimationController.AnimationType int type) {
final Rect preResizeBounds = new Rect(mPipBoundsState.getBounds());
mPipBoundsState.setBounds(destinationBounds);
if (direction == TRANSITION_DIRECTION_REMOVE_STACK) {
removePipImmediately();
@@ -1142,23 +1144,26 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
&& mPictureInPictureParams != null
&& !mPictureInPictureParams.isSeamlessResizeEnabled();
if (animateCrossFadeResize) {
// Take a snapshot of the PIP task and hide it. We'll show it and fade it out after
// the wct transaction is applied and the activity is laid out again.
final SurfaceControl snapshotSurface = mTaskOrganizer.takeScreenshot(mToken);
mSurfaceTransactionHelper.reparentAndShowSurfaceSnapshot(
mSurfaceControlTransactionFactory.getTransaction(), mLeash, snapshotSurface);
mSyncTransactionQueue.queue(wct);
mSyncTransactionQueue.runInSync(t -> {
// Scale the snapshot from its pre-resize bounds to the post-resize bounds.
final Rect snapshotSrc = new Rect(0, 0, snapshotSurface.getWidth(),
snapshotSurface.getHeight());
final Rect snapshotDest = new Rect(0, 0, destinationBounds.width(),
destinationBounds.height());
mSurfaceTransactionHelper.scale(t, snapshotSurface, snapshotSrc, snapshotDest);
// Take a snapshot of the PIP task and show it. We'll fade it out after the wct
// transaction is applied and the activity is laid out again.
preResizeBounds.offsetTo(0, 0);
final Rect snapshotDest = new Rect(0, 0, destinationBounds.width(),
destinationBounds.height());
final SurfaceControl snapshotSurface = ScreenshotUtils.takeScreenshot(
mSurfaceControlTransactionFactory.getTransaction(), mLeash, preResizeBounds);
if (snapshotSurface != null) {
mSyncTransactionQueue.queue(wct);
mSyncTransactionQueue.runInSync(t -> {
// Scale the snapshot from its pre-resize bounds to the post-resize bounds.
mSurfaceTransactionHelper.scale(t, snapshotSurface, preResizeBounds,
snapshotDest);
// Start animation to fade out the snapshot.
fadeOutAndRemoveOverlay(snapshotSurface);
});
// Start animation to fade out the snapshot.
fadeOutAndRemoveOverlay(snapshotSurface);
});
} else {
applyFinishBoundsResize(wct, direction);
}
} else {
applyFinishBoundsResize(wct, direction);
}

View File

@@ -16,7 +16,6 @@
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_LAUNCH_TASK;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER;
@@ -37,8 +36,6 @@ import android.annotation.Nullable;
import android.app.WindowConfiguration;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.GraphicBuffer;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.os.Binder;
import android.os.Bundle;
@@ -53,7 +50,6 @@ import android.window.ITaskOrganizerController;
import android.window.ITransitionPlayer;
import android.window.IWindowContainerTransactionCallback;
import android.window.IWindowOrganizerController;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
import com.android.internal.annotations.VisibleForTesting;
@@ -786,44 +782,6 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
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
public void registerTransitionPlayer(ITransitionPlayer player) {
enforceTaskPermission("registerTransitionPlayer()");