Merge "Handle remote animation for split API" into sc-v2-dev am: bd6816c6f5
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/15632615 Change-Id: I9ed6de09f4ff46e02ffa6bffeec3724652dec168
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 RemoteAnimationDefinition definition = new RemoteAnimationDefinition();
|
||||||
final RemoteAnimationAdapter animationAdapter =
|
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_OPEN, animationAdapter);
|
||||||
definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CLOSE, animationAdapter);
|
definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CLOSE, animationAdapter);
|
||||||
definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CHANGE, animationAdapter);
|
definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CHANGE, animationAdapter);
|
||||||
|
|||||||
@@ -16,8 +16,15 @@
|
|||||||
|
|
||||||
package androidx.window.extensions.organizer;
|
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.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
@@ -27,24 +34,41 @@ import android.view.IRemoteAnimationRunner;
|
|||||||
import android.view.RemoteAnimationTarget;
|
import android.view.RemoteAnimationTarget;
|
||||||
import android.view.SurfaceControl;
|
import android.view.SurfaceControl;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
|
import android.view.animation.Animation;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
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. */
|
/** To run the TaskFragment animations. */
|
||||||
class TaskFragmentAnimationRunner extends IRemoteAnimationRunner.Stub {
|
class TaskFragmentAnimationRunner extends IRemoteAnimationRunner.Stub {
|
||||||
|
|
||||||
private static final String TAG = "TaskFragAnimationRunner";
|
private static final String TAG = "TaskFragAnimationRunner";
|
||||||
private final Handler mHandler = new Handler(Looper.myLooper());
|
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
|
@Nullable
|
||||||
private IRemoteAnimationFinishedCallback mFinishedCallback;
|
private Animator mAnimator;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAnimationStart(@WindowManager.TransitionOldType int transit,
|
public void onAnimationStart(@WindowManager.TransitionOldType int transit,
|
||||||
RemoteAnimationTarget[] apps,
|
@NonNull RemoteAnimationTarget[] apps,
|
||||||
RemoteAnimationTarget[] wallpapers,
|
@NonNull RemoteAnimationTarget[] wallpapers,
|
||||||
RemoteAnimationTarget[] nonApps,
|
@NonNull RemoteAnimationTarget[] nonApps,
|
||||||
IRemoteAnimationFinishedCallback finishedCallback) {
|
@NonNull IRemoteAnimationFinishedCallback finishedCallback) {
|
||||||
if (wallpapers.length != 0 || nonApps.length != 0) {
|
if (wallpapers.length != 0 || nonApps.length != 0) {
|
||||||
throw new IllegalArgumentException("TaskFragment shouldn't handle animation with"
|
throw new IllegalArgumentException("TaskFragment shouldn't handle animation with"
|
||||||
+ "wallpaper or non-app windows.");
|
+ "wallpaper or non-app windows.");
|
||||||
@@ -52,7 +76,7 @@ class TaskFragmentAnimationRunner extends IRemoteAnimationRunner.Stub {
|
|||||||
if (TaskFragmentAnimationController.DEBUG) {
|
if (TaskFragmentAnimationController.DEBUG) {
|
||||||
Log.v(TAG, "onAnimationStart transit=" + transit);
|
Log.v(TAG, "onAnimationStart transit=" + transit);
|
||||||
}
|
}
|
||||||
mHandler.post(() -> startAnimation(apps, finishedCallback));
|
mHandler.post(() -> startAnimation(transit, apps, finishedCallback));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -60,34 +84,144 @@ class TaskFragmentAnimationRunner extends IRemoteAnimationRunner.Stub {
|
|||||||
if (TaskFragmentAnimationController.DEBUG) {
|
if (TaskFragmentAnimationController.DEBUG) {
|
||||||
Log.v(TAG, "onAnimationCancelled");
|
Log.v(TAG, "onAnimationCancelled");
|
||||||
}
|
}
|
||||||
mHandler.post(this::onAnimationFinished);
|
mHandler.post(this::cancelAnimation);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startAnimation(RemoteAnimationTarget[] targets,
|
/** Creates and starts animation. */
|
||||||
IRemoteAnimationFinishedCallback finishedCallback) {
|
private void startAnimation(@WindowManager.TransitionOldType int transit,
|
||||||
// TODO(b/196173550) replace with actual animations
|
@NonNull RemoteAnimationTarget[] targets,
|
||||||
mFinishedCallback = finishedCallback;
|
@NonNull IRemoteAnimationFinishedCallback finishedCallback) {
|
||||||
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
|
if (mAnimator != null) {
|
||||||
for (RemoteAnimationTarget target : targets) {
|
Log.w(TAG, "start new animation when the previous one is not finished yet.");
|
||||||
if (target.mode == MODE_OPENING) {
|
mAnimator.cancel();
|
||||||
t.show(target.leash);
|
|
||||||
t.setAlpha(target.leash, 1);
|
|
||||||
}
|
|
||||||
t.setPosition(target.leash, target.localBounds.left, target.localBounds.top);
|
|
||||||
}
|
}
|
||||||
t.apply();
|
mAnimator = createAnimator(transit, targets, finishedCallback);
|
||||||
onAnimationFinished();
|
mAnimator.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onAnimationFinished() {
|
/** Cancels animation. */
|
||||||
if (mFinishedCallback == null) {
|
private void cancelAnimation() {
|
||||||
|
if (mAnimator == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
mAnimator.cancel();
|
||||||
mFinishedCallback.onAnimationFinished();
|
mAnimator = null;
|
||||||
} catch (RemoteException e) {
|
}
|
||||||
e.rethrowFromSystemServer();
|
|
||||||
|
/** 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