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:
Chris Li
2021-09-21 04:32:12 +00:00
committed by Automerger Merge Worker
3 changed files with 232 additions and 29 deletions

View File

@@ -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();
}
}

View File

@@ -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);

View File

@@ -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);
} }
} }