summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Chris Li <lihongyu@google.com> 2021-09-21 04:20:29 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2021-09-21 04:20:29 +0000
commitbd6816c6f51dc24a68777d709b10f8d30e8f6a19 (patch)
tree4a900eafa44ca2956d528404c346cb4da1940969
parentc31f380a7def52771d592092d147eddb79685242 (diff)
parent211d6cf754ad22981829d5b72ff9436ae11aada9 (diff)
Merge "Handle remote animation for split API" into sc-v2-dev
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentAnimationAdapter.java69
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentAnimationController.java2
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentAnimationRunner.java190
3 files changed, 232 insertions, 29 deletions
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentAnimationAdapter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentAnimationAdapter.java
new file mode 100644
index 000000000000..2fa0045953e0
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentAnimationAdapter.java
@@ -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();
+ }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentAnimationController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentAnimationController.java
index b85287d8a919..663124349464 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentAnimationController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentAnimationController.java
@@ -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);
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentAnimationRunner.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentAnimationRunner.java
index 9ee60d8c6bd3..7ac11185c302 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentAnimationRunner.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentAnimationRunner.java
@@ -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());
+ }
+ 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);
+ }
}
- mFinishedCallback = null;
+ 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);
}
}