Merge changes I1ea02fa9,I033a79dc,Iac219a8e into sc-v2-dev am: c3acdff9e8 am: dc696b0e6c
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/14660736 Change-Id: I0e6d974c6a521a53026c720af88c1e231c83dbf1
This commit is contained in:
@@ -16,6 +16,12 @@
|
||||
|
||||
package android.window;
|
||||
|
||||
import static android.app.ActivityOptions.ANIM_CLIP_REVEAL;
|
||||
import static android.app.ActivityOptions.ANIM_CUSTOM;
|
||||
import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS;
|
||||
import static android.app.ActivityOptions.ANIM_SCALE_UP;
|
||||
import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN;
|
||||
import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP;
|
||||
import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
|
||||
import static android.view.WindowManager.TRANSIT_CHANGE;
|
||||
import static android.view.WindowManager.TRANSIT_CLOSE;
|
||||
@@ -31,6 +37,7 @@ import android.annotation.Nullable;
|
||||
import android.app.ActivityManager;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.hardware.HardwareBuffer;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.view.Surface;
|
||||
@@ -102,6 +109,8 @@ public final class TransitionInfo implements Parcelable {
|
||||
private SurfaceControl mRootLeash;
|
||||
private final Point mRootOffset = new Point();
|
||||
|
||||
private AnimationOptions mOptions;
|
||||
|
||||
/** @hide */
|
||||
public TransitionInfo(@WindowManager.TransitionOldType int type,
|
||||
@WindowManager.TransitionFlags int flags) {
|
||||
@@ -116,6 +125,7 @@ public final class TransitionInfo implements Parcelable {
|
||||
mRootLeash = new SurfaceControl();
|
||||
mRootLeash.readFromParcel(in);
|
||||
mRootOffset.readFromParcel(in);
|
||||
mOptions = in.readTypedObject(AnimationOptions.CREATOR);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -126,6 +136,7 @@ public final class TransitionInfo implements Parcelable {
|
||||
dest.writeList(mChanges);
|
||||
mRootLeash.writeToParcel(dest, flags);
|
||||
mRootOffset.writeToParcel(dest, flags);
|
||||
dest.writeTypedObject(mOptions, flags);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@@ -154,6 +165,10 @@ public final class TransitionInfo implements Parcelable {
|
||||
mRootOffset.set(offsetLeft, offsetTop);
|
||||
}
|
||||
|
||||
public void setAnimationOptions(AnimationOptions options) {
|
||||
mOptions = options;
|
||||
}
|
||||
|
||||
public int getType() {
|
||||
return mType;
|
||||
}
|
||||
@@ -182,6 +197,10 @@ public final class TransitionInfo implements Parcelable {
|
||||
return mRootOffset;
|
||||
}
|
||||
|
||||
public AnimationOptions getAnimationOptions() {
|
||||
return mOptions;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public List<Change> getChanges() {
|
||||
return mChanges;
|
||||
@@ -484,4 +503,146 @@ public final class TransitionInfo implements Parcelable {
|
||||
+ mStartRotation + "->" + mEndRotation + "}";
|
||||
}
|
||||
}
|
||||
|
||||
/** Represents animation options during a transition */
|
||||
public static final class AnimationOptions implements Parcelable {
|
||||
|
||||
private int mType;
|
||||
private int mEnterResId;
|
||||
private int mExitResId;
|
||||
private boolean mOverrideTaskTransition;
|
||||
private String mPackageName;
|
||||
private final Rect mTransitionBounds = new Rect();
|
||||
private HardwareBuffer mThumbnail;
|
||||
|
||||
private AnimationOptions(int type) {
|
||||
mType = type;
|
||||
}
|
||||
|
||||
public AnimationOptions(Parcel in) {
|
||||
mType = in.readInt();
|
||||
mEnterResId = in.readInt();
|
||||
mExitResId = in.readInt();
|
||||
mOverrideTaskTransition = in.readBoolean();
|
||||
mPackageName = in.readString();
|
||||
mTransitionBounds.readFromParcel(in);
|
||||
mThumbnail = in.readTypedObject(HardwareBuffer.CREATOR);
|
||||
}
|
||||
|
||||
public static AnimationOptions makeCustomAnimOptions(String packageName, int enterResId,
|
||||
int exitResId, boolean overrideTaskTransition) {
|
||||
AnimationOptions options = new AnimationOptions(ANIM_CUSTOM);
|
||||
options.mPackageName = packageName;
|
||||
options.mEnterResId = enterResId;
|
||||
options.mExitResId = exitResId;
|
||||
options.mOverrideTaskTransition = overrideTaskTransition;
|
||||
return options;
|
||||
}
|
||||
|
||||
public static AnimationOptions makeClipRevealAnimOptions(int startX, int startY, int width,
|
||||
int height) {
|
||||
AnimationOptions options = new AnimationOptions(ANIM_CLIP_REVEAL);
|
||||
options.mTransitionBounds.set(startX, startY, startX + width, startY + height);
|
||||
return options;
|
||||
}
|
||||
|
||||
public static AnimationOptions makeScaleUpAnimOptions(int startX, int startY, int width,
|
||||
int height) {
|
||||
AnimationOptions options = new AnimationOptions(ANIM_SCALE_UP);
|
||||
options.mTransitionBounds.set(startX, startY, startX + width, startY + height);
|
||||
return options;
|
||||
}
|
||||
|
||||
public static AnimationOptions makeThumnbnailAnimOptions(HardwareBuffer srcThumb,
|
||||
int startX, int startY, boolean scaleUp) {
|
||||
AnimationOptions options = new AnimationOptions(
|
||||
scaleUp ? ANIM_THUMBNAIL_SCALE_UP : ANIM_THUMBNAIL_SCALE_DOWN);
|
||||
options.mTransitionBounds.set(startX, startY, startX, startY);
|
||||
options.mThumbnail = srcThumb;
|
||||
return options;
|
||||
}
|
||||
|
||||
public static AnimationOptions makeCrossProfileAnimOptions() {
|
||||
AnimationOptions options = new AnimationOptions(ANIM_OPEN_CROSS_PROFILE_APPS);
|
||||
return options;
|
||||
}
|
||||
|
||||
public int getType() {
|
||||
return mType;
|
||||
}
|
||||
|
||||
public int getEnterResId() {
|
||||
return mEnterResId;
|
||||
}
|
||||
|
||||
public int getExitResId() {
|
||||
return mExitResId;
|
||||
}
|
||||
|
||||
public boolean getOverrideTaskTransition() {
|
||||
return mOverrideTaskTransition;
|
||||
}
|
||||
|
||||
public String getPackageName() {
|
||||
return mPackageName;
|
||||
}
|
||||
|
||||
public Rect getTransitionBounds() {
|
||||
return mTransitionBounds;
|
||||
}
|
||||
|
||||
public HardwareBuffer getThumbnail() {
|
||||
return mThumbnail;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeInt(mType);
|
||||
dest.writeInt(mEnterResId);
|
||||
dest.writeInt(mExitResId);
|
||||
dest.writeBoolean(mOverrideTaskTransition);
|
||||
dest.writeString(mPackageName);
|
||||
mTransitionBounds.writeToParcel(dest, flags);
|
||||
dest.writeTypedObject(mThumbnail, flags);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static final Creator<AnimationOptions> CREATOR =
|
||||
new Creator<AnimationOptions>() {
|
||||
@Override
|
||||
public AnimationOptions createFromParcel(Parcel in) {
|
||||
return new AnimationOptions(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnimationOptions[] newArray(int size) {
|
||||
return new AnimationOptions[size];
|
||||
}
|
||||
};
|
||||
|
||||
/** @hide */
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static String typeToString(int mode) {
|
||||
switch(mode) {
|
||||
case ANIM_CUSTOM: return "ANIM_CUSTOM";
|
||||
case ANIM_CLIP_REVEAL: return "ANIM_CLIP_REVEAL";
|
||||
case ANIM_SCALE_UP: return "ANIM_SCALE_UP";
|
||||
case ANIM_THUMBNAIL_SCALE_UP: return "ANIM_THUMBNAIL_SCALE_UP";
|
||||
case ANIM_THUMBNAIL_SCALE_DOWN: return "ANIM_THUMBNAIL_SCALE_DOWN";
|
||||
case ANIM_OPEN_CROSS_PROFILE_APPS: return "ANIM_OPEN_CROSS_PROFILE_APPS";
|
||||
default: return "<unknown:" + mode + ">";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "{ AnimationOtions type= " + typeToString(mType) + " package=" + mPackageName
|
||||
+ " override=" + mOverrideTaskTransition + " b=" + mTransitionBounds + "}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,16 +16,20 @@
|
||||
|
||||
package com.android.internal.policy;
|
||||
|
||||
import static android.view.WindowManager.TRANSIT_CLOSE;
|
||||
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
|
||||
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION;
|
||||
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
|
||||
import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE;
|
||||
import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
|
||||
import static android.view.WindowManager.TRANSIT_OLD_NONE;
|
||||
import static android.view.WindowManager.TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE;
|
||||
import static android.view.WindowManager.TRANSIT_OLD_TRANSLUCENT_ACTIVITY_OPEN;
|
||||
import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_INTRA_CLOSE;
|
||||
import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_INTRA_OPEN;
|
||||
import static android.view.WindowManager.TRANSIT_OPEN;
|
||||
|
||||
import android.annotation.DrawableRes;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.ActivityManager;
|
||||
@@ -34,12 +38,18 @@ import android.content.res.Configuration;
|
||||
import android.content.res.ResourceId;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Picture;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.hardware.HardwareBuffer;
|
||||
import android.os.SystemProperties;
|
||||
import android.util.Slog;
|
||||
import android.view.WindowManager.LayoutParams;
|
||||
import android.view.WindowManager.TransitionOldType;
|
||||
import android.view.WindowManager.TransitionType;
|
||||
import android.view.animation.AlphaAnimation;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.AnimationSet;
|
||||
@@ -56,11 +66,17 @@ import java.util.List;
|
||||
|
||||
/** @hide */
|
||||
public class TransitionAnimation {
|
||||
public static final int WALLPAPER_TRANSITION_NONE = 0;
|
||||
public static final int WALLPAPER_TRANSITION_OPEN = 1;
|
||||
public static final int WALLPAPER_TRANSITION_CLOSE = 2;
|
||||
public static final int WALLPAPER_TRANSITION_INTRA_OPEN = 3;
|
||||
public static final int WALLPAPER_TRANSITION_INTRA_CLOSE = 4;
|
||||
|
||||
// These are the possible states for the enter/exit activities during a thumbnail transition
|
||||
public static final int THUMBNAIL_TRANSITION_ENTER_SCALE_UP = 0;
|
||||
public static final int THUMBNAIL_TRANSITION_EXIT_SCALE_UP = 1;
|
||||
public static final int THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN = 2;
|
||||
public static final int THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN = 3;
|
||||
private static final int THUMBNAIL_TRANSITION_ENTER_SCALE_UP = 0;
|
||||
private static final int THUMBNAIL_TRANSITION_EXIT_SCALE_UP = 1;
|
||||
private static final int THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN = 2;
|
||||
private static final int THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN = 3;
|
||||
|
||||
/**
|
||||
* Maximum duration for the clip reveal animation. This is used when there is a lot of movement
|
||||
@@ -72,9 +88,15 @@ public class TransitionAnimation {
|
||||
|
||||
public static final int DEFAULT_APP_TRANSITION_DURATION = 336;
|
||||
|
||||
/** Fraction of animation at which the recents thumbnail stays completely transparent */
|
||||
private static final float RECENTS_THUMBNAIL_FADEIN_FRACTION = 0.5f;
|
||||
/** Fraction of animation at which the recents thumbnail becomes completely transparent */
|
||||
private static final float RECENTS_THUMBNAIL_FADEOUT_FRACTION = 0.5f;
|
||||
|
||||
/** Interpolator to be used for animations that respond directly to a touch */
|
||||
static final Interpolator TOUCH_RESPONSE_INTERPOLATOR =
|
||||
new PathInterpolator(0.3f, 0f, 0.1f, 1f);
|
||||
|
||||
private static final String DEFAULT_PACKAGE = "android";
|
||||
|
||||
private final Context mContext;
|
||||
@@ -86,7 +108,9 @@ public class TransitionAnimation {
|
||||
new PathInterpolator(0.3f, 0f, 0.1f, 1f);
|
||||
private final Interpolator mClipHorizontalInterpolator = new PathInterpolator(0, 0, 0.4f, 1f);
|
||||
private final Interpolator mDecelerateInterpolator;
|
||||
private final Interpolator mFastOutLinearInInterpolator;
|
||||
private final Interpolator mLinearOutSlowInInterpolator;
|
||||
private final Interpolator mThumbnailFadeInInterpolator;
|
||||
private final Interpolator mThumbnailFadeOutInterpolator;
|
||||
private final Rect mTmpFromClipRect = new Rect();
|
||||
private final Rect mTmpToClipRect = new Rect();
|
||||
@@ -107,8 +131,19 @@ public class TransitionAnimation {
|
||||
|
||||
mDecelerateInterpolator = AnimationUtils.loadInterpolator(context,
|
||||
com.android.internal.R.interpolator.decelerate_cubic);
|
||||
mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
|
||||
com.android.internal.R.interpolator.fast_out_linear_in);
|
||||
mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
|
||||
com.android.internal.R.interpolator.linear_out_slow_in);
|
||||
mThumbnailFadeInInterpolator = input -> {
|
||||
// Linear response for first fraction, then complete after that.
|
||||
if (input < RECENTS_THUMBNAIL_FADEIN_FRACTION) {
|
||||
return 0f;
|
||||
}
|
||||
float t = (input - RECENTS_THUMBNAIL_FADEIN_FRACTION)
|
||||
/ (1f - RECENTS_THUMBNAIL_FADEIN_FRACTION);
|
||||
return mFastOutLinearInInterpolator.getInterpolation(t);
|
||||
};
|
||||
mThumbnailFadeOutInterpolator = input -> {
|
||||
// Linear response for first fraction, then complete after that.
|
||||
if (input < RECENTS_THUMBNAIL_FADEOUT_FRACTION) {
|
||||
@@ -181,6 +216,13 @@ public class TransitionAnimation {
|
||||
DEFAULT_PACKAGE, com.android.internal.R.anim.cross_profile_apps_thumbnail_enter);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Animation createCrossProfileAppsThumbnailAnimationLocked(Rect appRect) {
|
||||
final Animation animation = loadCrossProfileAppThumbnailEnterAnimation();
|
||||
return prepareThumbnailAnimationWithDuration(animation, appRect.width(),
|
||||
appRect.height(), 0, null);
|
||||
}
|
||||
|
||||
/** Load animation by resource Id from specific package. */
|
||||
@Nullable
|
||||
public Animation loadAnimationRes(String packageName, int resId) {
|
||||
@@ -347,8 +389,15 @@ public class TransitionAnimation {
|
||||
}
|
||||
}
|
||||
|
||||
public Animation createClipRevealAnimationLocked(int transit, boolean enter, Rect appFrame,
|
||||
Rect displayFrame, Rect startRect) {
|
||||
public Animation createClipRevealAnimationLocked(@TransitionType int transit,
|
||||
int wallpaperTransit, boolean enter, Rect appFrame, Rect displayFrame, Rect startRect) {
|
||||
return createClipRevealAnimationLockedCompat(
|
||||
getTransitCompatType(transit, wallpaperTransit), enter, appFrame, displayFrame,
|
||||
startRect);
|
||||
}
|
||||
|
||||
public Animation createClipRevealAnimationLockedCompat(@TransitionOldType int transit,
|
||||
boolean enter, Rect appFrame, Rect displayFrame, Rect startRect) {
|
||||
final Animation anim;
|
||||
if (enter) {
|
||||
final int appWidth = appFrame.width();
|
||||
@@ -458,8 +507,14 @@ public class TransitionAnimation {
|
||||
return anim;
|
||||
}
|
||||
|
||||
public Animation createScaleUpAnimationLocked(int transit, boolean enter,
|
||||
Rect containingFrame, Rect startRect) {
|
||||
public Animation createScaleUpAnimationLocked(@TransitionType int transit, int wallpaperTransit,
|
||||
boolean enter, Rect containingFrame, Rect startRect) {
|
||||
return createScaleUpAnimationLockedCompat(getTransitCompatType(transit, wallpaperTransit),
|
||||
enter, containingFrame, startRect);
|
||||
}
|
||||
|
||||
public Animation createScaleUpAnimationLockedCompat(@TransitionOldType int transit,
|
||||
boolean enter, Rect containingFrame, Rect startRect) {
|
||||
Animation a;
|
||||
setupDefaultNextAppTransitionStartRect(startRect, mTmpRect);
|
||||
final int appWidth = containingFrame.width();
|
||||
@@ -514,12 +569,19 @@ public class TransitionAnimation {
|
||||
return a;
|
||||
}
|
||||
|
||||
public Animation createThumbnailEnterExitAnimationLocked(boolean enter, boolean scaleUp,
|
||||
Rect containingFrame, @TransitionType int transit, int wallpaperTransit,
|
||||
HardwareBuffer thumbnailHeader, Rect startRect) {
|
||||
return createThumbnailEnterExitAnimationLockedCompat(enter, scaleUp, containingFrame,
|
||||
getTransitCompatType(transit, wallpaperTransit), thumbnailHeader, startRect);
|
||||
}
|
||||
|
||||
/**
|
||||
* This animation is created when we are doing a thumbnail transition, for the activity that is
|
||||
* leaving, and the activity that is entering.
|
||||
*/
|
||||
public Animation createThumbnailEnterExitAnimationLocked(int thumbTransitState,
|
||||
Rect containingFrame, int transit, HardwareBuffer thumbnailHeader,
|
||||
public Animation createThumbnailEnterExitAnimationLockedCompat(boolean enter, boolean scaleUp,
|
||||
Rect containingFrame, @TransitionOldType int transit, HardwareBuffer thumbnailHeader,
|
||||
Rect startRect) {
|
||||
final int appWidth = containingFrame.width();
|
||||
final int appHeight = containingFrame.height();
|
||||
@@ -529,6 +591,7 @@ public class TransitionAnimation {
|
||||
final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
|
||||
final int thumbHeightI = thumbnailHeader != null ? thumbnailHeader.getHeight() : appHeight;
|
||||
final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
|
||||
final int thumbTransitState = getThumbnailTransitionState(enter, scaleUp);
|
||||
|
||||
switch (thumbTransitState) {
|
||||
case THUMBNAIL_TRANSITION_ENTER_SCALE_UP: {
|
||||
@@ -587,8 +650,8 @@ public class TransitionAnimation {
|
||||
* This alternate animation is created when we are doing a thumbnail transition, for the
|
||||
* activity that is leaving, and the activity that is entering.
|
||||
*/
|
||||
public Animation createAspectScaledThumbnailEnterExitAnimationLocked(int thumbTransitState,
|
||||
int orientation, int transit, Rect containingFrame, Rect contentInsets,
|
||||
public Animation createAspectScaledThumbnailEnterExitAnimationLocked(boolean enter,
|
||||
boolean scaleUp, int orientation, int transit, Rect containingFrame, Rect contentInsets,
|
||||
@Nullable Rect surfaceInsets, @Nullable Rect stableInsets, boolean freeform,
|
||||
Rect startRect, Rect defaultStartRect) {
|
||||
Animation a;
|
||||
@@ -601,11 +664,11 @@ public class TransitionAnimation {
|
||||
final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
|
||||
final int thumbStartX = mTmpRect.left - containingFrame.left - contentInsets.left;
|
||||
final int thumbStartY = mTmpRect.top - containingFrame.top;
|
||||
final int thumbTransitState = getThumbnailTransitionState(enter, scaleUp);
|
||||
|
||||
switch (thumbTransitState) {
|
||||
case THUMBNAIL_TRANSITION_ENTER_SCALE_UP:
|
||||
case THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN: {
|
||||
final boolean scaleUp = thumbTransitState == THUMBNAIL_TRANSITION_ENTER_SCALE_UP;
|
||||
if (freeform && scaleUp) {
|
||||
a = createAspectScaledThumbnailEnterFreeformAnimationLocked(
|
||||
containingFrame, surfaceInsets, startRect, defaultStartRect);
|
||||
@@ -719,11 +782,152 @@ public class TransitionAnimation {
|
||||
THUMBNAIL_APP_TRANSITION_DURATION, mTouchResponseInterpolator);
|
||||
}
|
||||
|
||||
/**
|
||||
* This animation runs for the thumbnail that gets cross faded with the enter/exit activity
|
||||
* when a thumbnail is specified with the pending animation override.
|
||||
*/
|
||||
public Animation createThumbnailAspectScaleAnimationLocked(Rect appRect,
|
||||
@Nullable Rect contentInsets, HardwareBuffer thumbnailHeader, int orientation,
|
||||
Rect startRect, Rect defaultStartRect, boolean scaleUp) {
|
||||
Animation a;
|
||||
final int thumbWidthI = thumbnailHeader.getWidth();
|
||||
final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
|
||||
final int thumbHeightI = thumbnailHeader.getHeight();
|
||||
final int appWidth = appRect.width();
|
||||
|
||||
float scaleW = appWidth / thumbWidth;
|
||||
getNextAppTransitionStartRect(startRect, defaultStartRect, mTmpRect);
|
||||
final float fromX;
|
||||
float fromY;
|
||||
final float toX;
|
||||
float toY;
|
||||
final float pivotX;
|
||||
final float pivotY;
|
||||
if (shouldScaleDownThumbnailTransition(orientation)) {
|
||||
fromX = mTmpRect.left;
|
||||
fromY = mTmpRect.top;
|
||||
|
||||
// For the curved translate animation to work, the pivot points needs to be at the
|
||||
// same absolute position as the one from the real surface.
|
||||
toX = mTmpRect.width() / 2 * (scaleW - 1f) + appRect.left;
|
||||
toY = appRect.height() / 2 * (1 - 1 / scaleW) + appRect.top;
|
||||
pivotX = mTmpRect.width() / 2;
|
||||
pivotY = appRect.height() / 2 / scaleW;
|
||||
if (mGridLayoutRecentsEnabled) {
|
||||
// In the grid layout, the header is displayed above the thumbnail instead of
|
||||
// overlapping it.
|
||||
fromY -= thumbHeightI;
|
||||
toY -= thumbHeightI * scaleW;
|
||||
}
|
||||
} else {
|
||||
pivotX = 0;
|
||||
pivotY = 0;
|
||||
fromX = mTmpRect.left;
|
||||
fromY = mTmpRect.top;
|
||||
toX = appRect.left;
|
||||
toY = appRect.top;
|
||||
}
|
||||
if (scaleUp) {
|
||||
// Animation up from the thumbnail to the full screen
|
||||
Animation scale = new ScaleAnimation(1f, scaleW, 1f, scaleW, pivotX, pivotY);
|
||||
scale.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
|
||||
scale.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
|
||||
Animation alpha = new AlphaAnimation(1f, 0f);
|
||||
alpha.setInterpolator(mThumbnailFadeOutInterpolator);
|
||||
alpha.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
|
||||
Animation translate = createCurvedMotion(fromX, toX, fromY, toY);
|
||||
translate.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
|
||||
translate.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
|
||||
|
||||
mTmpFromClipRect.set(0, 0, thumbWidthI, thumbHeightI);
|
||||
mTmpToClipRect.set(appRect);
|
||||
|
||||
// Containing frame is in screen space, but we need the clip rect in the
|
||||
// app space.
|
||||
mTmpToClipRect.offsetTo(0, 0);
|
||||
mTmpToClipRect.right = (int) (mTmpToClipRect.right / scaleW);
|
||||
mTmpToClipRect.bottom = (int) (mTmpToClipRect.bottom / scaleW);
|
||||
|
||||
if (contentInsets != null) {
|
||||
mTmpToClipRect.inset((int) (-contentInsets.left * scaleW),
|
||||
(int) (-contentInsets.top * scaleW),
|
||||
(int) (-contentInsets.right * scaleW),
|
||||
(int) (-contentInsets.bottom * scaleW));
|
||||
}
|
||||
|
||||
Animation clipAnim = new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect);
|
||||
clipAnim.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
|
||||
clipAnim.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
|
||||
|
||||
// This AnimationSet uses the Interpolators assigned above.
|
||||
AnimationSet set = new AnimationSet(false);
|
||||
set.addAnimation(scale);
|
||||
if (!mGridLayoutRecentsEnabled) {
|
||||
// In the grid layout, the header should be shown for the whole animation.
|
||||
set.addAnimation(alpha);
|
||||
}
|
||||
set.addAnimation(translate);
|
||||
set.addAnimation(clipAnim);
|
||||
a = set;
|
||||
} else {
|
||||
// Animation down from the full screen to the thumbnail
|
||||
Animation scale = new ScaleAnimation(scaleW, 1f, scaleW, 1f, pivotX, pivotY);
|
||||
scale.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
|
||||
scale.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
|
||||
Animation alpha = new AlphaAnimation(0f, 1f);
|
||||
alpha.setInterpolator(mThumbnailFadeInInterpolator);
|
||||
alpha.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
|
||||
Animation translate = createCurvedMotion(toX, fromX, toY, fromY);
|
||||
translate.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
|
||||
translate.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
|
||||
|
||||
// This AnimationSet uses the Interpolators assigned above.
|
||||
AnimationSet set = new AnimationSet(false);
|
||||
set.addAnimation(scale);
|
||||
if (!mGridLayoutRecentsEnabled) {
|
||||
// In the grid layout, the header should be shown for the whole animation.
|
||||
set.addAnimation(alpha);
|
||||
}
|
||||
set.addAnimation(translate);
|
||||
a = set;
|
||||
|
||||
}
|
||||
return prepareThumbnailAnimationWithDuration(a, appWidth, appRect.height(), 0,
|
||||
null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an overlay with a background color and a thumbnail for the cross profile apps
|
||||
* animation.
|
||||
*/
|
||||
public HardwareBuffer createCrossProfileAppsThumbnail(
|
||||
@DrawableRes int thumbnailDrawableRes, Rect frame) {
|
||||
final int width = frame.width();
|
||||
final int height = frame.height();
|
||||
|
||||
final Picture picture = new Picture();
|
||||
final Canvas canvas = picture.beginRecording(width, height);
|
||||
canvas.drawColor(Color.argb(0.6f, 0, 0, 0));
|
||||
final int thumbnailSize = mContext.getResources().getDimensionPixelSize(
|
||||
com.android.internal.R.dimen.cross_profile_apps_thumbnail_size);
|
||||
final Drawable drawable = mContext.getDrawable(thumbnailDrawableRes);
|
||||
drawable.setBounds(
|
||||
(width - thumbnailSize) / 2,
|
||||
(height - thumbnailSize) / 2,
|
||||
(width + thumbnailSize) / 2,
|
||||
(height + thumbnailSize) / 2);
|
||||
drawable.setTint(mContext.getColor(android.R.color.white));
|
||||
drawable.draw(canvas);
|
||||
picture.endRecording();
|
||||
|
||||
return Bitmap.createBitmap(picture).getHardwareBuffer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the specified animation with a standard duration, interpolator, etc.
|
||||
*/
|
||||
private Animation prepareThumbnailAnimation(Animation a, int appWidth, int appHeight,
|
||||
int transit) {
|
||||
@TransitionOldType int transit) {
|
||||
// Pick the desired duration. If this is an inter-activity transition,
|
||||
// it is the standard duration for that. Otherwise we use the longer
|
||||
// task transition duration.
|
||||
@@ -820,6 +1024,22 @@ public class TransitionAnimation {
|
||||
return anim;
|
||||
}
|
||||
|
||||
private static @TransitionOldType int getTransitCompatType(@TransitionType int transit,
|
||||
int wallpaperTransit) {
|
||||
if (wallpaperTransit == WALLPAPER_TRANSITION_INTRA_OPEN) {
|
||||
return TRANSIT_OLD_WALLPAPER_INTRA_OPEN;
|
||||
} else if (wallpaperTransit == WALLPAPER_TRANSITION_INTRA_CLOSE) {
|
||||
return TRANSIT_OLD_WALLPAPER_INTRA_CLOSE;
|
||||
} else if (transit == TRANSIT_OPEN) {
|
||||
return TRANSIT_OLD_ACTIVITY_OPEN;
|
||||
} else if (transit == TRANSIT_CLOSE) {
|
||||
return TRANSIT_OLD_ACTIVITY_CLOSE;
|
||||
}
|
||||
|
||||
// We only do some special handle for above type, so use type NONE for default behavior.
|
||||
return TRANSIT_OLD_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the duration for the clip reveal animation. If the clip is "cut off", meaning that
|
||||
* the start rect is outside of the target rect, and there is a lot of movement going on.
|
||||
@@ -842,11 +1062,34 @@ public class TransitionAnimation {
|
||||
* (MAX_CLIP_REVEAL_TRANSITION_DURATION - DEFAULT_APP_TRANSITION_DURATION));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current thumbnail transition state.
|
||||
*/
|
||||
private int getThumbnailTransitionState(boolean enter, boolean scaleUp) {
|
||||
if (enter) {
|
||||
if (scaleUp) {
|
||||
return THUMBNAIL_TRANSITION_ENTER_SCALE_UP;
|
||||
} else {
|
||||
return THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN;
|
||||
}
|
||||
} else {
|
||||
if (scaleUp) {
|
||||
return THUMBNAIL_TRANSITION_EXIT_SCALE_UP;
|
||||
} else {
|
||||
return THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the specified animation with a standard duration, interpolator, etc.
|
||||
*/
|
||||
private static Animation prepareThumbnailAnimationWithDuration(Animation a, int appWidth,
|
||||
public static Animation prepareThumbnailAnimationWithDuration(Animation a, int appWidth,
|
||||
int appHeight, long duration, Interpolator interpolator) {
|
||||
if (a == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (duration > 0) {
|
||||
a.setDuration(duration);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,13 @@
|
||||
|
||||
package com.android.wm.shell.transition;
|
||||
|
||||
import static android.app.ActivityOptions.ANIM_CLIP_REVEAL;
|
||||
import static android.app.ActivityOptions.ANIM_CUSTOM;
|
||||
import static android.app.ActivityOptions.ANIM_NONE;
|
||||
import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS;
|
||||
import static android.app.ActivityOptions.ANIM_SCALE_UP;
|
||||
import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN;
|
||||
import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP;
|
||||
import static android.view.WindowManager.TRANSIT_CHANGE;
|
||||
import static android.view.WindowManager.TRANSIT_CLOSE;
|
||||
import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
|
||||
@@ -29,17 +36,28 @@ import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER;
|
||||
import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
|
||||
import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
|
||||
|
||||
import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_CLOSE;
|
||||
import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_INTRA_CLOSE;
|
||||
import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_INTRA_OPEN;
|
||||
import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_NONE;
|
||||
import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_OPEN;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.hardware.HardwareBuffer;
|
||||
import android.os.IBinder;
|
||||
import android.os.SystemProperties;
|
||||
import android.os.UserHandle;
|
||||
import android.util.ArrayMap;
|
||||
import android.view.Choreographer;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.SurfaceSession;
|
||||
import android.view.animation.AlphaAnimation;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.Transformation;
|
||||
@@ -61,23 +79,45 @@ import java.util.ArrayList;
|
||||
public class DefaultTransitionHandler implements Transitions.TransitionHandler {
|
||||
private static final int MAX_ANIMATION_DURATION = 3000;
|
||||
|
||||
/**
|
||||
* Restrict ability of activities overriding transition animation in a way such that
|
||||
* an activity can do it only when the transition happens within a same task.
|
||||
*
|
||||
* @see android.app.Activity#overridePendingTransition(int, int)
|
||||
*/
|
||||
private static final String DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY =
|
||||
"persist.wm.disable_custom_task_animation";
|
||||
|
||||
/**
|
||||
* @see #DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY
|
||||
*/
|
||||
static boolean sDisableCustomTaskAnimationProperty =
|
||||
SystemProperties.getBoolean(DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY, true);
|
||||
|
||||
private final TransactionPool mTransactionPool;
|
||||
private final Context mContext;
|
||||
private final ShellExecutor mMainExecutor;
|
||||
private final ShellExecutor mAnimExecutor;
|
||||
private final TransitionAnimation mTransitionAnimation;
|
||||
|
||||
private final SurfaceSession mSurfaceSession = new SurfaceSession();
|
||||
|
||||
/** Keeps track of the currently-running animations associated with each transition. */
|
||||
private final ArrayMap<IBinder, ArrayList<Animator>> mAnimations = new ArrayMap<>();
|
||||
|
||||
private final Rect mInsets = new Rect(0, 0, 0, 0);
|
||||
private float mTransitionAnimationScaleSetting = 1.0f;
|
||||
|
||||
private final int mCurrentUserId;
|
||||
|
||||
DefaultTransitionHandler(@NonNull TransactionPool transactionPool, Context context,
|
||||
@NonNull ShellExecutor mainExecutor, @NonNull ShellExecutor animExecutor) {
|
||||
mTransactionPool = transactionPool;
|
||||
mContext = context;
|
||||
mMainExecutor = mainExecutor;
|
||||
mAnimExecutor = animExecutor;
|
||||
mTransitionAnimation = new TransitionAnimation(context, false /* debug */, Transitions.TAG);
|
||||
mCurrentUserId = UserHandle.myUserId();
|
||||
|
||||
AttributeCache.init(context);
|
||||
}
|
||||
@@ -101,6 +141,8 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
|
||||
mAnimations.remove(transition);
|
||||
finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
|
||||
};
|
||||
|
||||
final int wallpaperTransit = getWallpaperTransitType(info);
|
||||
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
|
||||
final TransitionInfo.Change change = info.getChanges().get(i);
|
||||
if (change.getMode() == TRANSIT_CHANGE) {
|
||||
@@ -118,9 +160,14 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
|
||||
// Don't animate anything that isn't independent.
|
||||
if (!TransitionInfo.isIndependent(change, info)) continue;
|
||||
|
||||
Animation a = loadAnimation(info.getType(), info.getFlags(), change);
|
||||
Animation a = loadAnimation(info, change, wallpaperTransit);
|
||||
if (a != null) {
|
||||
startAnimInternal(animations, a, change.getLeash(), onAnimFinish);
|
||||
startAnimInternal(animations, a, change.getLeash(), onAnimFinish,
|
||||
null /* position */);
|
||||
|
||||
if (info.getAnimationOptions() != null) {
|
||||
attachThumbnail(animations, onAnimFinish, change, info.getAnimationOptions());
|
||||
}
|
||||
}
|
||||
}
|
||||
startTransaction.apply();
|
||||
@@ -142,68 +189,111 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Animation loadAnimation(int type, int flags, TransitionInfo.Change change) {
|
||||
// TODO(b/178678389): It should handle more type animation here
|
||||
private Animation loadAnimation(TransitionInfo info, TransitionInfo.Change change,
|
||||
int wallpaperTransit) {
|
||||
Animation a = null;
|
||||
|
||||
final boolean isOpening = Transitions.isOpeningType(type);
|
||||
final int type = info.getType();
|
||||
final int flags = info.getFlags();
|
||||
final int changeMode = change.getMode();
|
||||
final int changeFlags = change.getFlags();
|
||||
final boolean isOpeningType = Transitions.isOpeningType(type);
|
||||
final boolean enter = Transitions.isOpeningType(changeMode);
|
||||
final boolean isTask = change.getTaskInfo() != null;
|
||||
final TransitionInfo.AnimationOptions options = info.getAnimationOptions();
|
||||
final int overrideType = options != null ? options.getType() : ANIM_NONE;
|
||||
final boolean canCustomContainer = isTask ? !sDisableCustomTaskAnimationProperty : true;
|
||||
|
||||
if (type == TRANSIT_RELAUNCH) {
|
||||
a = mTransitionAnimation.createRelaunchAnimation(
|
||||
change.getStartAbsBounds(), mInsets, change.getEndAbsBounds());
|
||||
change.getEndAbsBounds(), mInsets, change.getEndAbsBounds());
|
||||
} else if (type == TRANSIT_KEYGUARD_GOING_AWAY) {
|
||||
a = mTransitionAnimation.loadKeyguardExitAnimation(flags,
|
||||
(changeFlags & FLAG_SHOW_WALLPAPER) != 0);
|
||||
} else if (type == TRANSIT_KEYGUARD_UNOCCLUDE) {
|
||||
a = mTransitionAnimation.loadKeyguardUnoccludeAnimation();
|
||||
} else if (changeMode == TRANSIT_OPEN && isOpening) {
|
||||
if ((changeFlags & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) {
|
||||
// This received a transferred starting window, so don't animate
|
||||
return null;
|
||||
}
|
||||
|
||||
if ((changeFlags & FLAG_IS_VOICE_INTERACTION) != 0) {
|
||||
a = mTransitionAnimation.loadVoiceActivityOpenAnimation(true /** enter */);
|
||||
} else if (change.getTaskInfo() != null) {
|
||||
a = mTransitionAnimation.loadDefaultAnimationAttr(
|
||||
R.styleable.WindowAnimation_taskOpenEnterAnimation);
|
||||
} else if (overrideType == ANIM_CUSTOM
|
||||
&& (canCustomContainer || options.getOverrideTaskTransition())) {
|
||||
a = mTransitionAnimation.loadAnimationRes(options.getPackageName(), enter
|
||||
? options.getEnterResId() : options.getExitResId());
|
||||
} else if (overrideType == ANIM_OPEN_CROSS_PROFILE_APPS && enter) {
|
||||
a = mTransitionAnimation.loadCrossProfileAppEnterAnimation();
|
||||
} else if (overrideType == ANIM_CLIP_REVEAL) {
|
||||
a = mTransitionAnimation.createClipRevealAnimationLocked(type, wallpaperTransit, enter,
|
||||
change.getEndAbsBounds(), change.getEndAbsBounds(),
|
||||
options.getTransitionBounds());
|
||||
} else if (overrideType == ANIM_SCALE_UP) {
|
||||
a = mTransitionAnimation.createScaleUpAnimationLocked(type, wallpaperTransit, enter,
|
||||
change.getEndAbsBounds(), options.getTransitionBounds());
|
||||
} else if (overrideType == ANIM_THUMBNAIL_SCALE_UP
|
||||
|| overrideType == ANIM_THUMBNAIL_SCALE_DOWN) {
|
||||
final boolean scaleUp = overrideType == ANIM_THUMBNAIL_SCALE_UP;
|
||||
a = mTransitionAnimation.createThumbnailEnterExitAnimationLocked(enter, scaleUp,
|
||||
change.getEndAbsBounds(), type, wallpaperTransit, options.getThumbnail(),
|
||||
options.getTransitionBounds());
|
||||
} else if (wallpaperTransit == WALLPAPER_TRANSITION_INTRA_OPEN) {
|
||||
a = mTransitionAnimation.loadDefaultAnimationAttr(enter
|
||||
? R.styleable.WindowAnimation_wallpaperIntraOpenEnterAnimation
|
||||
: R.styleable.WindowAnimation_wallpaperIntraOpenExitAnimation);
|
||||
} else if (wallpaperTransit == WALLPAPER_TRANSITION_INTRA_CLOSE) {
|
||||
a = mTransitionAnimation.loadDefaultAnimationAttr(enter
|
||||
? R.styleable.WindowAnimation_wallpaperIntraCloseEnterAnimation
|
||||
: R.styleable.WindowAnimation_wallpaperIntraCloseExitAnimation);
|
||||
} else if (wallpaperTransit == WALLPAPER_TRANSITION_OPEN) {
|
||||
a = mTransitionAnimation.loadDefaultAnimationAttr(enter
|
||||
? R.styleable.WindowAnimation_wallpaperOpenEnterAnimation
|
||||
: R.styleable.WindowAnimation_wallpaperOpenExitAnimation);
|
||||
} else if (wallpaperTransit == WALLPAPER_TRANSITION_CLOSE) {
|
||||
a = mTransitionAnimation.loadDefaultAnimationAttr(enter
|
||||
? R.styleable.WindowAnimation_wallpaperCloseEnterAnimation
|
||||
: R.styleable.WindowAnimation_wallpaperCloseExitAnimation);
|
||||
} else if ((changeFlags & FLAG_IS_VOICE_INTERACTION) != 0) {
|
||||
if (isOpeningType) {
|
||||
a = mTransitionAnimation.loadVoiceActivityOpenAnimation(enter);
|
||||
} else {
|
||||
a = mTransitionAnimation.loadDefaultAnimationRes(
|
||||
(changeFlags & FLAG_TRANSLUCENT) == 0
|
||||
? R.anim.activity_open_enter : R.anim.activity_translucent_open_enter);
|
||||
a = mTransitionAnimation.loadVoiceActivityExitAnimation(enter);
|
||||
}
|
||||
} else if (changeMode == TRANSIT_TO_FRONT && isOpening) {
|
||||
if ((changeFlags & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) {
|
||||
// This received a transferred starting window, so don't animate
|
||||
return null;
|
||||
}
|
||||
|
||||
if ((changeFlags & FLAG_IS_VOICE_INTERACTION) != 0) {
|
||||
a = mTransitionAnimation.loadVoiceActivityOpenAnimation(true /** enter */);
|
||||
} else if ((changeFlags & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0 && isOpeningType) {
|
||||
// This received a transferred starting window, so don't animate
|
||||
return null;
|
||||
} else if (type == TRANSIT_OPEN) {
|
||||
if (isTask) {
|
||||
a = mTransitionAnimation.loadDefaultAnimationAttr(enter
|
||||
? R.styleable.WindowAnimation_taskOpenEnterAnimation
|
||||
: R.styleable.WindowAnimation_taskOpenExitAnimation);
|
||||
} else {
|
||||
a = mTransitionAnimation.loadDefaultAnimationAttr(
|
||||
R.styleable.WindowAnimation_taskToFrontEnterAnimation);
|
||||
if ((changeFlags & FLAG_TRANSLUCENT) != 0 && enter) {
|
||||
a = mTransitionAnimation.loadDefaultAnimationRes(
|
||||
R.anim.activity_translucent_open_enter);
|
||||
} else {
|
||||
a = mTransitionAnimation.loadDefaultAnimationAttr(enter
|
||||
? R.styleable.WindowAnimation_activityOpenEnterAnimation
|
||||
: R.styleable.WindowAnimation_activityOpenExitAnimation);
|
||||
}
|
||||
}
|
||||
} else if (changeMode == TRANSIT_CLOSE && !isOpening) {
|
||||
if ((changeFlags & FLAG_IS_VOICE_INTERACTION) != 0) {
|
||||
a = mTransitionAnimation.loadVoiceActivityExitAnimation(false /** enter */);
|
||||
} else if (change.getTaskInfo() != null) {
|
||||
a = mTransitionAnimation.loadDefaultAnimationAttr(
|
||||
R.styleable.WindowAnimation_taskCloseExitAnimation);
|
||||
} else if (type == TRANSIT_TO_FRONT) {
|
||||
a = mTransitionAnimation.loadDefaultAnimationAttr(enter
|
||||
? R.styleable.WindowAnimation_taskToFrontEnterAnimation
|
||||
: R.styleable.WindowAnimation_taskToFrontExitAnimation);
|
||||
} else if (type == TRANSIT_CLOSE) {
|
||||
if (isTask) {
|
||||
a = mTransitionAnimation.loadDefaultAnimationAttr(enter
|
||||
? R.styleable.WindowAnimation_taskCloseEnterAnimation
|
||||
: R.styleable.WindowAnimation_taskCloseExitAnimation);
|
||||
} else {
|
||||
a = mTransitionAnimation.loadDefaultAnimationRes(
|
||||
(changeFlags & FLAG_TRANSLUCENT) == 0
|
||||
? R.anim.activity_close_exit : R.anim.activity_translucent_close_exit);
|
||||
}
|
||||
} else if (changeMode == TRANSIT_TO_BACK && !isOpening) {
|
||||
if ((changeFlags & FLAG_IS_VOICE_INTERACTION) != 0) {
|
||||
a = mTransitionAnimation.loadVoiceActivityExitAnimation(false /** enter */);
|
||||
} else {
|
||||
a = mTransitionAnimation.loadDefaultAnimationAttr(
|
||||
R.styleable.WindowAnimation_taskToBackExitAnimation);
|
||||
if ((changeFlags & FLAG_TRANSLUCENT) != 0 && !enter) {
|
||||
a = mTransitionAnimation.loadDefaultAnimationRes(
|
||||
R.anim.activity_translucent_close_exit);
|
||||
} else {
|
||||
a = mTransitionAnimation.loadDefaultAnimationAttr(enter
|
||||
? R.styleable.WindowAnimation_activityCloseEnterAnimation
|
||||
: R.styleable.WindowAnimation_activityCloseExitAnimation);
|
||||
}
|
||||
}
|
||||
} else if (type == TRANSIT_TO_BACK) {
|
||||
a = mTransitionAnimation.loadDefaultAnimationAttr(enter
|
||||
? R.styleable.WindowAnimation_taskToBackEnterAnimation
|
||||
: R.styleable.WindowAnimation_taskToBackExitAnimation);
|
||||
} else if (changeMode == TRANSIT_CHANGE) {
|
||||
// In the absence of a specific adapter, we just want to keep everything stationary.
|
||||
a = new AlphaAnimation(1.f, 1.f);
|
||||
@@ -211,17 +301,19 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
|
||||
}
|
||||
|
||||
if (a != null) {
|
||||
Rect start = change.getStartAbsBounds();
|
||||
Rect end = change.getEndAbsBounds();
|
||||
if (!a.isInitialized()) {
|
||||
Rect end = change.getEndAbsBounds();
|
||||
a.initialize(end.width(), end.height(), end.width(), end.height());
|
||||
}
|
||||
a.restrictDuration(MAX_ANIMATION_DURATION);
|
||||
a.initialize(end.width(), end.height(), start.width(), start.height());
|
||||
a.scaleCurrentDuration(mTransitionAnimationScaleSetting);
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
private void startAnimInternal(@NonNull ArrayList<Animator> animations, @NonNull Animation anim,
|
||||
@NonNull SurfaceControl leash, @NonNull Runnable finishCallback) {
|
||||
@NonNull SurfaceControl leash, @NonNull Runnable finishCallback,
|
||||
@Nullable Point position) {
|
||||
final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
|
||||
final ValueAnimator va = ValueAnimator.ofFloat(0f, 1f);
|
||||
final Transformation transformation = new Transformation();
|
||||
@@ -232,11 +324,13 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
|
||||
va.addUpdateListener(animation -> {
|
||||
final long currentPlayTime = Math.min(va.getDuration(), va.getCurrentPlayTime());
|
||||
|
||||
applyTransformation(currentPlayTime, transaction, leash, anim, transformation, matrix);
|
||||
applyTransformation(currentPlayTime, transaction, leash, anim, transformation, matrix,
|
||||
position);
|
||||
});
|
||||
|
||||
final Runnable finisher = () -> {
|
||||
applyTransformation(va.getDuration(), transaction, leash, anim, transformation, matrix);
|
||||
applyTransformation(va.getDuration(), transaction, leash, anim, transformation, matrix,
|
||||
position);
|
||||
|
||||
mTransactionPool.release(transaction);
|
||||
mMainExecutor.execute(() -> {
|
||||
@@ -259,9 +353,112 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
|
||||
mAnimExecutor.execute(va::start);
|
||||
}
|
||||
|
||||
private void attachThumbnail(@NonNull ArrayList<Animator> animations,
|
||||
@NonNull Runnable finishCallback, TransitionInfo.Change change,
|
||||
TransitionInfo.AnimationOptions options) {
|
||||
final boolean isTask = change.getTaskInfo() != null;
|
||||
final boolean isOpen = Transitions.isOpeningType(change.getMode());
|
||||
final boolean isClose = Transitions.isClosingType(change.getMode());
|
||||
if (isOpen) {
|
||||
if (options.getType() == ANIM_OPEN_CROSS_PROFILE_APPS && isTask) {
|
||||
attachCrossProfileThunmbnailAnimation(animations, finishCallback, change);
|
||||
} else if (options.getType() == ANIM_THUMBNAIL_SCALE_UP) {
|
||||
attachThumbnailAnimation(animations, finishCallback, change, options);
|
||||
}
|
||||
} else if (isClose && options.getType() == ANIM_THUMBNAIL_SCALE_DOWN) {
|
||||
attachThumbnailAnimation(animations, finishCallback, change, options);
|
||||
}
|
||||
}
|
||||
|
||||
private void attachCrossProfileThunmbnailAnimation(@NonNull ArrayList<Animator> animations,
|
||||
@NonNull Runnable finishCallback, TransitionInfo.Change change) {
|
||||
final int thumbnailDrawableRes = change.getTaskInfo().userId == mCurrentUserId
|
||||
? R.drawable.ic_account_circle : R.drawable.ic_corp_badge;
|
||||
final Rect bounds = change.getEndAbsBounds();
|
||||
final HardwareBuffer thumbnail = mTransitionAnimation.createCrossProfileAppsThumbnail(
|
||||
thumbnailDrawableRes, bounds);
|
||||
if (thumbnail == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
|
||||
final WindowThumbnail wt = WindowThumbnail.createAndAttach(mSurfaceSession,
|
||||
change.getLeash(), thumbnail, transaction);
|
||||
final Animation a =
|
||||
mTransitionAnimation.createCrossProfileAppsThumbnailAnimationLocked(bounds);
|
||||
if (a == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Runnable finisher = () -> {
|
||||
wt.destroy(transaction);
|
||||
mTransactionPool.release(transaction);
|
||||
|
||||
finishCallback.run();
|
||||
};
|
||||
a.restrictDuration(MAX_ANIMATION_DURATION);
|
||||
a.scaleCurrentDuration(mTransitionAnimationScaleSetting);
|
||||
startAnimInternal(animations, a, wt.getSurface(), finisher,
|
||||
new Point(bounds.left, bounds.top));
|
||||
}
|
||||
|
||||
private void attachThumbnailAnimation(@NonNull ArrayList<Animator> animations,
|
||||
@NonNull Runnable finishCallback, TransitionInfo.Change change,
|
||||
TransitionInfo.AnimationOptions options) {
|
||||
final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
|
||||
final WindowThumbnail wt = WindowThumbnail.createAndAttach(mSurfaceSession,
|
||||
change.getLeash(), options.getThumbnail(), transaction);
|
||||
final Rect bounds = change.getEndAbsBounds();
|
||||
final int orientation = mContext.getResources().getConfiguration().orientation;
|
||||
final Animation a = mTransitionAnimation.createThumbnailAspectScaleAnimationLocked(bounds,
|
||||
mInsets, options.getThumbnail(), orientation, null /* startRect */,
|
||||
options.getTransitionBounds(), options.getType() == ANIM_THUMBNAIL_SCALE_UP);
|
||||
|
||||
final Runnable finisher = () -> {
|
||||
wt.destroy(transaction);
|
||||
mTransactionPool.release(transaction);
|
||||
|
||||
finishCallback.run();
|
||||
};
|
||||
a.restrictDuration(MAX_ANIMATION_DURATION);
|
||||
a.scaleCurrentDuration(mTransitionAnimationScaleSetting);
|
||||
startAnimInternal(animations, a, wt.getSurface(), finisher, null /* position */);
|
||||
}
|
||||
|
||||
private static int getWallpaperTransitType(TransitionInfo info) {
|
||||
boolean hasOpenWallpaper = false;
|
||||
boolean hasCloseWallpaper = false;
|
||||
|
||||
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
|
||||
final TransitionInfo.Change change = info.getChanges().get(i);
|
||||
if ((change.getFlags() & FLAG_SHOW_WALLPAPER) != 0) {
|
||||
if (Transitions.isOpeningType(change.getMode())) {
|
||||
hasOpenWallpaper = true;
|
||||
} else if (Transitions.isClosingType(change.getMode())) {
|
||||
hasCloseWallpaper = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasOpenWallpaper && hasCloseWallpaper) {
|
||||
return Transitions.isOpeningType(info.getType())
|
||||
? WALLPAPER_TRANSITION_INTRA_OPEN : WALLPAPER_TRANSITION_INTRA_CLOSE;
|
||||
} else if (hasOpenWallpaper) {
|
||||
return WALLPAPER_TRANSITION_OPEN;
|
||||
} else if (hasCloseWallpaper) {
|
||||
return WALLPAPER_TRANSITION_CLOSE;
|
||||
} else {
|
||||
return WALLPAPER_TRANSITION_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
private static void applyTransformation(long time, SurfaceControl.Transaction t,
|
||||
SurfaceControl leash, Animation anim, Transformation transformation, float[] matrix) {
|
||||
SurfaceControl leash, Animation anim, Transformation transformation, float[] matrix,
|
||||
Point position) {
|
||||
anim.getTransformation(time, transformation);
|
||||
if (position != null) {
|
||||
transformation.getMatrix().postTranslate(position.x, position.y);
|
||||
}
|
||||
t.setMatrix(leash, transformation.getMatrix(), matrix);
|
||||
t.setAlpha(leash, transformation.getAlpha());
|
||||
t.setFrameTimelineVsync(Choreographer.getInstance().getVsyncId());
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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.transition;
|
||||
|
||||
import android.graphics.ColorSpace;
|
||||
import android.graphics.GraphicBuffer;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.hardware.HardwareBuffer;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.SurfaceSession;
|
||||
|
||||
/**
|
||||
* Represents a surface that is displayed over a transition surface.
|
||||
*/
|
||||
class WindowThumbnail {
|
||||
|
||||
private SurfaceControl mSurfaceControl;
|
||||
|
||||
private WindowThumbnail() {}
|
||||
|
||||
/** Create a thumbnail surface and attach it over a parent surface. */
|
||||
static WindowThumbnail createAndAttach(SurfaceSession surfaceSession, SurfaceControl parent,
|
||||
HardwareBuffer thumbnailHeader, SurfaceControl.Transaction t) {
|
||||
WindowThumbnail windowThumbnail = new WindowThumbnail();
|
||||
windowThumbnail.mSurfaceControl = new SurfaceControl.Builder(surfaceSession)
|
||||
.setParent(parent)
|
||||
.setName("WindowThumanil : " + parent.toString())
|
||||
.setCallsite("WindowThumanil")
|
||||
.setFormat(PixelFormat.TRANSLUCENT)
|
||||
.build();
|
||||
|
||||
GraphicBuffer graphicBuffer = GraphicBuffer.createFromHardwareBuffer(thumbnailHeader);
|
||||
t.setBuffer(windowThumbnail.mSurfaceControl, graphicBuffer);
|
||||
t.setColorSpace(windowThumbnail.mSurfaceControl, ColorSpace.get(ColorSpace.Named.SRGB));
|
||||
t.setLayer(windowThumbnail.mSurfaceControl, Integer.MAX_VALUE);
|
||||
t.show(windowThumbnail.mSurfaceControl);
|
||||
t.apply();
|
||||
|
||||
return windowThumbnail;
|
||||
}
|
||||
|
||||
SurfaceControl getSurface() {
|
||||
return mSurfaceControl;
|
||||
}
|
||||
|
||||
/** Remove the thumbnail surface and release the surface. */
|
||||
void destroy(SurfaceControl.Transaction t) {
|
||||
if (mSurfaceControl == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
t.remove(mSurfaceControl);
|
||||
t.apply();
|
||||
mSurfaceControl.release();
|
||||
mSurfaceControl = null;
|
||||
}
|
||||
}
|
||||
@@ -68,6 +68,7 @@ import android.service.voice.VoiceInteractionManagerInternal;
|
||||
import android.util.Slog;
|
||||
import android.view.RemoteAnimationDefinition;
|
||||
import android.window.SizeConfigurationBuckets;
|
||||
import android.window.TransitionInfo;
|
||||
|
||||
import com.android.internal.app.AssistUtils;
|
||||
import com.android.internal.policy.IKeyguardDismissCallback;
|
||||
@@ -1014,6 +1015,9 @@ class ActivityClientController extends IActivityClientController.Stub {
|
||||
r.mDisplayContent.mAppTransition.overridePendingAppTransition(
|
||||
packageName, enterAnim, exitAnim, null, null,
|
||||
r.mOverrideTaskTransition);
|
||||
mService.getTransitionController().setOverrideAnimation(
|
||||
TransitionInfo.AnimationOptions.makeCustomAnimOptions(packageName,
|
||||
enterAnim, exitAnim, r.mOverrideTaskTransition));
|
||||
}
|
||||
}
|
||||
Binder.restoreCallingIdentity(origId);
|
||||
|
||||
@@ -313,6 +313,7 @@ import android.window.IRemoteTransition;
|
||||
import android.window.SizeConfigurationBuckets;
|
||||
import android.window.SplashScreenView.SplashScreenViewParcelable;
|
||||
import android.window.TaskSnapshot;
|
||||
import android.window.TransitionInfo.AnimationOptions;
|
||||
import android.window.WindowContainerToken;
|
||||
|
||||
import com.android.internal.R;
|
||||
@@ -4166,6 +4167,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
private void applyOptionsAnimation(ActivityOptions pendingOptions, Intent intent) {
|
||||
final int animationType = pendingOptions.getAnimationType();
|
||||
final DisplayContent displayContent = getDisplayContent();
|
||||
AnimationOptions options = null;
|
||||
switch (animationType) {
|
||||
case ANIM_CUSTOM:
|
||||
displayContent.mAppTransition.overridePendingAppTransition(
|
||||
@@ -4175,11 +4177,17 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
pendingOptions.getAnimationStartedListener(),
|
||||
pendingOptions.getAnimationFinishedListener(),
|
||||
pendingOptions.getOverrideTaskTransition());
|
||||
options = AnimationOptions.makeCustomAnimOptions(pendingOptions.getPackageName(),
|
||||
pendingOptions.getCustomEnterResId(), pendingOptions.getCustomExitResId(),
|
||||
pendingOptions.getOverrideTaskTransition());
|
||||
break;
|
||||
case ANIM_CLIP_REVEAL:
|
||||
displayContent.mAppTransition.overridePendingAppTransitionClipReveal(
|
||||
pendingOptions.getStartX(), pendingOptions.getStartY(),
|
||||
pendingOptions.getWidth(), pendingOptions.getHeight());
|
||||
options = AnimationOptions.makeClipRevealAnimOptions(
|
||||
pendingOptions.getStartX(), pendingOptions.getStartY(),
|
||||
pendingOptions.getWidth(), pendingOptions.getHeight());
|
||||
if (intent.getSourceBounds() == null) {
|
||||
intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
|
||||
pendingOptions.getStartY(),
|
||||
@@ -4191,6 +4199,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
displayContent.mAppTransition.overridePendingAppTransitionScaleUp(
|
||||
pendingOptions.getStartX(), pendingOptions.getStartY(),
|
||||
pendingOptions.getWidth(), pendingOptions.getHeight());
|
||||
options = AnimationOptions.makeScaleUpAnimOptions(
|
||||
pendingOptions.getStartX(), pendingOptions.getStartY(),
|
||||
pendingOptions.getWidth(), pendingOptions.getHeight());
|
||||
if (intent.getSourceBounds() == null) {
|
||||
intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
|
||||
pendingOptions.getStartY(),
|
||||
@@ -4206,6 +4217,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
pendingOptions.getStartX(), pendingOptions.getStartY(),
|
||||
pendingOptions.getAnimationStartedListener(),
|
||||
scaleUp);
|
||||
options = AnimationOptions.makeThumnbnailAnimOptions(buffer,
|
||||
pendingOptions.getStartX(), pendingOptions.getStartY(), scaleUp);
|
||||
if (intent.getSourceBounds() == null && buffer != null) {
|
||||
intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
|
||||
pendingOptions.getStartY(),
|
||||
@@ -4245,6 +4258,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
case ANIM_OPEN_CROSS_PROFILE_APPS:
|
||||
displayContent.mAppTransition
|
||||
.overridePendingAppTransitionStartCrossProfileApps();
|
||||
options = AnimationOptions.makeCrossProfileAnimOptions();
|
||||
break;
|
||||
case ANIM_NONE:
|
||||
case ANIM_UNDEFINED:
|
||||
@@ -4253,6 +4267,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
Slog.e(TAG_WM, "applyOptionsLocked: Unknown animationType=" + animationType);
|
||||
break;
|
||||
}
|
||||
|
||||
if (options != null) {
|
||||
mAtmService.getTransitionController().setOverrideAnimation(options);
|
||||
}
|
||||
}
|
||||
|
||||
void clearAllDrawn() {
|
||||
@@ -6597,8 +6615,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
}
|
||||
final Configuration displayConfig = mDisplayContent.getConfiguration();
|
||||
return getDisplayContent().mAppTransition.createThumbnailAspectScaleAnimationLocked(
|
||||
appRect, insets, thumbnailHeader, task, displayConfig.uiMode,
|
||||
displayConfig.orientation);
|
||||
appRect, insets, thumbnailHeader, task, displayConfig.orientation);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -78,10 +78,7 @@ import static com.android.internal.R.styleable.WindowAnimation_wallpaperIntraOpe
|
||||
import static com.android.internal.R.styleable.WindowAnimation_wallpaperIntraOpenExitAnimation;
|
||||
import static com.android.internal.R.styleable.WindowAnimation_wallpaperOpenEnterAnimation;
|
||||
import static com.android.internal.R.styleable.WindowAnimation_wallpaperOpenExitAnimation;
|
||||
import static com.android.internal.policy.TransitionAnimation.THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN;
|
||||
import static com.android.internal.policy.TransitionAnimation.THUMBNAIL_TRANSITION_ENTER_SCALE_UP;
|
||||
import static com.android.internal.policy.TransitionAnimation.THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN;
|
||||
import static com.android.internal.policy.TransitionAnimation.THUMBNAIL_TRANSITION_EXIT_SCALE_UP;
|
||||
import static com.android.internal.policy.TransitionAnimation.prepareThumbnailAnimationWithDuration;
|
||||
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
|
||||
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS_ANIM;
|
||||
import static com.android.server.wm.AppTransitionProto.APP_TRANSITION_STATE;
|
||||
@@ -102,12 +99,7 @@ import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Picture;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.hardware.HardwareBuffer;
|
||||
import android.os.Binder;
|
||||
import android.os.Debug;
|
||||
@@ -132,7 +124,6 @@ import android.view.animation.AlphaAnimation;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.AnimationSet;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.view.animation.ClipRectAnimation;
|
||||
import android.view.animation.Interpolator;
|
||||
import android.view.animation.PathInterpolator;
|
||||
import android.view.animation.ScaleAnimation;
|
||||
@@ -638,24 +629,6 @@ public class AppTransition implements Dump {
|
||||
(MAX_CLIP_REVEAL_TRANSITION_DURATION - DEFAULT_APP_TRANSITION_DURATION));
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the specified animation with a standard duration, interpolator, etc.
|
||||
*/
|
||||
Animation prepareThumbnailAnimationWithDuration(@Nullable Animation a, int appWidth,
|
||||
int appHeight, long duration, Interpolator interpolator) {
|
||||
if (a != null) {
|
||||
if (duration > 0) {
|
||||
a.setDuration(duration);
|
||||
}
|
||||
a.setFillAfter(true);
|
||||
if (interpolator != null) {
|
||||
a.setInterpolator(interpolator);
|
||||
}
|
||||
a.initialize(appWidth, appHeight, appWidth, appHeight);
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the specified animation with a standard duration, interpolator, etc.
|
||||
*/
|
||||
@@ -677,57 +650,17 @@ public class AppTransition implements Dump {
|
||||
mDecelerateInterpolator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current thumbnail transition state.
|
||||
*/
|
||||
int getThumbnailTransitionState(boolean enter) {
|
||||
if (enter) {
|
||||
if (mNextAppTransitionScaleUp) {
|
||||
return THUMBNAIL_TRANSITION_ENTER_SCALE_UP;
|
||||
} else {
|
||||
return THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN;
|
||||
}
|
||||
} else {
|
||||
if (mNextAppTransitionScaleUp) {
|
||||
return THUMBNAIL_TRANSITION_EXIT_SCALE_UP;
|
||||
} else {
|
||||
return THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an overlay with a background color and a thumbnail for the cross profile apps
|
||||
* animation.
|
||||
*/
|
||||
HardwareBuffer createCrossProfileAppsThumbnail(
|
||||
@DrawableRes int thumbnailDrawableRes, Rect frame) {
|
||||
final int width = frame.width();
|
||||
final int height = frame.height();
|
||||
|
||||
final Picture picture = new Picture();
|
||||
final Canvas canvas = picture.beginRecording(width, height);
|
||||
canvas.drawColor(Color.argb(0.6f, 0, 0, 0));
|
||||
final int thumbnailSize = mService.mContext.getResources().getDimensionPixelSize(
|
||||
com.android.internal.R.dimen.cross_profile_apps_thumbnail_size);
|
||||
final Drawable drawable = mService.mContext.getDrawable(thumbnailDrawableRes);
|
||||
drawable.setBounds(
|
||||
(width - thumbnailSize) / 2,
|
||||
(height - thumbnailSize) / 2,
|
||||
(width + thumbnailSize) / 2,
|
||||
(height + thumbnailSize) / 2);
|
||||
drawable.setTint(mContext.getColor(android.R.color.white));
|
||||
drawable.draw(canvas);
|
||||
picture.endRecording();
|
||||
|
||||
return Bitmap.createBitmap(picture).getHardwareBuffer();
|
||||
return mTransitionAnimation.createCrossProfileAppsThumbnail(thumbnailDrawableRes, frame);
|
||||
}
|
||||
|
||||
Animation createCrossProfileAppsThumbnailAnimationLocked(Rect appRect) {
|
||||
final Animation animation =
|
||||
mTransitionAnimation.loadCrossProfileAppThumbnailEnterAnimation();
|
||||
return prepareThumbnailAnimationWithDuration(animation, appRect.width(),
|
||||
appRect.height(), 0, null);
|
||||
return mTransitionAnimation.createCrossProfileAppsThumbnailAnimationLocked(appRect);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -735,115 +668,14 @@ public class AppTransition implements Dump {
|
||||
* when a thumbnail is specified with the pending animation override.
|
||||
*/
|
||||
Animation createThumbnailAspectScaleAnimationLocked(Rect appRect, @Nullable Rect contentInsets,
|
||||
HardwareBuffer thumbnailHeader, WindowContainer container, int uiMode,
|
||||
int orientation) {
|
||||
Animation a;
|
||||
final int thumbWidthI = thumbnailHeader.getWidth();
|
||||
final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
|
||||
final int thumbHeightI = thumbnailHeader.getHeight();
|
||||
final int appWidth = appRect.width();
|
||||
|
||||
float scaleW = appWidth / thumbWidth;
|
||||
getNextAppTransitionStartRect(container, mTmpRect);
|
||||
final float fromX;
|
||||
float fromY;
|
||||
final float toX;
|
||||
float toY;
|
||||
final float pivotX;
|
||||
final float pivotY;
|
||||
if (shouldScaleDownThumbnailTransition(uiMode, orientation)) {
|
||||
fromX = mTmpRect.left;
|
||||
fromY = mTmpRect.top;
|
||||
|
||||
// For the curved translate animation to work, the pivot points needs to be at the
|
||||
// same absolute position as the one from the real surface.
|
||||
toX = mTmpRect.width() / 2 * (scaleW - 1f) + appRect.left;
|
||||
toY = appRect.height() / 2 * (1 - 1 / scaleW) + appRect.top;
|
||||
pivotX = mTmpRect.width() / 2;
|
||||
pivotY = appRect.height() / 2 / scaleW;
|
||||
if (mGridLayoutRecentsEnabled) {
|
||||
// In the grid layout, the header is displayed above the thumbnail instead of
|
||||
// overlapping it.
|
||||
fromY -= thumbHeightI;
|
||||
toY -= thumbHeightI * scaleW;
|
||||
}
|
||||
} else {
|
||||
pivotX = 0;
|
||||
pivotY = 0;
|
||||
fromX = mTmpRect.left;
|
||||
fromY = mTmpRect.top;
|
||||
toX = appRect.left;
|
||||
toY = appRect.top;
|
||||
}
|
||||
final long duration = getAspectScaleDuration();
|
||||
final Interpolator interpolator = getAspectScaleInterpolator();
|
||||
if (mNextAppTransitionScaleUp) {
|
||||
// Animation up from the thumbnail to the full screen
|
||||
Animation scale = new ScaleAnimation(1f, scaleW, 1f, scaleW, pivotX, pivotY);
|
||||
scale.setInterpolator(interpolator);
|
||||
scale.setDuration(duration);
|
||||
Animation alpha = new AlphaAnimation(1f, 0f);
|
||||
alpha.setInterpolator(mThumbnailFadeOutInterpolator);
|
||||
alpha.setDuration(duration);
|
||||
Animation translate = createCurvedMotion(fromX, toX, fromY, toY);
|
||||
translate.setInterpolator(interpolator);
|
||||
translate.setDuration(duration);
|
||||
|
||||
mTmpFromClipRect.set(0, 0, thumbWidthI, thumbHeightI);
|
||||
mTmpToClipRect.set(appRect);
|
||||
|
||||
// Containing frame is in screen space, but we need the clip rect in the
|
||||
// app space.
|
||||
mTmpToClipRect.offsetTo(0, 0);
|
||||
mTmpToClipRect.right = (int) (mTmpToClipRect.right / scaleW);
|
||||
mTmpToClipRect.bottom = (int) (mTmpToClipRect.bottom / scaleW);
|
||||
|
||||
if (contentInsets != null) {
|
||||
mTmpToClipRect.inset((int) (-contentInsets.left * scaleW),
|
||||
(int) (-contentInsets.top * scaleW),
|
||||
(int) (-contentInsets.right * scaleW),
|
||||
(int) (-contentInsets.bottom * scaleW));
|
||||
}
|
||||
|
||||
Animation clipAnim = new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect);
|
||||
clipAnim.setInterpolator(interpolator);
|
||||
clipAnim.setDuration(duration);
|
||||
|
||||
// This AnimationSet uses the Interpolators assigned above.
|
||||
AnimationSet set = new AnimationSet(false);
|
||||
set.addAnimation(scale);
|
||||
if (!mGridLayoutRecentsEnabled) {
|
||||
// In the grid layout, the header should be shown for the whole animation.
|
||||
set.addAnimation(alpha);
|
||||
}
|
||||
set.addAnimation(translate);
|
||||
set.addAnimation(clipAnim);
|
||||
a = set;
|
||||
} else {
|
||||
// Animation down from the full screen to the thumbnail
|
||||
Animation scale = new ScaleAnimation(scaleW, 1f, scaleW, 1f, pivotX, pivotY);
|
||||
scale.setInterpolator(interpolator);
|
||||
scale.setDuration(duration);
|
||||
Animation alpha = new AlphaAnimation(0f, 1f);
|
||||
alpha.setInterpolator(mThumbnailFadeInInterpolator);
|
||||
alpha.setDuration(duration);
|
||||
Animation translate = createCurvedMotion(toX, fromX, toY, fromY);
|
||||
translate.setInterpolator(interpolator);
|
||||
translate.setDuration(duration);
|
||||
|
||||
// This AnimationSet uses the Interpolators assigned above.
|
||||
AnimationSet set = new AnimationSet(false);
|
||||
set.addAnimation(scale);
|
||||
if (!mGridLayoutRecentsEnabled) {
|
||||
// In the grid layout, the header should be shown for the whole animation.
|
||||
set.addAnimation(alpha);
|
||||
}
|
||||
set.addAnimation(translate);
|
||||
a = set;
|
||||
|
||||
}
|
||||
return prepareThumbnailAnimationWithDuration(a, appWidth, appRect.height(), 0,
|
||||
null);
|
||||
HardwareBuffer thumbnailHeader, WindowContainer container, int orientation) {
|
||||
AppTransitionAnimationSpec spec = mNextAppTransitionAnimationsSpecs.get(
|
||||
container.hashCode());
|
||||
return mTransitionAnimation.createThumbnailAspectScaleAnimationLocked(appRect,
|
||||
contentInsets, thumbnailHeader, orientation, spec != null ? spec.rect : null,
|
||||
mDefaultNextAppTransitionAnimationSpec != null
|
||||
? mDefaultNextAppTransitionAnimationSpec.rect : null,
|
||||
mNextAppTransitionScaleUp);
|
||||
}
|
||||
|
||||
private Animation createCurvedMotion(float fromX, float toX, float fromY, float toY) {
|
||||
@@ -1043,7 +875,7 @@ public class AppTransition implements Dump {
|
||||
+ "transit=%s Callers=%s",
|
||||
a, appTransitionOldToString(transit), Debug.getCallers(3));
|
||||
} else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CLIP_REVEAL) {
|
||||
a = mTransitionAnimation.createClipRevealAnimationLocked(
|
||||
a = mTransitionAnimation.createClipRevealAnimationLockedCompat(
|
||||
transit, enter, frame, displayFrame,
|
||||
mDefaultNextAppTransitionAnimationSpec != null
|
||||
? mDefaultNextAppTransitionAnimationSpec.rect : null);
|
||||
@@ -1052,7 +884,7 @@ public class AppTransition implements Dump {
|
||||
+ "transit=%s Callers=%s",
|
||||
a, appTransitionOldToString(transit), Debug.getCallers(3));
|
||||
} else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_SCALE_UP) {
|
||||
a = mTransitionAnimation.createScaleUpAnimationLocked(transit, enter, frame,
|
||||
a = mTransitionAnimation.createScaleUpAnimationLockedCompat(transit, enter, frame,
|
||||
mDefaultNextAppTransitionAnimationSpec != null
|
||||
? mDefaultNextAppTransitionAnimationSpec.rect : null);
|
||||
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
|
||||
@@ -1064,8 +896,8 @@ public class AppTransition implements Dump {
|
||||
mNextAppTransitionScaleUp =
|
||||
(mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP);
|
||||
final HardwareBuffer thumbnailHeader = getAppTransitionThumbnailHeader(container);
|
||||
a = mTransitionAnimation.createThumbnailEnterExitAnimationLocked(
|
||||
getThumbnailTransitionState(enter), frame, transit, thumbnailHeader,
|
||||
a = mTransitionAnimation.createThumbnailEnterExitAnimationLockedCompat(enter,
|
||||
mNextAppTransitionScaleUp, frame, transit, thumbnailHeader,
|
||||
mDefaultNextAppTransitionAnimationSpec != null
|
||||
? mDefaultNextAppTransitionAnimationSpec.rect : null);
|
||||
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
|
||||
@@ -1080,9 +912,9 @@ public class AppTransition implements Dump {
|
||||
(mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP);
|
||||
AppTransitionAnimationSpec spec = mNextAppTransitionAnimationsSpecs.get(
|
||||
container.hashCode());
|
||||
a = mTransitionAnimation.createAspectScaledThumbnailEnterExitAnimationLocked(
|
||||
getThumbnailTransitionState(enter), orientation, transit, frame,
|
||||
insets, surfaceInsets, stableInsets, freeform, spec != null ? spec.rect : null,
|
||||
a = mTransitionAnimation.createAspectScaledThumbnailEnterExitAnimationLocked(enter,
|
||||
mNextAppTransitionScaleUp, orientation, transit, frame, insets, surfaceInsets,
|
||||
stableInsets, freeform, spec != null ? spec.rect : null,
|
||||
mDefaultNextAppTransitionAnimationSpec != null
|
||||
? mDefaultNextAppTransitionAnimationSpec.rect : null);
|
||||
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package com.android.server.wm;
|
||||
|
||||
|
||||
import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
|
||||
import static android.view.Display.DEFAULT_DISPLAY;
|
||||
import static android.view.WindowManager.TRANSIT_CHANGE;
|
||||
@@ -124,6 +123,8 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
|
||||
/** The final animation targets derived from participants after promotion. */
|
||||
private ArraySet<WindowContainer> mTargets = null;
|
||||
|
||||
private TransitionInfo.AnimationOptions mOverrideOptions;
|
||||
|
||||
private @TransitionState int mState = STATE_COLLECTING;
|
||||
private boolean mReadyCalled = false;
|
||||
|
||||
@@ -206,6 +207,15 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
|
||||
mChanges.get(wc).mExistenceChanged = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set animation options for collecting transition by ActivityRecord.
|
||||
* @param options AnimationOptions captured from ActivityOptions
|
||||
*/
|
||||
void setOverrideAnimation(TransitionInfo.AnimationOptions options) {
|
||||
if (mSyncId < 0) return;
|
||||
mOverrideOptions = options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this when all known changes related to this transition have been applied. Until
|
||||
* all participants have finished drawing, the transition can still collect participants.
|
||||
@@ -327,6 +337,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
|
||||
mController.mAtm.mRootWindowContainer.getDisplayContent(displayId)
|
||||
.getPendingTransaction().merge(transaction);
|
||||
mSyncId = -1;
|
||||
mOverrideOptions = null;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -340,6 +351,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
|
||||
// Resolve the animating targets from the participants
|
||||
mTargets = calculateTargets(mParticipants, mChanges);
|
||||
final TransitionInfo info = calculateTransitionInfo(mType, mFlags, mTargets, mChanges);
|
||||
info.setAnimationOptions(mOverrideOptions);
|
||||
|
||||
handleNonAppWindowsInTransition(displayId, mType, mFlags);
|
||||
|
||||
@@ -375,6 +387,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
|
||||
cleanUpOnFailure();
|
||||
}
|
||||
mSyncId = -1;
|
||||
mOverrideOptions = null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -28,6 +28,7 @@ import android.util.Slog;
|
||||
import android.view.WindowManager;
|
||||
import android.window.IRemoteTransition;
|
||||
import android.window.ITransitionPlayer;
|
||||
import android.window.TransitionInfo;
|
||||
import android.window.TransitionRequestInfo;
|
||||
|
||||
import com.android.internal.protolog.ProtoLogGroup;
|
||||
@@ -240,6 +241,12 @@ class TransitionController {
|
||||
mCollectingTransition.collectExistenceChange(wc);
|
||||
}
|
||||
|
||||
/** @see Transition#setOverrideAnimation */
|
||||
void setOverrideAnimation(TransitionInfo.AnimationOptions options) {
|
||||
if (mCollectingTransition == null) return;
|
||||
mCollectingTransition.setOverrideAnimation(options);
|
||||
}
|
||||
|
||||
/** @see Transition#setReady */
|
||||
void setReady(boolean ready) {
|
||||
if (mCollectingTransition == null) return;
|
||||
|
||||
Reference in New Issue
Block a user