blob: 5f974785b6672dad384fd6418a643b3e578099c8 [file] [log] [blame]
Filip Gruszczynski84fa3352016-01-25 16:28:49 -08001/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.wm;
18
19import static com.android.server.wm.AppTransition.DEFAULT_APP_TRANSITION_DURATION;
20import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
21import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
22import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
23
24import android.animation.Animator;
25import android.animation.ValueAnimator;
26import android.graphics.Rect;
27import android.util.ArrayMap;
28import android.util.Slog;
29import android.view.animation.LinearInterpolator;
30
31/**
32 * Enables animating bounds of objects.
33 *
34 * In multi-window world bounds of both stack and tasks can change. When we need these bounds to
35 * change smoothly and not require the app to relaunch (e.g. because it handles resizes and
36 * relaunching it would cause poorer experience), these class provides a way to directly animate
37 * the bounds of the resized object.
38 *
39 * The object that is resized needs to implement {@link AnimateBoundsUser} interface.
40 */
41public class BoundsAnimationController {
42 private static final String TAG = TAG_WITH_CLASS_NAME ? "BoundsAnimationController" : TAG_WM;
43
44 // Only acccessed on UI thread.
45 private ArrayMap<AnimateBoundsUser, BoundsAnimator> mRunningAnimations = new ArrayMap<>();
46
47 private final class BoundsAnimator extends ValueAnimator
48 implements ValueAnimator.AnimatorUpdateListener, ValueAnimator.AnimatorListener {
49 private final AnimateBoundsUser mTarget;
50 private final Rect mFrom;
51 private final Rect mTo;
52 private final Rect mTmpRect;
53
54 BoundsAnimator(AnimateBoundsUser target, Rect from, Rect to) {
55 super();
56 mTarget = target;
57 mFrom = from;
58 mTo = to;
59 mTmpRect = new Rect();
60 addUpdateListener(this);
61 addListener(this);
62 }
63
64 @Override
65 public void onAnimationUpdate(ValueAnimator animation) {
66 final float value = (Float) animation.getAnimatedValue();
67 final float remains = 1 - value;
68 mTmpRect.left = (int) (mFrom.left * remains + mTo.left * value);
69 mTmpRect.top = (int) (mFrom.top * remains + mTo.top * value);
70 mTmpRect.right = (int) (mFrom.right * remains + mTo.right * value);
71 mTmpRect.bottom = (int) (mFrom.bottom * remains + mTo.bottom * value);
72 if (DEBUG_ANIM) Slog.d(TAG, "animateUpdate: mTarget=" + mTarget + ", mBounds="
73 + mTmpRect + ", from=" + mFrom + ", mTo=" + mTo + ", value=" + value
74 + ", remains=" + remains);
75 if (!mTarget.setSize(mTmpRect)) {
76 // Whoops, the target doesn't feel like animating anymore. Let's immediately finish
77 // any further animation.
78 animation.cancel();
79 }
80 }
81
82
83 @Override
84 public void onAnimationStart(Animator animation) {
85
86 }
87
88 @Override
89 public void onAnimationEnd(Animator animation) {
90 finishAnimation();
91 }
92
93 @Override
94 public void onAnimationCancel(Animator animation) {
95 finishAnimation();
96 }
97
98 private void finishAnimation() {
99 mTarget.finishBoundsAnimation();
100 removeListener(this);
101 removeUpdateListener(this);
102 mRunningAnimations.remove(mTarget);
103 }
104
105 @Override
106 public void onAnimationRepeat(Animator animation) {
107
108 }
109 }
110
111 public interface AnimateBoundsUser {
112 /**
113 * Asks the target to directly (without any intermediate steps, like scheduling animation)
114 * resize its bounds.
115 *
116 * @return Whether the target still wants to be animated and successfully finished the
117 * operation. If it returns false, the animation will immediately be cancelled. The target
118 * should return false when something abnormal happened, e.g. it was completely removed
119 * from the hierarchy and is not valid anymore.
120 */
121 boolean setSize(Rect bounds);
122
123 /**
124 * Callback for the target to inform it that the animation is finished, so it can do some
125 * necessary cleanup.
126 */
127 void finishBoundsAnimation();
128 }
129
130 void animateBounds(AnimateBoundsUser target, Rect from, Rect to) {
131 final BoundsAnimator existing = mRunningAnimations.get(target);
132 if (existing != null) {
133 existing.cancel();
134 }
135 BoundsAnimator animator = new BoundsAnimator(target, from, to);
136 mRunningAnimations.put(target, animator);
137 animator.setFloatValues(0f, 1f);
138 animator.setDuration(DEFAULT_APP_TRANSITION_DURATION);
139 animator.setInterpolator(new LinearInterpolator());
140 animator.start();
141 }
142}