Merge changes from topic "edge-extension"

* changes:
  Support edge extension of windows during animations in shell
  Add API for extending windows during animations
This commit is contained in:
Pablo Gamito
2022-02-01 21:08:52 +00:00
committed by Android (Google) Code Review
9 changed files with 492 additions and 19 deletions

View File

@@ -729,6 +729,10 @@ package android {
field public static final int freezesText = 16843116; // 0x101016c
field public static final int fromAlpha = 16843210; // 0x10101ca
field public static final int fromDegrees = 16843187; // 0x10101b3
field public static final int fromExtendBottom;
field public static final int fromExtendLeft;
field public static final int fromExtendRight;
field public static final int fromExtendTop;
field public static final int fromId = 16843850; // 0x101044a
field public static final int fromScene = 16843741; // 0x10103dd
field public static final int fromXDelta = 16843206; // 0x10101c6
@@ -1574,6 +1578,10 @@ package android {
field public static final int titleTextStyle = 16843512; // 0x10102f8
field public static final int toAlpha = 16843211; // 0x10101cb
field public static final int toDegrees = 16843188; // 0x10101b4
field public static final int toExtendBottom;
field public static final int toExtendLeft;
field public static final int toExtendRight;
field public static final int toExtendTop;
field public static final int toId = 16843849; // 0x1010449
field public static final int toScene = 16843742; // 0x10103de
field public static final int toXDelta = 16843207; // 0x10101c7

View File

@@ -864,6 +864,15 @@ public abstract class Animation implements Cloneable {
return mHasRoundedCorners;
}
/**
* @return if a window animation has outsets applied to it.
*
* @hide
*/
public boolean hasExtension() {
return false;
}
/**
* If showBackground is {@code true} and this animation is applied on a window, then the windows
* in the animation will animate with the background associated with this window behind them.
@@ -941,6 +950,21 @@ public abstract class Animation implements Cloneable {
return (getStartOffset() + getDuration()) * (getRepeatCount() + 1);
}
/**
* Gets the transformation to apply a specific point in time. Implementations of this method
* should always be kept in sync with getTransformation.
*
* @param normalizedTime time between 0 and 1 where 0 is the start of the animation and 1 the
* end.
* @param outTransformation A transformation object that is provided by the
* caller and will be filled in by the animation.
* @hide
*/
public void getTransformationAt(float normalizedTime, Transformation outTransformation) {
final float interpolatedTime = mInterpolator.getInterpolation(normalizedTime);
applyTransformation(interpolatedTime, outTransformation);
}
/**
* Gets the transformation to apply at a specified point in time. Implementations of this
* method should always replace the specified Transformation or document they are doing
@@ -987,8 +1011,7 @@ public abstract class Animation implements Cloneable {
normalizedTime = 1.0f - normalizedTime;
}
final float interpolatedTime = mInterpolator.getInterpolation(normalizedTime);
applyTransformation(interpolatedTime, outTransformation);
getTransformationAt(normalizedTime, outTransformation);
}
if (expired) {

View File

@@ -359,6 +359,26 @@ public class AnimationSet extends Animation {
}
}
/**
* The transformation of an animation set is the concatenation of all of its
* component animations.
*
* @see android.view.animation.Animation#getTransformationAt
* @hide
*/
@Override
public void getTransformationAt(float interpolatedTime, Transformation t) {
final Transformation temp = mTempTransformation;
for (int i = mAnimations.size() - 1; i >= 0; --i) {
final Animation a = mAnimations.get(i);
temp.clear();
a.getTransformationAt(interpolatedTime, t);
t.compose(temp);
}
}
/**
* The transformation of an animation set is the concatenation of all of its
* component animations.
@@ -517,4 +537,15 @@ public class AnimationSet extends Animation {
public boolean willChangeBounds() {
return (mFlags & PROPERTY_CHANGE_BOUNDS_MASK) == PROPERTY_CHANGE_BOUNDS_MASK;
}
/** @hide */
@Override
public boolean hasExtension() {
for (Animation animation : mAnimations) {
if (animation.hasExtension()) {
return true;
}
}
return false;
}
}

View File

@@ -190,6 +190,8 @@ public class AnimationUtils {
anim = new TranslateAnimation(c, attrs);
} else if (name.equals("cliprect")) {
anim = new ClipRectAnimation(c, attrs);
} else if (name.equals("extend")) {
anim = new ExtendAnimation(c, attrs);
} else {
throw new RuntimeException("Unknown animation name: " + parser.getName());
}

View File

@@ -0,0 +1,176 @@
/*
* Copyright (C) 2022 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 android.view.animation;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Insets;
import android.util.AttributeSet;
/**
* An animation that controls the outset of an object.
*
* @hide
*/
public class ExtendAnimation extends Animation {
protected Insets mFromInsets = Insets.NONE;
protected Insets mToInsets = Insets.NONE;
private int mFromLeftType = ABSOLUTE;
private int mFromTopType = ABSOLUTE;
private int mFromRightType = ABSOLUTE;
private int mFromBottomType = ABSOLUTE;
private int mToLeftType = ABSOLUTE;
private int mToTopType = ABSOLUTE;
private int mToRightType = ABSOLUTE;
private int mToBottomType = ABSOLUTE;
private float mFromLeftValue;
private float mFromTopValue;
private float mFromRightValue;
private float mFromBottomValue;
private float mToLeftValue;
private float mToTopValue;
private float mToRightValue;
private float mToBottomValue;
/**
* Constructor used when an ExtendAnimation is loaded from a resource.
*
* @param context Application context to use
* @param attrs Attribute set from which to read values
*/
public ExtendAnimation(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs,
com.android.internal.R.styleable.ExtendAnimation);
Description d = Description.parseValue(a.peekValue(
com.android.internal.R.styleable.ExtendAnimation_fromExtendLeft));
mFromLeftType = d.type;
mFromLeftValue = d.value;
d = Description.parseValue(a.peekValue(
com.android.internal.R.styleable.ExtendAnimation_fromExtendTop));
mFromTopType = d.type;
mFromTopValue = d.value;
d = Description.parseValue(a.peekValue(
com.android.internal.R.styleable.ExtendAnimation_fromExtendRight));
mFromRightType = d.type;
mFromRightValue = d.value;
d = Description.parseValue(a.peekValue(
com.android.internal.R.styleable.ExtendAnimation_fromExtendBottom));
mFromBottomType = d.type;
mFromBottomValue = d.value;
d = Description.parseValue(a.peekValue(
com.android.internal.R.styleable.ExtendAnimation_toExtendLeft));
mToLeftType = d.type;
mToLeftValue = d.value;
d = Description.parseValue(a.peekValue(
com.android.internal.R.styleable.ExtendAnimation_toExtendTop));
mToTopType = d.type;
mToTopValue = d.value;
d = Description.parseValue(a.peekValue(
com.android.internal.R.styleable.ExtendAnimation_toExtendRight));
mToRightType = d.type;
mToRightValue = d.value;
d = Description.parseValue(a.peekValue(
com.android.internal.R.styleable.ExtendAnimation_toExtendBottom));
mToBottomType = d.type;
mToBottomValue = d.value;
a.recycle();
}
/**
* Constructor to use when building an ExtendAnimation from code
*
* @param fromInsets the insets to animate from
* @param toInsets the insets to animate to
*/
public ExtendAnimation(Insets fromInsets, Insets toInsets) {
if (fromInsets == null || toInsets == null) {
throw new RuntimeException("Expected non-null animation outsets");
}
mFromLeftValue = -fromInsets.left;
mFromTopValue = -fromInsets.top;
mFromRightValue = -fromInsets.right;
mFromBottomValue = -fromInsets.bottom;
mToLeftValue = -toInsets.left;
mToTopValue = -toInsets.top;
mToRightValue = -toInsets.right;
mToBottomValue = -toInsets.bottom;
}
/**
* Constructor to use when building an ExtendAnimation from code
*/
public ExtendAnimation(int fromL, int fromT, int fromR, int fromB,
int toL, int toT, int toR, int toB) {
this(Insets.of(-fromL, -fromT, -fromR, -fromB), Insets.of(-toL, -toT, -toR, -toB));
}
@Override
protected void applyTransformation(float it, Transformation tr) {
int l = mFromInsets.left + (int) ((mToInsets.left - mFromInsets.left) * it);
int t = mFromInsets.top + (int) ((mToInsets.top - mFromInsets.top) * it);
int r = mFromInsets.right + (int) ((mToInsets.right - mFromInsets.right) * it);
int b = mFromInsets.bottom + (int) ((mToInsets.bottom - mFromInsets.bottom) * it);
tr.setInsets(l, t, r, b);
}
@Override
public boolean willChangeTransformationMatrix() {
return false;
}
/** @hide */
@Override
public boolean hasExtension() {
return mFromInsets.left < 0 || mFromInsets.top < 0 || mFromInsets.right < 0
|| mFromInsets.bottom < 0;
}
@Override
public void initialize(int width, int height, int parentWidth, int parentHeight) {
super.initialize(width, height, parentWidth, parentHeight);
// We remove any negative extension (i.e. positive insets) and set those to 0
mFromInsets = Insets.min(Insets.of(
-(int) resolveSize(mFromLeftType, mFromLeftValue, width, parentWidth),
-(int) resolveSize(mFromTopType, mFromTopValue, height, parentHeight),
-(int) resolveSize(mFromRightType, mFromRightValue, width, parentWidth),
-(int) resolveSize(mFromBottomType, mFromBottomValue, height, parentHeight)
), Insets.NONE);
mToInsets = Insets.min(Insets.of(
-(int) resolveSize(mToLeftType, mToLeftValue, width, parentWidth),
-(int) resolveSize(mToTopType, mToTopValue, height, parentHeight),
-(int) resolveSize(mToRightType, mToRightValue, width, parentWidth),
-(int) resolveSize(mToBottomType, mToBottomValue, height, parentHeight)
), Insets.NONE);
}
}

