diff options
| author | 2021-09-21 04:20:29 +0000 | |
|---|---|---|
| committer | 2021-09-21 04:20:29 +0000 | |
| commit | bd6816c6f51dc24a68777d709b10f8d30e8f6a19 (patch) | |
| tree | 4a900eafa44ca2956d528404c346cb4da1940969 | |
| parent | c31f380a7def52771d592092d147eddb79685242 (diff) | |
| parent | 211d6cf754ad22981829d5b72ff9436ae11aada9 (diff) | |
Merge "Handle remote animation for split API" into sc-v2-dev
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); } } |