• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package com.android.systemui.statusbar;
18 
19 import android.animation.Animator;
20 import android.animation.AnimatorListenerAdapter;
21 import android.animation.ValueAnimator;
22 import android.util.ArrayMap;
23 import android.util.ArraySet;
24 import android.view.View;
25 import android.view.ViewGroup;
26 import android.view.animation.Interpolator;
27 
28 import com.android.systemui.Interpolators;
29 import com.android.systemui.R;
30 import com.android.systemui.statusbar.notification.TransformState;
31 import com.android.systemui.statusbar.stack.StackStateAnimator;
32 
33 import java.util.Stack;
34 
35 /**
36  * A view that can be transformed to and from.
37  */
38 public class ViewTransformationHelper implements TransformableView {
39 
40     private static final int TAG_CONTAINS_TRANSFORMED_VIEW = R.id.contains_transformed_view;
41 
42     private ArrayMap<Integer, View> mTransformedViews = new ArrayMap<>();
43     private ArrayMap<Integer, CustomTransformation> mCustomTransformations = new ArrayMap<>();
44     private ValueAnimator mViewTransformationAnimation;
45 
addTransformedView(int key, View transformedView)46     public void addTransformedView(int key, View transformedView) {
47         mTransformedViews.put(key, transformedView);
48     }
49 
reset()50     public void reset() {
51         mTransformedViews.clear();
52     }
53 
setCustomTransformation(CustomTransformation transformation, int viewType)54     public void setCustomTransformation(CustomTransformation transformation, int viewType) {
55         mCustomTransformations.put(viewType, transformation);
56     }
57 
58     @Override
getCurrentState(int fadingView)59     public TransformState getCurrentState(int fadingView) {
60         View view = mTransformedViews.get(fadingView);
61         if (view != null && view.getVisibility() != View.GONE) {
62             return TransformState.createFrom(view);
63         }
64         return null;
65     }
66 
67     @Override
transformTo(final TransformableView notification, final Runnable endRunnable)68     public void transformTo(final TransformableView notification, final Runnable endRunnable) {
69         if (mViewTransformationAnimation != null) {
70             mViewTransformationAnimation.cancel();
71         }
72         mViewTransformationAnimation = ValueAnimator.ofFloat(0.0f, 1.0f);
73         mViewTransformationAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
74             @Override
75             public void onAnimationUpdate(ValueAnimator animation) {
76                 transformTo(notification, animation.getAnimatedFraction());
77             }
78         });
79         mViewTransformationAnimation.setInterpolator(Interpolators.LINEAR);
80         mViewTransformationAnimation.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
81         mViewTransformationAnimation.addListener(new AnimatorListenerAdapter() {
82             public boolean mCancelled;
83 
84             @Override
85             public void onAnimationEnd(Animator animation) {
86                 if (!mCancelled) {
87                     if (endRunnable != null) {
88                         endRunnable.run();
89                     }
90                     setVisible(false);
91                 } else {
92                     abortTransformations();
93                 }
94             }
95 
96             @Override
97             public void onAnimationCancel(Animator animation) {
98                 mCancelled = true;
99             }
100         });
101         mViewTransformationAnimation.start();
102     }
103 
104     @Override
transformTo(TransformableView notification, float transformationAmount)105     public void transformTo(TransformableView notification, float transformationAmount) {
106         for (Integer viewType : mTransformedViews.keySet()) {
107             TransformState ownState = getCurrentState(viewType);
108             if (ownState != null) {
109                 CustomTransformation customTransformation = mCustomTransformations.get(viewType);
110                 if (customTransformation != null && customTransformation.transformTo(
111                         ownState, notification, transformationAmount)) {
112                     ownState.recycle();
113                     continue;
114                 }
115                 TransformState otherState = notification.getCurrentState(viewType);
116                 if (otherState != null) {
117                     ownState.transformViewTo(otherState, transformationAmount);
118                     otherState.recycle();
119                 } else {
120                     ownState.disappear(transformationAmount, notification);
121                 }
122                 ownState.recycle();
123             }
124         }
125     }
126 
127     @Override
transformFrom(final TransformableView notification)128     public void transformFrom(final TransformableView notification) {
129         if (mViewTransformationAnimation != null) {
130             mViewTransformationAnimation.cancel();
131         }
132         mViewTransformationAnimation = ValueAnimator.ofFloat(0.0f, 1.0f);
133         mViewTransformationAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
134             @Override
135             public void onAnimationUpdate(ValueAnimator animation) {
136                 transformFrom(notification, animation.getAnimatedFraction());
137             }
138         });
139         mViewTransformationAnimation.addListener(new AnimatorListenerAdapter() {
140             public boolean mCancelled;
141 
142             @Override
143             public void onAnimationEnd(Animator animation) {
144                 if (!mCancelled) {
145                     setVisible(true);
146                 } else {
147                     abortTransformations();
148                 }
149             }
150 
151             @Override
152             public void onAnimationCancel(Animator animation) {
153                 mCancelled = true;
154             }
155         });
156         mViewTransformationAnimation.setInterpolator(Interpolators.LINEAR);
157         mViewTransformationAnimation.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
158         mViewTransformationAnimation.start();
159     }
160 
161     @Override
transformFrom(TransformableView notification, float transformationAmount)162     public void transformFrom(TransformableView notification, float transformationAmount) {
163         for (Integer viewType : mTransformedViews.keySet()) {
164             TransformState ownState = getCurrentState(viewType);
165             if (ownState != null) {
166                 CustomTransformation customTransformation = mCustomTransformations.get(viewType);
167                 if (customTransformation != null && customTransformation.transformFrom(
168                         ownState, notification, transformationAmount)) {
169                     ownState.recycle();
170                     continue;
171                 }
172                 TransformState otherState = notification.getCurrentState(viewType);
173                 if (otherState != null) {
174                     ownState.transformViewFrom(otherState, transformationAmount);
175                     otherState.recycle();
176                 } else {
177                     ownState.appear(transformationAmount, notification);
178                 }
179                 ownState.recycle();
180             }
181         }
182     }
183 
184     @Override
setVisible(boolean visible)185     public void setVisible(boolean visible) {
186         if (mViewTransformationAnimation != null) {
187             mViewTransformationAnimation.cancel();
188         }
189         for (Integer viewType : mTransformedViews.keySet()) {
190             TransformState ownState = getCurrentState(viewType);
191             if (ownState != null) {
192                 ownState.setVisible(visible, false /* force */);
193                 ownState.recycle();
194             }
195         }
196     }
197 
abortTransformations()198     private void abortTransformations() {
199         for (Integer viewType : mTransformedViews.keySet()) {
200             TransformState ownState = getCurrentState(viewType);
201             if (ownState != null) {
202                 ownState.abortTransformation();
203                 ownState.recycle();
204             }
205         }
206     }
207 
208     /**
209      * Add the remaining transformation views such that all views are being transformed correctly
210      * @param viewRoot the root below which all elements need to be transformed
211      */
addRemainingTransformTypes(View viewRoot)212     public void addRemainingTransformTypes(View viewRoot) {
213         // lets now tag the right views
214         int numValues = mTransformedViews.size();
215         for (int i = 0; i < numValues; i++) {
216             View view = mTransformedViews.valueAt(i);
217             while (view != viewRoot.getParent()) {
218                 view.setTag(TAG_CONTAINS_TRANSFORMED_VIEW, true);
219                 view = (View) view.getParent();
220             }
221         }
222         Stack<View> stack = new Stack<>();
223         // Add the right views now
224         stack.push(viewRoot);
225         while (!stack.isEmpty()) {
226             View child = stack.pop();
227             Boolean containsView = (Boolean) child.getTag(TAG_CONTAINS_TRANSFORMED_VIEW);
228             if (containsView == null) {
229                 // This one is unhandled, let's add it to our list.
230                 int id = child.getId();
231                 if (id != View.NO_ID) {
232                     // We only fade views with an id
233                     addTransformedView(id, child);
234                     continue;
235                 }
236             }
237             child.setTag(TAG_CONTAINS_TRANSFORMED_VIEW, null);
238             if (child instanceof ViewGroup && !mTransformedViews.containsValue(child)){
239                 ViewGroup group = (ViewGroup) child;
240                 for (int i = 0; i < group.getChildCount(); i++) {
241                     stack.push(group.getChildAt(i));
242                 }
243             }
244         }
245     }
246 
resetTransformedView(View view)247     public void resetTransformedView(View view) {
248         TransformState state = TransformState.createFrom(view);
249         state.setVisible(true /* visible */, true /* force */);
250         state.recycle();
251     }
252 
253     /**
254      * @return a set of all views are being transformed.
255      */
getAllTransformingViews()256     public ArraySet<View> getAllTransformingViews() {
257         return new ArraySet<>(mTransformedViews.values());
258     }
259 
260     public static abstract class CustomTransformation {
261         /**
262          * Transform a state to the given view
263          * @param ownState the state to transform
264          * @param notification the view to transform to
265          * @param transformationAmount how much transformation should be done
266          * @return whether a custom transformation is performed
267          */
transformTo(TransformState ownState, TransformableView notification, float transformationAmount)268         public abstract boolean transformTo(TransformState ownState,
269                 TransformableView notification,
270                 float transformationAmount);
271 
272         /**
273          * Transform to this state from the given view
274          * @param ownState the state to transform to
275          * @param notification the view to transform from
276          * @param transformationAmount how much transformation should be done
277          * @return whether a custom transformation is performed
278          */
transformFrom(TransformState ownState, TransformableView notification, float transformationAmount)279         public abstract boolean transformFrom(TransformState ownState,
280                 TransformableView notification,
281                 float transformationAmount);
282 
283         /**
284          * Perform a custom initialisation before transforming.
285          *
286          * @param ownState our own state
287          * @param otherState the other state
288          * @return whether a custom initialization is done
289          */
initTransformation(TransformState ownState, TransformState otherState)290         public boolean initTransformation(TransformState ownState,
291                 TransformState otherState) {
292             return false;
293         }
294 
customTransformTarget(TransformState ownState, TransformState otherState)295         public boolean customTransformTarget(TransformState ownState,
296                 TransformState otherState) {
297             return false;
298         }
299 
300         /**
301          * Get a custom interpolator for this animation
302          * @param interpolationType the type of the interpolation, i.e TranslationX / TranslationY
303          * @param isFrom true if this transformation from the other view
304          */
getCustomInterpolator(int interpolationType, boolean isFrom)305         public Interpolator getCustomInterpolator(int interpolationType, boolean isFrom) {
306             return null;
307         }
308     }
309 }
310