View File

@@ -18,6 +18,7 @@ package android.view.animation;
import android.annotation.FloatRange;
import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.Insets;
import android.graphics.Matrix;
import android.graphics.Rect;
@@ -53,6 +54,8 @@ public class Transformation {
private boolean mHasClipRect;
private Rect mClipRect = new Rect();
private Insets mInsets = Insets.NONE;
/**
* Creates a new transformation with alpha = 1 and the identity matrix.
*/
@@ -132,8 +135,9 @@ public class Transformation {
setClipRect(bounds);
}
}
setInsets(Insets.add(getInsets(), t.getInsets()));
}
/**
* Like {@link #compose(Transformation)} but does this.postConcat(t) of
* the transformation matrix.
@@ -160,7 +164,7 @@ public class Transformation {
public Matrix getMatrix() {
return mMatrix;
}
/**
* Sets the degree of transparency
* @param alpha 1.0 means fully opaqe and 0.0 means fully transparent
@@ -169,6 +173,13 @@ public class Transformation {
mAlpha = alpha;
}
/**
* @return The degree of transparency
*/
public float getAlpha() {
return mAlpha;
}
/**
* Sets the current Transform's clip rect
* @hide
@@ -203,12 +214,29 @@ public class Transformation {
}
/**
* @return The degree of transparency
* Sets the current Transform's insets
* @hide
*/
public float getAlpha() {
return mAlpha;
public void setInsets(Insets insets) {
mInsets = insets;
}
/**
* Sets the current Transform's insets
* @hide
*/
public void setInsets(int left, int top, int right, int bottom) {
mInsets = Insets.of(left, top, right, bottom);
}
/**
* Returns the current Transform's outset rect
* @hide
*/
public Insets getInsets() {
return mInsets;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder(64);
@@ -216,7 +244,7 @@ public class Transformation {
toShortString(sb);
return sb.toString();
}
/**
* Return a string representation of the transformation in a compact form.
*/
@@ -225,7 +253,7 @@ public class Transformation {
toShortString(sb);
return sb.toString();
}
/**
* @hide
*/
@@ -234,7 +262,7 @@ public class Transformation {
sb.append(" matrix="); sb.append(mMatrix.toShortString());
sb.append('}');
}
/**
* Print short string, to optimize dumping.
* @hide

View File

@@ -2885,7 +2885,7 @@
<code>public void sayHello(View v)</code> method of your context
(typically, your Activity).
{@deprecated View actually traverses the Context
hierarchy looking for the relevant method, which is fragile (an intermediate
hierarchy looking for the relevant method, which is fragile (an intermediate
ContextWrapper adding a same-named method would change behavior) and restricts
bytecode optimizers such as R8. Instead, use View.setOnClickListener.}-->
<attr name="onClick" format="string" />
@@ -6975,6 +6975,34 @@
<attr name="toBottom" format="fraction" />
</declare-styleable>
<!-- Defines the ExtendAnimation used to extend windows during animations -->
<declare-styleable name="ExtendAnimation">
<!-- Defines the amount a window should be extended outward from the left at
the start of the animation. -->
<attr name="fromExtendLeft" format="float|fraction" />
<!-- Defines the amount a window should be extended outward from the top at
the start of the animation. -->
<attr name="fromExtendTop" format="float|fraction" />
<!-- Defines the amount a window should be extended outward from the right at
the start of the animation. -->
<attr name="fromExtendRight" format="float|fraction" />
<!-- Defines the amount a window should be extended outward from the bottom at
the start of the animation. -->
<attr name="fromExtendBottom" format="float|fraction" />
<!-- Defines the amount a window should be extended outward from the left by
the end of the animation by transitioning from the fromExtendLeft amount. -->
<attr name="toExtendLeft" format="float|fraction" />
<!-- Defines the amount a window should be extended outward from the top by
the end of the animation by transitioning from the fromExtendTop amount. -->
<attr name="toExtendTop" format="float|fraction" />
<!-- Defines the amount a window should be extended outward from the right by
the end of the animation by transitioning from the fromExtendRight amount. -->
<attr name="toExtendRight" format="float|fraction" />
<!-- Defines the amount a window should be extended outward from the bottom by
the end of the animation by transitioning from the fromExtendBottom amount. -->
<attr name="toExtendBottom" format="float|fraction" />
</declare-styleable>
<declare-styleable name="LayoutAnimation">
<!-- Fraction of the animation duration used to delay the beginning of
the animation of each child. -->
@@ -7827,7 +7855,7 @@
<!-- Name of a method on the Context used to inflate the menu that will be
called when the item is clicked.
{@deprecated Menu actually traverses the Context hierarchy looking for the
{@deprecated Menu actually traverses the Context hierarchy looking for the
relevant method, which is fragile (an intermediate ContextWrapper adding a
same-named method would change behavior) and restricts bytecode optimizers
such as R8. Instead, use MenuItem.setOnMenuItemClickListener.} -->

View File

@@ -3260,6 +3260,14 @@
<public name="inheritKeyStoreKeys" />
<public name="preferKeepClear" />
<public name="autoHandwritingEnabled" />
<public name="fromExtendLeft" />
<public name="fromExtendTop" />
<public name="fromExtendRight" />
<public name="fromExtendBottom" />
<public name="toExtendLeft" />
<public name="toExtendTop" />
<public name="toExtendRight" />
<public name="toExtendBottom" />
</staging-public-group>
<staging-public-group type="id" first-id="0x01de0000">

View File

@@ -67,7 +67,11 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Insets;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
@@ -78,6 +82,7 @@ import android.os.SystemProperties;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.view.Choreographer;
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
import android.view.WindowManager;
@@ -103,6 +108,8 @@ import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
/** The default handler that handles anything not already handled. */
public class DefaultTransitionHandler implements Transitions.TransitionHandler {
@@ -331,6 +338,9 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
};
final List<Consumer<SurfaceControl.Transaction>> postStartTransactionCallbacks =
new ArrayList<>();
@ColorInt int backgroundColorForTransition = 0;
final int wallpaperTransit = getWallpaperTransitType(info);
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
@@ -402,13 +412,15 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
}
}
float cornerRadius = 0;
final float cornerRadius;
if (a.hasRoundedCorners() && isTask) {
// hasRoundedCorners is currently only enabled for tasks
final Context displayContext =
mDisplayController.getDisplayContext(change.getTaskInfo().displayId);
cornerRadius =
ScreenDecorationsUtils.getWindowCornerRadius(displayContext);
} else {
cornerRadius = 0;
}
if (a.getShowBackground()) {
@@ -424,12 +436,37 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
}
}
boolean delayedEdgeExtension = false;
if (!isTask && a.hasExtension()) {
if (!Transitions.isOpeningType(change.getMode())) {
// Can screenshot now (before startTransaction is applied)
edgeExtendWindow(change, a, startTransaction, finishTransaction);
} else {
// Need to screenshot after startTransaction is applied otherwise activity
// may not be visible or ready yet.
postStartTransactionCallbacks
.add(t -> edgeExtendWindow(change, a, t, finishTransaction));
delayedEdgeExtension = true;
}
}
final Rect clipRect = Transitions.isClosingType(change.getMode())
? mRotator.getEndBoundsInStartRotation(change)
: change.getEndAbsBounds();
startSurfaceAnimation(animations, a, change.getLeash(), onAnimFinish,
mTransactionPool, mMainExecutor, mAnimExecutor, null /* position */,
cornerRadius, clipRect);
if (delayedEdgeExtension) {
// If the edge extension needs to happen after the startTransition has been
// applied, then we want to only start the animation after the edge extension
// postStartTransaction callback has been run
postStartTransactionCallbacks.add(t ->
startSurfaceAnimation(animations, a, change.getLeash(), onAnimFinish,
mTransactionPool, mMainExecutor, mAnimExecutor,
null /* position */, cornerRadius, clipRect));
} else {
startSurfaceAnimation(animations, a, change.getLeash(), onAnimFinish,
mTransactionPool, mMainExecutor, mAnimExecutor, null /* position */,
cornerRadius, clipRect);
}
if (info.getAnimationOptions() != null) {
attachThumbnail(animations, onAnimFinish, change, info.getAnimationOptions(),
@@ -443,7 +480,20 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
startTransaction, finishTransaction);
}
startTransaction.apply();
// postStartTransactionCallbacks require that the start transaction is already
// applied to run otherwise they may result in flickers and UI inconsistencies.
boolean waitForStartTransactionApply = postStartTransactionCallbacks.size() > 0;
startTransaction.apply(waitForStartTransactionApply);
// Run tasks that require startTransaction to already be applied
for (Consumer<SurfaceControl.Transaction> postStartTransactionCallback :
postStartTransactionCallbacks) {
final SurfaceControl.Transaction t = mTransactionPool.acquire();
postStartTransactionCallback.accept(t);
t.apply();
mTransactionPool.release(t);
}
mRotator.cleanUp(finishTransaction);
TransitionMetrics.getInstance().reportAnimationStart(transition);
// run finish now in-case there are no animations
@@ -451,6 +501,117 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
return true;
}
private void edgeExtendWindow(TransitionInfo.Change change,
Animation a, SurfaceControl.Transaction startTransaction,
SurfaceControl.Transaction finishTransaction) {
final Transformation transformationAtStart = new Transformation();
a.getTransformationAt(0, transformationAtStart);
final Transformation transformationAtEnd = new Transformation();
a.getTransformationAt(1, transformationAtEnd);
// We want to create an extension surface that is the maximal size and the animation will
// take care of cropping any part that overflows.
final Insets maxExtensionInsets = Insets.min(
transformationAtStart.getInsets(), transformationAtEnd.getInsets());
final int targetSurfaceHeight = Math.max(change.getStartAbsBounds().height(),
change.getEndAbsBounds().height());
final int targetSurfaceWidth = Math.max(change.getStartAbsBounds().width(),
change.getEndAbsBounds().width());
if (maxExtensionInsets.left < 0) {
final Rect edgeBounds = new Rect(0, 0, 1, targetSurfaceHeight);
final Rect extensionRect = new Rect(0, 0,
-maxExtensionInsets.left, targetSurfaceHeight);
final int xPos = maxExtensionInsets.left;
final int yPos = 0;
createExtensionSurface(change.getLeash(), edgeBounds, extensionRect, xPos, yPos,
"Left Edge Extension", startTransaction, finishTransaction);
}
if (maxExtensionInsets.top < 0) {
final Rect edgeBounds = new Rect(0, 0, targetSurfaceWidth, 1);
final Rect extensionRect = new Rect(0, 0,
targetSurfaceWidth, -maxExtensionInsets.top);
final int xPos = 0;
final int yPos = maxExtensionInsets.top;
createExtensionSurface(change.getLeash(), edgeBounds, extensionRect, xPos, yPos,
"Top Edge Extension", startTransaction, finishTransaction);
}
if (maxExtensionInsets.right < 0) {
final Rect edgeBounds = new Rect(targetSurfaceWidth - 1, 0,
targetSurfaceWidth, targetSurfaceHeight);
final Rect extensionRect = new Rect(0, 0,
-maxExtensionInsets.right, targetSurfaceHeight);
final int xPos = targetSurfaceWidth;
final int yPos = 0;
createExtensionSurface(change.getLeash(), edgeBounds, extensionRect, xPos, yPos,
"Right Edge Extension", startTransaction, finishTransaction);
}
if (maxExtensionInsets.bottom < 0) {
final Rect edgeBounds = new Rect(0, targetSurfaceHeight - 1,
targetSurfaceWidth, targetSurfaceHeight);
final Rect extensionRect = new Rect(0, 0,
targetSurfaceWidth, -maxExtensionInsets.bottom);
final int xPos = maxExtensionInsets.left;
final int yPos = targetSurfaceHeight;
createExtensionSurface(change.getLeash(), edgeBounds, extensionRect, xPos, yPos,
"Bottom Edge Extension", startTransaction, finishTransaction);
}
}
private SurfaceControl createExtensionSurface(SurfaceControl surfaceToExtend, Rect edgeBounds,
Rect extensionRect, int xPos, int yPos, String layerName,
SurfaceControl.Transaction startTransaction,
SurfaceControl.Transaction finishTransaction) {
final SurfaceControl edgeExtensionLayer = new SurfaceControl.Builder()
.setName(layerName)
.setParent(surfaceToExtend)
.setHidden(true)
.setCallsite("DefaultTransitionHandler#startAnimation")
.setOpaque(true)
.setBufferSize(extensionRect.width(), extensionRect.height())
.build();
SurfaceControl.LayerCaptureArgs captureArgs =
new SurfaceControl.LayerCaptureArgs.Builder(surfaceToExtend)
.setSourceCrop(edgeBounds)
.setFrameScale(1)
.setPixelFormat(PixelFormat.RGBA_8888)
.setChildrenOnly(true)
.setAllowProtected(true)
.build();
final SurfaceControl.ScreenshotHardwareBuffer edgeBuffer =
SurfaceControl.captureLayers(captureArgs);
if (edgeBuffer == null) {
ProtoLog.e(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
"Failed to capture edge of window.");
return null;
}
android.graphics.BitmapShader shader =
new android.graphics.BitmapShader(edgeBuffer.asBitmap(),
android.graphics.Shader.TileMode.CLAMP,
android.graphics.Shader.TileMode.CLAMP);
final Paint paint = new Paint();
paint.setShader(shader);
final Surface surface = new Surface(edgeExtensionLayer);
Canvas c = surface.lockHardwareCanvas();
c.drawRect(extensionRect, paint);
surface.unlockCanvasAndPost(c);
surface.release();
startTransaction.setLayer(edgeExtensionLayer, Integer.MIN_VALUE);
startTransaction.setPosition(edgeExtensionLayer, xPos, yPos);
startTransaction.setVisibility(edgeExtensionLayer, true);
finishTransaction.remove(edgeExtensionLayer);
return edgeExtensionLayer;
}
private void addBackgroundToTransition(
@NonNull SurfaceControl rootLeash,
@ColorInt int color,
@@ -778,9 +939,17 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
}
t.setMatrix(leash, transformation.getMatrix(), matrix);
t.setAlpha(leash, transformation.getAlpha());
Insets extensionInsets = Insets.min(transformation.getInsets(), Insets.NONE);
if (!extensionInsets.equals(Insets.NONE) && clipRect != null && !clipRect.isEmpty()) {
// Clip out any overflowing edge extension
clipRect.inset(extensionInsets);
t.setCrop(leash, clipRect);
}
if (anim.hasRoundedCorners() && cornerRadius > 0 && clipRect != null) {
// We can only apply rounded corner if a crop is set
t.setWindowCrop(leash, clipRect);
t.setCrop(leash, clipRect);
t.setCornerRadius(leash, cornerRadius);
}