- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / android / java / src / org / chromium / chrome / browser / infobar / AnimationHelper.java
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 package org.chromium.chrome.browser.infobar;
6
7 import android.animation.Animator;
8 import android.animation.AnimatorListenerAdapter;
9 import android.animation.AnimatorSet;
10 import android.animation.ObjectAnimator;
11 import android.animation.PropertyValuesHolder;
12 import android.os.Build;
13 import android.view.View;
14 import android.view.ViewTreeObserver;
15 import android.view.animation.AccelerateDecelerateInterpolator;
16
17 import org.chromium.base.ApiCompatibilityUtils;
18
19 import java.util.ArrayList;
20
21 /**
22  * Sets up animations to move InfoBars around inside of the InfoBarContainer.
23  *
24  * Animations proceed in several phases:
25  * 1) Prep work is done for the InfoBar so that the View being animated in (if it exists) is
26  *    properly sized.  This involves adding the View to a FrameLayout with a visibility of
27  *    INVISIBLE and triggering a layout.
28  *
29  * 2) Once the View has an actual size, we compute all of the actions needed for the animation.
30  *    We use translations primarily to slide things in and out of the screen as things are shown,
31  *    hidden, or resized.
32  *
33  * 3) The animation is kicked off and the animations run.  During this phase, the View being shown
34  *    is added to ContentWrapperView.
35  *
36  * 4) At the end of the animation, we clean up everything and make sure all the children are in the
37  *    right places.
38  */
39 public class AnimationHelper implements ViewTreeObserver.OnGlobalLayoutListener {
40     private static final String TAG = "AnimationHelper";
41
42     private static final long ANIMATION_DURATION_MS = 250;
43
44     public static final int ANIMATION_TYPE_SHOW = 0;
45     public static final int ANIMATION_TYPE_SWAP = 1;
46     public static final int ANIMATION_TYPE_HIDE = 2;
47     public static final int ANIMATION_TYPE_BOUNDARY = 3;
48
49     private final InfoBarContainer mContainer;
50     private final InfoBar mInfoBar;
51     private final ContentWrapperView mTargetWrapperView;
52     private final AnimatorSet mAnimatorSet;
53     private final int mAnimationType;
54     private final View mToShow;
55
56     private boolean mAnimationStarted;
57
58     /**
59      * Creates and starts an animation.
60      * @param container InfoBarContainer that is having its InfoBars animated.
61      * @param target ContentWrapperView that is the focus of the animation and is being resized,
62      *               shown, or hidden.
63      * @param infoBar InfoBar that goes with the specified ContentWrapperView.
64      * @param toShow If non-null, this View will replace whatever child View the ContentWrapperView
65      *               is currently displaying.
66      * @param aniamtionType Type of animation being performed.
67      */
68     public AnimationHelper(InfoBarContainer container, ContentWrapperView target, InfoBar infoBar,
69             View toShow, int animationType) {
70         mContainer = container;
71         mInfoBar = infoBar;
72         mTargetWrapperView = target;
73         mAnimatorSet = new AnimatorSet();
74         mAnimationType = animationType;
75         mToShow = toShow;
76         assert mContainer.indexOfChild(mTargetWrapperView) != -1;
77     }
78
79     /**
80      * Start the animation.
81      */
82     public void start() {
83         mTargetWrapperView.prepareTransition(mToShow);
84         mContainer.prepareTransition(mToShow);
85
86         if (mToShow == null) {
87             // We've got a size already; start the animation immediately.
88             continueAnimation();
89         } else {
90             // Wait for the object to be sized.
91             mTargetWrapperView.getViewTreeObserver().addOnGlobalLayoutListener(this);
92         }
93     }
94
95     /**
96      * @return the InfoBar being animated.
97      */
98     public InfoBar getInfoBar() {
99         return mInfoBar;
100     }
101
102     /**
103      * @return the ContentWrapperView being animated.
104      */
105     public ContentWrapperView getTarget() {
106         return mTargetWrapperView;
107     }
108
109     /**
110      * @return the type of animation being performed.
111      */
112     public int getAnimationType() {
113         return mAnimationType;
114     }
115
116     /**
117      * Catch when the layout occurs, which lets us know when the View has been sized properly.
118      */
119     @Override
120     public void onGlobalLayout() {
121         ApiCompatibilityUtils.removeOnGlobalLayoutListener(mTargetWrapperView, this);
122         continueAnimation();
123     }
124
125     private void continueAnimation() {
126         if (mAnimationStarted) return;
127         mAnimationStarted = true;
128
129         boolean infoBarsOnTop = mContainer.areInfoBarsOnTop();
130         int indexOfWrapperView = mContainer.indexOfChild(mTargetWrapperView);
131         assert indexOfWrapperView != -1;
132
133         ArrayList<Animator> animators = new ArrayList<Animator>();
134         mTargetWrapperView.getAnimationsForTransition(animators);
135
136         // Determine where the tops of each InfoBar will need to be.
137         int heightDifference = mTargetWrapperView.getTransitionHeightDifference();
138         int cumulativeTopStart = 0;
139         int cumulativeTopEnd = 0;
140         int cumulativeEndHeight = 0;
141         if (!infoBarsOnTop) {
142             if (heightDifference >= 0) {
143                 // The current container is smaller than the final container, so the current 0
144                 // coordinate will be >= 0 in the final container.
145                 cumulativeTopStart = heightDifference;
146             } else {
147                 // The current container is bigger than the final container, so the current 0
148                 // coordinate will be < 0 in the final container.
149                 cumulativeTopEnd = -heightDifference;
150             }
151         }
152
153         for (int i = 0; i < mContainer.getChildCount(); ++i) {
154             View view = mContainer.getChildAt(i);
155
156             // At this point, the View being transitioned in shouldn't have been added to the
157             // visible container, yet, and shouldn't affect calculations.
158             int startHeight = view.getHeight();
159             int endHeight = startHeight + (i == indexOfWrapperView ? heightDifference : 0);
160             int topStart = cumulativeTopStart;
161             int topEnd = cumulativeTopEnd;
162             int bottomStart = topStart + startHeight;
163             int bottomEnd = topEnd + endHeight;
164
165             if (topStart == topEnd && bottomStart == bottomEnd) {
166                 // The View needs to stay put.
167                 view.setTop(topEnd);
168                 view.setBottom(bottomEnd);
169                 view.setY(topEnd);
170                 view.setTranslationY(0);
171             } else {
172                 // A translation is required to move the View into place.
173                 int translation = heightDifference;
174                 if (infoBarsOnTop) translation *= -1;
175
176                 boolean translateDownward = false;
177                 if (topStart < topEnd) {
178                     translateDownward = infoBarsOnTop;
179                 } else if (topStart > topEnd) {
180                     translateDownward = !infoBarsOnTop;
181                 } else {
182                     translateDownward = bottomEnd > bottomStart;
183                 }
184
185                 PropertyValuesHolder viewTranslation;
186                 if (translateDownward) {
187                     view.setTop(topEnd);
188                     view.setBottom(bottomEnd);
189                     view.setTranslationY(translation);
190                     view.setY(topEnd + translation);
191                     viewTranslation =
192                             PropertyValuesHolder.ofFloat("translationY", translation, 0.0f);
193                 } else {
194                     viewTranslation =
195                             PropertyValuesHolder.ofFloat("translationY", 0.0f, -translation);
196                 }
197
198                 animators.add(ObjectAnimator.ofPropertyValuesHolder(view, viewTranslation));
199             }
200
201             // Add heights to the cumulative totals.
202             cumulativeTopStart += startHeight;
203             cumulativeTopEnd += endHeight;
204             cumulativeEndHeight += endHeight;
205         }
206
207         // Lock the InfoBarContainer's size at its largest during the animation to avoid
208         // clipping issues.
209         final int oldContainerTop = mContainer.getTop();
210         final int oldContainerBottom = mContainer.getBottom();
211         final int newContainerTop;
212         final int newContainerBottom;
213         if (infoBarsOnTop) {
214             newContainerTop = oldContainerTop;
215             newContainerBottom = newContainerTop + cumulativeEndHeight;
216         } else {
217             newContainerBottom = oldContainerBottom;
218             newContainerTop = newContainerBottom - cumulativeEndHeight;
219         }
220         final int biggestContainerTop = Math.min(oldContainerTop, newContainerTop);
221         final int biggestContainerBottom = Math.max(oldContainerBottom, newContainerBottom);
222         mContainer.setTop(biggestContainerTop);
223         mContainer.setBottom(biggestContainerBottom);
224
225         // Set up and run all of the animations.
226         mAnimatorSet.addListener(new AnimatorListenerAdapter() {
227             @Override
228             public void onAnimationStart(Animator animation) {
229                 mTargetWrapperView.startTransition();
230                 mContainer.startTransition();
231             }
232
233             @Override
234             public void onAnimationEnd(Animator animation) {
235                 mTargetWrapperView.finishTransition();
236                 mContainer.finishTransition();
237
238                 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN && mToShow != null &&
239                         (mAnimationType == ANIMATION_TYPE_SHOW ||
240                                 mAnimationType == ANIMATION_TYPE_SWAP)) {
241                         mToShow.announceForAccessibility(
242                                 mInfoBar.getMessageText(mContainer.getContext()));
243                 }
244             }
245         });
246
247         mAnimatorSet.playTogether(animators);
248         mAnimatorSet.setDuration(ANIMATION_DURATION_MS);
249         mAnimatorSet.setInterpolator(new AccelerateDecelerateInterpolator());
250         mAnimatorSet.start();
251     }
252 }