Merge "Handle remote animation for split API" into sc-v2-dev am: bd6816c6f5 am: bda335fb22
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/15632615 Change-Id: I1b460235c7109049a49e852fa60aea13b326997c
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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 androidx.window.extensions.organizer;
|
||||
|
||||
import android.view.Choreographer;
|
||||
import android.view.RemoteAnimationTarget;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.Transformation;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
/**
|
||||
* Wrapper to handle the TaskFragment animation update in one {@link SurfaceControl.Transaction}.
|
||||
*/
|
||||
class TaskFragmentAnimationAdapter {
|
||||
private final Animation mAnimation;
|
||||
private final RemoteAnimationTarget mTarget;
|
||||
private final SurfaceControl mLeash;
|
||||
private final Transformation mTransformation = new Transformation();
|
||||
private final float[] mMatrix = new float[9];
|
||||
private boolean mIsFirstFrame = true;
|
||||
|
||||
TaskFragmentAnimationAdapter(@NonNull Animation animation,
|
||||
@NonNull RemoteAnimationTarget target) {
|
||||
mAnimation = animation;
|
||||
mTarget = target;
|
||||
mLeash = target.leash;
|
||||
}
|
||||
|
||||
/** Called on frame update. */
|
||||
void onAnimationUpdate(@NonNull SurfaceControl.Transaction t, long currentPlayTime) {
|
||||
if (mIsFirstFrame) {
|
||||
t.show(mLeash);
|
||||
mIsFirstFrame = false;
|
||||
}
|
||||
|
||||
currentPlayTime = Math.min(currentPlayTime, mAnimation.getDuration());
|
||||
mAnimation.getTransformation(currentPlayTime, mTransformation);
|
||||
mTransformation.getMatrix().postTranslate(
|
||||
mTarget.localBounds.left, mTarget.localBounds.top);
|
||||
t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix);
|
||||
t.setAlpha(mLeash, mTransformation.getAlpha());
|
||||
t.setFrameTimelineVsync(Choreographer.getInstance().getVsyncId());
|
||||
}
|
||||
|
||||
/** Called after animation finished. */
|
||||
void onAnimationEnd(@NonNull SurfaceControl.Transaction t) {
|
||||
onAnimationUpdate(t, mAnimation.getDuration());
|
||||
}
|
||||
|
||||
long getDurationHint() {
|
||||
return mAnimation.computeDurationHint();
|
||||
}
|
||||
}
|
||||
@@ -45,7 +45,7 @@ class TaskFragmentAnimationController {
|
||||
}
|
||||
final RemoteAnimationDefinition definition = new RemoteAnimationDefinition();
|
||||
final RemoteAnimationAdapter animationAdapter =
|
||||
new RemoteAnimationAdapter(mRemoteRunner, 0, 0);
|
||||
new RemoteAnimationAdapter(mRemoteRunner, 0, 0, true /* changeNeedsSnapshot */);
|
||||
definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_OPEN, animationAdapter);
|
||||
definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CLOSE, animationAdapter);
|
||||
definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CHANGE, animationAdapter);
|
||||
|
||||
@@ -16,8 +16,15 @@
|
||||
|
||||
package androidx.window.extensions.organizer;
|
||||
|
||||
import static android.view.RemoteAnimationTarget.MODE_OPENING;
|
||||
import static android.view.RemoteAnimationTarget.MODE_CLOSING;
|
||||
import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CHANGE;
|
||||
import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CLOSE;
|
||||
import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_OPEN;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.app.ActivityThread;
|
||||
import android.content.Context;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.RemoteException;
|
||||
@@ -27,24 +34,41 @@ import android.view.IRemoteAnimationRunner;
|
||||
import android.view.RemoteAnimationTarget;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.WindowManager;
|
||||
import android.view.animation.Animation;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.internal.R;
|
||||
import com.android.internal.policy.AttributeCache;
|
||||
import com.android.internal.policy.TransitionAnimation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/** To run the TaskFragment animations. */
|
||||
class TaskFragmentAnimationRunner extends IRemoteAnimationRunner.Stub {
|
||||
|
||||
private static final String TAG = "TaskFragAnimationRunner";
|
||||
private final Handler mHandler = new Handler(Looper.myLooper());
|
||||
private final TransitionAnimation mTransitionAnimation;
|
||||
|
||||
TaskFragmentAnimationRunner() {
|
||||
final Context context = ActivityThread.currentActivityThread().getApplication();
|
||||
mTransitionAnimation = new TransitionAnimation(context, false /* debug */, TAG);
|
||||
// Initialize the AttributeCache for the TransitionAnimation.
|
||||
AttributeCache.init(context);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private IRemoteAnimationFinishedCallback mFinishedCallback;
|
||||
private Animator mAnimator;
|
||||
|
||||
@Override
|
||||
public void onAnimationStart(@WindowManager.TransitionOldType int transit,
|
||||
RemoteAnimationTarget[] apps,
|
||||
RemoteAnimationTarget[] wallpapers,
|
||||
RemoteAnimationTarget[] nonApps,
|
||||
IRemoteAnimationFinishedCallback finishedCallback) {
|
||||
@NonNull RemoteAnimationTarget[] apps,
|
||||
@NonNull RemoteAnimationTarget[] wallpapers,
|
||||
@NonNull RemoteAnimationTarget[] nonApps,
|
||||
@NonNull IRemoteAnimationFinishedCallback finishedCallback) {
|
||||
if (wallpapers.length != 0 || nonApps.length != 0) {
|
||||
throw new IllegalArgumentException("TaskFragment shouldn't handle animation with"
|
||||
+ "wallpaper or non-app windows.");
|
||||
@@ -52,7 +76,7 @@ class TaskFragmentAnimationRunner extends IRemoteAnimationRunner.Stub {
|
||||
if (TaskFragmentAnimationController.DEBUG) {
|
||||
Log.v(TAG, "onAnimationStart transit=" + transit);
|
||||
}
|
||||
mHandler.post(() -> startAnimation(apps, finishedCallback));
|
||||
mHandler.post(() -> startAnimation(transit, apps, finishedCallback));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -60,34 +84,144 @@ class TaskFragmentAnimationRunner extends IRemoteAnimationRunner.Stub {
|
||||
if (TaskFragmentAnimationController.DEBUG) {
|
||||
Log.v(TAG, "onAnimationCancelled");
|
||||
}
|
||||
mHandler.post(this::onAnimationFinished);
|
||||
mHandler.post(this::cancelAnimation);
|
||||
}
|
||||
|
||||
private void startAnimation(RemoteAnimationTarget[] targets,
|
||||
IRemoteAnimationFinishedCallback finishedCallback) {
|
||||
// TODO(b/196173550) replace with actual animations
|
||||
mFinishedCallback = finishedCallback;
|
||||
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
|
||||
for (RemoteAnimationTarget target : targets) {
|
||||
if (target.mode == MODE_OPENING) {
|
||||
t.show(target.leash);
|
||||
t.setAlpha(target.leash, 1);
|
||||
}
|
||||
t.setPosition(target.leash, target.localBounds.left, target.localBounds.top);
|
||||
/** Creates and starts animation. */
|
||||
private void startAnimation(@WindowManager.TransitionOldType int transit,
|
||||
@NonNull RemoteAnimationTarget[] targets,
|
||||
@NonNull IRemoteAnimationFinishedCallback finishedCallback) {
|
||||
if (mAnimator != null) {
|
||||
Log.w(TAG, "start new animation when the previous one is not finished yet.");
|
||||
mAnimator.cancel();
|
||||
}
|
||||
t.apply();
|
||||
onAnimationFinished();
|
||||
mAnimator = createAnimator(transit, targets, finishedCallback);
|
||||
mAnimator.start();
|
||||
}
|
||||
|
||||
private void onAnimationFinished() {
|
||||
if (mFinishedCallback == null) {
|
||||
/** Cancels animation. */
|
||||
private void cancelAnimation() {
|
||||
if (mAnimator == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
mFinishedCallback.onAnimationFinished();
|
||||
} catch (RemoteException e) {
|
||||
e.rethrowFromSystemServer();
|
||||
mAnimator.cancel();
|
||||
mAnimator = null;
|
||||
}
|
||||
|
||||
/** Creates the animator given the transition type and windows. */
|
||||
private Animator createAnimator(@WindowManager.TransitionOldType int transit,
|
||||
@NonNull RemoteAnimationTarget[] targets,
|
||||
@NonNull IRemoteAnimationFinishedCallback finishedCallback) {
|
||||
final List<TaskFragmentAnimationAdapter> adapters =
|
||||
createAnimationAdapters(transit, targets);
|
||||
long duration = 0;
|
||||
for (TaskFragmentAnimationAdapter adapter : adapters) {
|
||||
duration = Math.max(duration, adapter.getDurationHint());
|
||||
}
|
||||
mFinishedCallback = null;
|
||||
final ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
|
||||
animator.setDuration(duration);
|
||||
animator.addUpdateListener((anim) -> {
|
||||
// Update all adapters in the same transaction.
|
||||
final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
|
||||
for (TaskFragmentAnimationAdapter adapter : adapters) {
|
||||
adapter.onAnimationUpdate(t, animator.getCurrentPlayTime());
|
||||
}
|
||||
t.apply();
|
||||
});
|
||||
animator.addListener(new Animator.AnimatorListener() {
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
|
||||
for (TaskFragmentAnimationAdapter adapter : adapters) {
|
||||
adapter.onAnimationEnd(t);
|
||||
}
|
||||
t.apply();
|
||||
|
||||
try {
|
||||
finishedCallback.onAnimationFinished();
|
||||
} catch (RemoteException e) {
|
||||
e.rethrowFromSystemServer();
|
||||
}
|
||||
mAnimator = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animator animation) {}
|
||||
});
|
||||
return animator;
|
||||
}
|
||||
|
||||
/** List of {@link TaskFragmentAnimationAdapter} to handle animations on all window targets. */
|
||||
private List<TaskFragmentAnimationAdapter> createAnimationAdapters(
|
||||
@WindowManager.TransitionOldType int transit,
|
||||
@NonNull RemoteAnimationTarget[] targets) {
|
||||
switch (transit) {
|
||||
case TRANSIT_OLD_TASK_FRAGMENT_OPEN:
|
||||
return createOpenAnimationAdapters(targets);
|
||||
case TRANSIT_OLD_TASK_FRAGMENT_CLOSE:
|
||||
return createCloseAnimationAdapters(targets);
|
||||
case TRANSIT_OLD_TASK_FRAGMENT_CHANGE:
|
||||
return createChangeAnimationAdapters(targets);
|
||||
default:
|
||||
throw new IllegalArgumentException("Unhandled transit type=" + transit);
|
||||
}
|
||||
}
|
||||
|
||||
private List<TaskFragmentAnimationAdapter> createOpenAnimationAdapters(
|
||||
@NonNull RemoteAnimationTarget[] targets) {
|
||||
// TODO(b/196173550) We need to customize the animation to handle two open window as one.
|
||||
final List<TaskFragmentAnimationAdapter> adapters = new ArrayList<>();
|
||||
for (RemoteAnimationTarget target : targets) {
|
||||
final Animation animation =
|
||||
loadOpenAnimation(target.mode != MODE_CLOSING /* isEnter */);
|
||||
adapters.add(new TaskFragmentAnimationAdapter(animation, target));
|
||||
}
|
||||
return adapters;
|
||||
}
|
||||
|
||||
private List<TaskFragmentAnimationAdapter> createCloseAnimationAdapters(
|
||||
@NonNull RemoteAnimationTarget[] targets) {
|
||||
// TODO(b/196173550) We need to customize the animation to handle two open window as one.
|
||||
final List<TaskFragmentAnimationAdapter> adapters = new ArrayList<>();
|
||||
for (RemoteAnimationTarget target : targets) {
|
||||
final Animation animation =
|
||||
loadCloseAnimation(target.mode != MODE_CLOSING /* isEnter */);
|
||||
adapters.add(new TaskFragmentAnimationAdapter(animation, target));
|
||||
}
|
||||
return adapters;
|
||||
}
|
||||
|
||||
private List<TaskFragmentAnimationAdapter> createChangeAnimationAdapters(
|
||||
@NonNull RemoteAnimationTarget[] targets) {
|
||||
// TODO(b/196173550) We need to hard code the change animation instead of using the default
|
||||
// open. See WindowChangeAnimationSpec.java as an example.
|
||||
final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
|
||||
for (RemoteAnimationTarget target : targets) {
|
||||
// The start leash is snapshot of the previous window. Hide it for now, will need to use
|
||||
// it for the fade in.
|
||||
if (target.startLeash != null) {
|
||||
t.hide(target.startLeash);
|
||||
}
|
||||
}
|
||||
t.apply();
|
||||
return createOpenAnimationAdapters(targets);
|
||||
}
|
||||
|
||||
private Animation loadOpenAnimation(boolean isEnter) {
|
||||
return mTransitionAnimation.loadDefaultAnimationAttr(isEnter
|
||||
? R.styleable.WindowAnimation_activityOpenEnterAnimation
|
||||
: R.styleable.WindowAnimation_activityOpenExitAnimation);
|
||||
}
|
||||
|
||||
private Animation loadCloseAnimation(boolean isEnter) {
|
||||
return mTransitionAnimation.loadDefaultAnimationAttr(isEnter
|
||||
? R.styleable.WindowAnimation_activityCloseEnterAnimation
|
||||
: R.styleable.WindowAnimation_activityCloseExitAnimation);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user