OSDN Git Service

Merge "Pin compiled code of HOME app"
[android-x86/frameworks-base.git] / packages / SystemUI / src / com / android / systemui / statusbar / notification / stack / ExpandableViewState.java
1 /*
2  * Copyright (C) 2015 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.notification.stack;
18
19 import android.animation.Animator;
20 import android.animation.AnimatorListenerAdapter;
21 import android.animation.PropertyValuesHolder;
22 import android.animation.ValueAnimator;
23 import android.view.View;
24
25 import com.android.systemui.Interpolators;
26 import com.android.systemui.R;
27 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
28 import com.android.systemui.statusbar.notification.row.ExpandableView;
29
30 /**
31 * A state of an expandable view
32 */
33 public class ExpandableViewState extends ViewState {
34
35     private static final int TAG_ANIMATOR_HEIGHT = R.id.height_animator_tag;
36     private static final int TAG_ANIMATOR_TOP_INSET = R.id.top_inset_animator_tag;
37     private static final int TAG_ANIMATOR_SHADOW_ALPHA = R.id.shadow_alpha_animator_tag;
38     private static final int TAG_END_HEIGHT = R.id.height_animator_end_value_tag;
39     private static final int TAG_END_TOP_INSET = R.id.top_inset_animator_end_value_tag;
40     private static final int TAG_END_SHADOW_ALPHA = R.id.shadow_alpha_animator_end_value_tag;
41     private static final int TAG_START_HEIGHT = R.id.height_animator_start_value_tag;
42     private static final int TAG_START_TOP_INSET = R.id.top_inset_animator_start_value_tag;
43     private static final int TAG_START_SHADOW_ALPHA = R.id.shadow_alpha_animator_start_value_tag;
44
45     // These are flags such that we can create masks for filtering.
46
47     /**
48      * No known location. This is the default and should not be set after an invocation of the
49      * algorithm.
50      */
51     public static final int LOCATION_UNKNOWN = 0x00;
52
53     /**
54      * The location is the first heads up notification, so on the very top.
55      */
56     public static final int LOCATION_FIRST_HUN = 0x01;
57
58     /**
59      * The location is hidden / scrolled away on the top.
60      */
61     public static final int LOCATION_HIDDEN_TOP = 0x02;
62
63     /**
64      * The location is in the main area of the screen and visible.
65      */
66     public static final int LOCATION_MAIN_AREA = 0x04;
67
68     /**
69      * The location is in the bottom stack and it's peeking
70      */
71     public static final int LOCATION_BOTTOM_STACK_PEEKING = 0x08;
72
73     /**
74      * The location is in the bottom stack and it's hidden.
75      */
76     public static final int LOCATION_BOTTOM_STACK_HIDDEN = 0x10;
77
78     /**
79      * The view isn't laid out at all.
80      */
81     public static final int LOCATION_GONE = 0x40;
82
83     /**
84      * The visible locations of a view.
85      */
86     public static final int VISIBLE_LOCATIONS = ExpandableViewState.LOCATION_FIRST_HUN
87             | ExpandableViewState.LOCATION_MAIN_AREA;
88
89     public int height;
90     public boolean dimmed;
91     public boolean dark;
92     public boolean hideSensitive;
93     public boolean belowSpeedBump;
94     public float shadowAlpha;
95     public boolean inShelf;
96
97     /**
98      * A state indicating whether a headsup is currently fully visible, even when not scrolled.
99      * Only valid if the view is heads upped.
100      */
101     public boolean headsUpIsVisible;
102
103     /**
104      * How much the child overlaps with the previous child on top. This is used to
105      * show the background properly when the child on top is translating away.
106      */
107     public int clipTopAmount;
108
109     /**
110      * The index of the view, only accounting for views not equal to GONE
111      */
112     public int notGoneIndex;
113
114     /**
115      * The location this view is currently rendered at.
116      *
117      * <p>See <code>LOCATION_</code> flags.</p>
118      */
119     public int location;
120
121     @Override
122     public void copyFrom(ViewState viewState) {
123         super.copyFrom(viewState);
124         if (viewState instanceof ExpandableViewState) {
125             ExpandableViewState svs = (ExpandableViewState) viewState;
126             height = svs.height;
127             dimmed = svs.dimmed;
128             shadowAlpha = svs.shadowAlpha;
129             dark = svs.dark;
130             hideSensitive = svs.hideSensitive;
131             belowSpeedBump = svs.belowSpeedBump;
132             clipTopAmount = svs.clipTopAmount;
133             notGoneIndex = svs.notGoneIndex;
134             location = svs.location;
135             headsUpIsVisible = svs.headsUpIsVisible;
136         }
137     }
138
139     /**
140      * Applies a {@link ExpandableViewState} to a {@link ExpandableView}.
141      */
142     @Override
143     public void applyToView(View view) {
144         super.applyToView(view);
145         if (view instanceof ExpandableView) {
146             ExpandableView expandableView = (ExpandableView) view;
147
148             int height = expandableView.getActualHeight();
149             int newHeight = this.height;
150
151             // apply height
152             if (height != newHeight) {
153                 expandableView.setActualHeight(newHeight, false /* notifyListeners */);
154             }
155
156             float shadowAlpha = expandableView.getShadowAlpha();
157             float newShadowAlpha = this.shadowAlpha;
158
159             // apply shadowAlpha
160             if (shadowAlpha != newShadowAlpha) {
161                 expandableView.setShadowAlpha(newShadowAlpha);
162             }
163
164             // apply dimming
165             expandableView.setDimmed(this.dimmed, false /* animate */);
166
167             // apply hiding sensitive
168             expandableView.setHideSensitive(
169                     this.hideSensitive, false /* animated */, 0 /* delay */, 0 /* duration */);
170
171             // apply below shelf speed bump
172             expandableView.setBelowSpeedBump(this.belowSpeedBump);
173
174             // apply dark
175             expandableView.setDark(this.dark, false /* animate */, 0 /* delay */);
176
177             // apply clipping
178             float oldClipTopAmount = expandableView.getClipTopAmount();
179             if (oldClipTopAmount != this.clipTopAmount) {
180                 expandableView.setClipTopAmount(this.clipTopAmount);
181             }
182
183             expandableView.setTransformingInShelf(false);
184             expandableView.setInShelf(inShelf);
185
186             if (headsUpIsVisible) {
187                 expandableView.setHeadsUpIsVisible();
188             }
189         }
190     }
191
192     @Override
193     public void animateTo(View child, AnimationProperties properties) {
194         super.animateTo(child, properties);
195         if (!(child instanceof ExpandableView)) {
196             return;
197         }
198         ExpandableView expandableView = (ExpandableView) child;
199         AnimationFilter animationFilter = properties.getAnimationFilter();
200
201         // start height animation
202         if (this.height != expandableView.getActualHeight()) {
203             startHeightAnimation(expandableView, properties);
204         }  else {
205             abortAnimation(child, TAG_ANIMATOR_HEIGHT);
206         }
207
208         // start shadow alpha animation
209         if (this.shadowAlpha != expandableView.getShadowAlpha()) {
210             startShadowAlphaAnimation(expandableView, properties);
211         } else {
212             abortAnimation(child, TAG_ANIMATOR_SHADOW_ALPHA);
213         }
214
215         // start top inset animation
216         if (this.clipTopAmount != expandableView.getClipTopAmount()) {
217             startInsetAnimation(expandableView, properties);
218         } else {
219             abortAnimation(child, TAG_ANIMATOR_TOP_INSET);
220         }
221
222         // start dimmed animation
223         expandableView.setDimmed(this.dimmed, animationFilter.animateDimmed);
224
225         // apply below the speed bump
226         expandableView.setBelowSpeedBump(this.belowSpeedBump);
227
228         // start hiding sensitive animation
229         expandableView.setHideSensitive(this.hideSensitive, animationFilter.animateHideSensitive,
230                 properties.delay, properties.duration);
231
232         // start dark animation
233         expandableView.setDark(this.dark, animationFilter.animateDark, properties.delay);
234
235         if (properties.wasAdded(child) && !hidden) {
236             expandableView.performAddAnimation(properties.delay, properties.duration,
237                     false /* isHeadsUpAppear */);
238         }
239
240         if (!expandableView.isInShelf() && this.inShelf) {
241             expandableView.setTransformingInShelf(true);
242         }
243         expandableView.setInShelf(this.inShelf);
244
245         if (headsUpIsVisible) {
246             expandableView.setHeadsUpIsVisible();
247         }
248     }
249
250     private void startHeightAnimation(final ExpandableView child, AnimationProperties properties) {
251         Integer previousStartValue = getChildTag(child, TAG_START_HEIGHT);
252         Integer previousEndValue = getChildTag(child, TAG_END_HEIGHT);
253         int newEndValue = this.height;
254         if (previousEndValue != null && previousEndValue == newEndValue) {
255             return;
256         }
257         ValueAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_HEIGHT);
258         AnimationFilter filter = properties.getAnimationFilter();
259         if (!filter.animateHeight) {
260             // just a local update was performed
261             if (previousAnimator != null) {
262                 // we need to increase all animation keyframes of the previous animator by the
263                 // relative change to the end value
264                 PropertyValuesHolder[] values = previousAnimator.getValues();
265                 int relativeDiff = newEndValue - previousEndValue;
266                 int newStartValue = previousStartValue + relativeDiff;
267                 values[0].setIntValues(newStartValue, newEndValue);
268                 child.setTag(TAG_START_HEIGHT, newStartValue);
269                 child.setTag(TAG_END_HEIGHT, newEndValue);
270                 previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
271                 return;
272             } else {
273                 // no new animation needed, let's just apply the value
274                 child.setActualHeight(newEndValue, false);
275                 return;
276             }
277         }
278
279         ValueAnimator animator = ValueAnimator.ofInt(child.getActualHeight(), newEndValue);
280         animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
281             @Override
282             public void onAnimationUpdate(ValueAnimator animation) {
283                 child.setActualHeight((int) animation.getAnimatedValue(),
284                         false /* notifyListeners */);
285             }
286         });
287         animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
288         long newDuration = cancelAnimatorAndGetNewDuration(properties.duration, previousAnimator);
289         animator.setDuration(newDuration);
290         if (properties.delay > 0 && (previousAnimator == null
291                 || previousAnimator.getAnimatedFraction() == 0)) {
292             animator.setStartDelay(properties.delay);
293         }
294         AnimatorListenerAdapter listener = properties.getAnimationFinishListener();
295         if (listener != null) {
296             animator.addListener(listener);
297         }
298         // remove the tag when the animation is finished
299         animator.addListener(new AnimatorListenerAdapter() {
300             boolean mWasCancelled;
301
302             @Override
303             public void onAnimationEnd(Animator animation) {
304                 child.setTag(TAG_ANIMATOR_HEIGHT, null);
305                 child.setTag(TAG_START_HEIGHT, null);
306                 child.setTag(TAG_END_HEIGHT, null);
307                 child.setActualHeightAnimating(false);
308                 if (!mWasCancelled && child instanceof ExpandableNotificationRow) {
309                     ((ExpandableNotificationRow) child).setGroupExpansionChanging(
310                             false /* isExpansionChanging */);
311                 }
312             }
313
314             @Override
315             public void onAnimationStart(Animator animation) {
316                 mWasCancelled = false;
317             }
318
319             @Override
320             public void onAnimationCancel(Animator animation) {
321                 mWasCancelled = true;
322             }
323         });
324         startAnimator(animator, listener);
325         child.setTag(TAG_ANIMATOR_HEIGHT, animator);
326         child.setTag(TAG_START_HEIGHT, child.getActualHeight());
327         child.setTag(TAG_END_HEIGHT, newEndValue);
328         child.setActualHeightAnimating(true);
329     }
330
331     private void startShadowAlphaAnimation(final ExpandableView child,
332             AnimationProperties properties) {
333         Float previousStartValue = getChildTag(child, TAG_START_SHADOW_ALPHA);
334         Float previousEndValue = getChildTag(child, TAG_END_SHADOW_ALPHA);
335         float newEndValue = this.shadowAlpha;
336         if (previousEndValue != null && previousEndValue == newEndValue) {
337             return;
338         }
339         ValueAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_SHADOW_ALPHA);
340         AnimationFilter filter = properties.getAnimationFilter();
341         if (!filter.animateShadowAlpha) {
342             // just a local update was performed
343             if (previousAnimator != null) {
344                 // we need to increase all animation keyframes of the previous animator by the
345                 // relative change to the end value
346                 PropertyValuesHolder[] values = previousAnimator.getValues();
347                 float relativeDiff = newEndValue - previousEndValue;
348                 float newStartValue = previousStartValue + relativeDiff;
349                 values[0].setFloatValues(newStartValue, newEndValue);
350                 child.setTag(TAG_START_SHADOW_ALPHA, newStartValue);
351                 child.setTag(TAG_END_SHADOW_ALPHA, newEndValue);
352                 previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
353                 return;
354             } else {
355                 // no new animation needed, let's just apply the value
356                 child.setShadowAlpha(newEndValue);
357                 return;
358             }
359         }
360
361         ValueAnimator animator = ValueAnimator.ofFloat(child.getShadowAlpha(), newEndValue);
362         animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
363             @Override
364             public void onAnimationUpdate(ValueAnimator animation) {
365                 child.setShadowAlpha((float) animation.getAnimatedValue());
366             }
367         });
368         animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
369         long newDuration = cancelAnimatorAndGetNewDuration(properties.duration, previousAnimator);
370         animator.setDuration(newDuration);
371         if (properties.delay > 0 && (previousAnimator == null
372                 || previousAnimator.getAnimatedFraction() == 0)) {
373             animator.setStartDelay(properties.delay);
374         }
375         AnimatorListenerAdapter listener = properties.getAnimationFinishListener();
376         if (listener != null) {
377             animator.addListener(listener);
378         }
379         // remove the tag when the animation is finished
380         animator.addListener(new AnimatorListenerAdapter() {
381             @Override
382             public void onAnimationEnd(Animator animation) {
383                 child.setTag(TAG_ANIMATOR_SHADOW_ALPHA, null);
384                 child.setTag(TAG_START_SHADOW_ALPHA, null);
385                 child.setTag(TAG_END_SHADOW_ALPHA, null);
386             }
387         });
388         startAnimator(animator, listener);
389         child.setTag(TAG_ANIMATOR_SHADOW_ALPHA, animator);
390         child.setTag(TAG_START_SHADOW_ALPHA, child.getShadowAlpha());
391         child.setTag(TAG_END_SHADOW_ALPHA, newEndValue);
392     }
393
394     private void startInsetAnimation(final ExpandableView child, AnimationProperties properties) {
395         Integer previousStartValue = getChildTag(child, TAG_START_TOP_INSET);
396         Integer previousEndValue = getChildTag(child, TAG_END_TOP_INSET);
397         int newEndValue = this.clipTopAmount;
398         if (previousEndValue != null && previousEndValue == newEndValue) {
399             return;
400         }
401         ValueAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TOP_INSET);
402         AnimationFilter filter = properties.getAnimationFilter();
403         if (!filter.animateTopInset) {
404             // just a local update was performed
405             if (previousAnimator != null) {
406                 // we need to increase all animation keyframes of the previous animator by the
407                 // relative change to the end value
408                 PropertyValuesHolder[] values = previousAnimator.getValues();
409                 int relativeDiff = newEndValue - previousEndValue;
410                 int newStartValue = previousStartValue + relativeDiff;
411                 values[0].setIntValues(newStartValue, newEndValue);
412                 child.setTag(TAG_START_TOP_INSET, newStartValue);
413                 child.setTag(TAG_END_TOP_INSET, newEndValue);
414                 previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
415                 return;
416             } else {
417                 // no new animation needed, let's just apply the value
418                 child.setClipTopAmount(newEndValue);
419                 return;
420             }
421         }
422
423         ValueAnimator animator = ValueAnimator.ofInt(child.getClipTopAmount(), newEndValue);
424         animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
425             @Override
426             public void onAnimationUpdate(ValueAnimator animation) {
427                 child.setClipTopAmount((int) animation.getAnimatedValue());
428             }
429         });
430         animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
431         long newDuration = cancelAnimatorAndGetNewDuration(properties.duration, previousAnimator);
432         animator.setDuration(newDuration);
433         if (properties.delay > 0 && (previousAnimator == null
434                 || previousAnimator.getAnimatedFraction() == 0)) {
435             animator.setStartDelay(properties.delay);
436         }
437         AnimatorListenerAdapter listener = properties.getAnimationFinishListener();
438         if (listener != null) {
439             animator.addListener(listener);
440         }
441         // remove the tag when the animation is finished
442         animator.addListener(new AnimatorListenerAdapter() {
443             @Override
444             public void onAnimationEnd(Animator animation) {
445                 child.setTag(TAG_ANIMATOR_TOP_INSET, null);
446                 child.setTag(TAG_START_TOP_INSET, null);
447                 child.setTag(TAG_END_TOP_INSET, null);
448             }
449         });
450         startAnimator(animator, listener);
451         child.setTag(TAG_ANIMATOR_TOP_INSET, animator);
452         child.setTag(TAG_START_TOP_INSET, child.getClipTopAmount());
453         child.setTag(TAG_END_TOP_INSET, newEndValue);
454     }
455
456     /**
457      * Get the end value of the height animation running on a view or the actualHeight
458      * if no animation is running.
459      */
460     public static int getFinalActualHeight(ExpandableView view) {
461         if (view == null) {
462             return 0;
463         }
464         ValueAnimator heightAnimator = getChildTag(view, TAG_ANIMATOR_HEIGHT);
465         if (heightAnimator == null) {
466             return view.getActualHeight();
467         } else {
468             return getChildTag(view, TAG_END_HEIGHT);
469         }
470     }
471
472     @Override
473     public void cancelAnimations(View view) {
474         super.cancelAnimations(view);
475         Animator animator = getChildTag(view, TAG_ANIMATOR_HEIGHT);
476         if (animator != null) {
477             animator.cancel();
478         }
479         animator = getChildTag(view, TAG_ANIMATOR_SHADOW_ALPHA);
480         if (animator != null) {
481             animator.cancel();
482         }
483         animator = getChildTag(view, TAG_ANIMATOR_TOP_INSET);
484         if (animator != null) {
485             animator.cancel();
486         }
487     }
488 }