blob: a017a2674359fa7b855e5fdbbb541a16c5d10815 [file] [log] [blame]
/*
* Copyright (C) 2020 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.pip;
import android.content.Context;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.RectF;
import android.view.SurfaceControl;
import com.android.wm.shell.R;
import com.android.wm.shell.transition.Transitions;
/**
* Abstracts the common operations on {@link SurfaceControl.Transaction} for PiP transition.
*/
public class PipSurfaceTransactionHelper {
/** for {@link #scale(SurfaceControl.Transaction, SurfaceControl, Rect, Rect)} operation */
private final Matrix mTmpTransform = new Matrix();
private final float[] mTmpFloat9 = new float[9];
private final RectF mTmpSourceRectF = new RectF();
private final RectF mTmpDestinationRectF = new RectF();
private final Rect mTmpDestinationRect = new Rect();
private int mCornerRadius;
private int mShadowRadius;
/**
* Called when display size or font size of settings changed
*
* @param context the current context
*/
public void onDensityOrFontScaleChanged(Context context) {
mCornerRadius = context.getResources().getDimensionPixelSize(R.dimen.pip_corner_radius);
mShadowRadius = context.getResources().getDimensionPixelSize(R.dimen.pip_shadow_radius);
}
/**
* Operates the alpha on a given transaction and leash
* @return same {@link PipSurfaceTransactionHelper} instance for method chaining
*/
public PipSurfaceTransactionHelper alpha(SurfaceControl.Transaction tx, SurfaceControl leash,
float alpha) {
tx.setAlpha(leash, alpha);
return this;
}
/**
* Operates the crop (and position) on a given transaction and leash
* @return same {@link PipSurfaceTransactionHelper} instance for method chaining
*/
public PipSurfaceTransactionHelper crop(SurfaceControl.Transaction tx, SurfaceControl leash,
Rect destinationBounds) {
tx.setWindowCrop(leash, destinationBounds.width(), destinationBounds.height())
.setPosition(leash, destinationBounds.left, destinationBounds.top);
return this;
}
/**
* Operates the scale (setMatrix) on a given transaction and leash
* @return same {@link PipSurfaceTransactionHelper} instance for method chaining
*/
public PipSurfaceTransactionHelper scale(SurfaceControl.Transaction tx, SurfaceControl leash,
Rect sourceBounds, Rect destinationBounds) {
return scale(tx, leash, sourceBounds, destinationBounds, 0 /* degrees */);
}
/**
* Operates the scale (setMatrix) on a given transaction and leash, along with a rotation.
* @return same {@link PipSurfaceTransactionHelper} instance for method chaining
*/
public PipSurfaceTransactionHelper scale(SurfaceControl.Transaction tx, SurfaceControl leash,
Rect sourceBounds, Rect destinationBounds, float degrees) {
mTmpSourceRectF.set(sourceBounds);
// We want the matrix to position the surface relative to the screen coordinates so offset
// the source to 0,0
mTmpSourceRectF.offsetTo(0, 0);
mTmpDestinationRectF.set(destinationBounds);
mTmpTransform.setRectToRect(mTmpSourceRectF, mTmpDestinationRectF, Matrix.ScaleToFit.FILL);
mTmpTransform.postRotate(degrees,
mTmpDestinationRectF.centerX(), mTmpDestinationRectF.centerY());
tx.setMatrix(leash, mTmpTransform, mTmpFloat9);
return this;
}
/**
* Operates the scale (setMatrix) on a given transaction and leash
* @return same {@link PipSurfaceTransactionHelper} instance for method chaining
*/
public PipSurfaceTransactionHelper scaleAndCrop(SurfaceControl.Transaction tx,
SurfaceControl leash, Rect sourceRectHint,
Rect sourceBounds, Rect destinationBounds, Rect insets,
boolean isInPipDirection) {
mTmpDestinationRect.set(sourceBounds);
// Similar to {@link #scale}, we want to position the surface relative to the screen
// coordinates so offset the bounds to 0,0
mTmpDestinationRect.offsetTo(0, 0);
mTmpDestinationRect.inset(insets);
// Scale by the shortest edge and offset such that the top/left of the scaled inset source
// rect aligns with the top/left of the destination bounds
final float scale;
if (isInPipDirection
&& sourceRectHint != null && sourceRectHint.width() < sourceBounds.width()) {
// scale by sourceRectHint if it's not edge-to-edge, for entering PiP transition only.
scale = sourceBounds.width() <= sourceBounds.height()
? (float) destinationBounds.width() / sourceRectHint.width()
: (float) destinationBounds.height() / sourceRectHint.height();
} else {
scale = sourceBounds.width() <= sourceBounds.height()
? (float) destinationBounds.width() / sourceBounds.width()
: (float) destinationBounds.height() / sourceBounds.height();
}
final float left = destinationBounds.left - insets.left * scale;
final float top = destinationBounds.top - insets.top * scale;
mTmpTransform.setScale(scale, scale);
tx.setMatrix(leash, mTmpTransform, mTmpFloat9)
.setCrop(leash, mTmpDestinationRect)
.setPosition(leash, left, top);
return this;
}
/**
* Operates the rotation according to the given degrees and scale (setMatrix) according to the
* source bounds and rotated destination bounds. The crop will be the unscaled source bounds.
* @return same {@link PipSurfaceTransactionHelper} instance for method chaining
*/
public PipSurfaceTransactionHelper rotateAndScaleWithCrop(SurfaceControl.Transaction tx,
SurfaceControl leash, Rect sourceBounds, Rect destinationBounds, Rect insets,
float degrees, float positionX, float positionY, boolean isExpanding,
boolean clockwise) {
mTmpDestinationRect.set(sourceBounds);
mTmpDestinationRect.inset(insets);
final int srcW = mTmpDestinationRect.width();
final int srcH = mTmpDestinationRect.height();
final int destW = destinationBounds.width();
final int destH = destinationBounds.height();
// Scale by the short side so there won't be empty area if the aspect ratio of source and
// destination are different.
final float scale = srcW <= srcH ? (float) destW / srcW : (float) destH / srcH;
final Rect crop = mTmpDestinationRect;
crop.set(0, 0, Transitions.SHELL_TRANSITIONS_ROTATION ? destH
: destW, Transitions.SHELL_TRANSITIONS_ROTATION ? destW : destH);
// Inverse scale for crop to fit in screen coordinates.
crop.scale(1 / scale);
crop.offset(insets.left, insets.top);
if (isExpanding) {
// Expand bounds (shrink insets) in source orientation.
positionX -= insets.left * scale;
positionY -= insets.top * scale;
} else {
// Shrink bounds (expand insets) in destination orientation.
if (clockwise) {
positionX -= insets.top * scale;
positionY += insets.left * scale;
} else {
positionX += insets.top * scale;
positionY -= insets.left * scale;
}
}
mTmpTransform.setScale(scale, scale);
mTmpTransform.postRotate(degrees);
mTmpTransform.postTranslate(positionX, positionY);
tx.setMatrix(leash, mTmpTransform, mTmpFloat9).setCrop(leash, crop);
return this;
}
/**
* Resets the scale (setMatrix) on a given transaction and leash if there's any
*
* @return same {@link PipSurfaceTransactionHelper} instance for method chaining
*/
public PipSurfaceTransactionHelper resetScale(SurfaceControl.Transaction tx,
SurfaceControl leash,
Rect destinationBounds) {
tx.setMatrix(leash, Matrix.IDENTITY_MATRIX, mTmpFloat9)
.setPosition(leash, destinationBounds.left, destinationBounds.top);
return this;
}
/**
* Operates the round corner radius on a given transaction and leash
* @return same {@link PipSurfaceTransactionHelper} instance for method chaining
*/
public PipSurfaceTransactionHelper round(SurfaceControl.Transaction tx, SurfaceControl leash,
boolean applyCornerRadius) {
tx.setCornerRadius(leash, applyCornerRadius ? mCornerRadius : 0);
return this;
}
/**
* Operates the round corner radius on a given transaction and leash, scaled by bounds
* @return same {@link PipSurfaceTransactionHelper} instance for method chaining
*/
public PipSurfaceTransactionHelper round(SurfaceControl.Transaction tx, SurfaceControl leash,
Rect fromBounds, Rect toBounds) {
final float scale = (float) (Math.hypot(fromBounds.width(), fromBounds.height())
/ Math.hypot(toBounds.width(), toBounds.height()));
tx.setCornerRadius(leash, mCornerRadius * scale);
return this;
}
/**
* Operates the shadow radius on a given transaction and leash
* @return same {@link PipSurfaceTransactionHelper} instance for method chaining
*/
public PipSurfaceTransactionHelper shadow(SurfaceControl.Transaction tx, SurfaceControl leash,
boolean applyShadowRadius) {
tx.setShadowRadius(leash, applyShadowRadius ? mShadowRadius : 0);
return this;
}
public interface SurfaceControlTransactionFactory {
SurfaceControl.Transaction getTransaction();
}
}