blob: 04a7bd79c6ca35ece62ae62eac30d5b079d7dc7f [file] [log] [blame]
Selim Cinekb036ca42015-02-20 15:56:28 +01001/*
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
17package com.android.systemui.statusbar.stack;
18
Selim Cinek0cfbef42016-11-09 19:06:36 -080019import android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
21import android.animation.ObjectAnimator;
22import android.animation.PropertyValuesHolder;
23import android.animation.ValueAnimator;
Selim Cinekf082fe22016-12-20 14:32:19 +010024import android.util.Property;
Selim Cinekb036ca42015-02-20 15:56:28 +010025import android.view.View;
Selim Cinek0cfbef42016-11-09 19:06:36 -080026import android.view.animation.Interpolator;
Selim Cinekb036ca42015-02-20 15:56:28 +010027
Selim Cinek0cfbef42016-11-09 19:06:36 -080028import com.android.systemui.Interpolators;
29import com.android.systemui.R;
Selim Cinekbbcebde2016-11-09 18:28:20 -080030import com.android.systemui.statusbar.ExpandableView;
Selim Cinek0228a252017-11-10 17:47:27 -080031import com.android.systemui.statusbar.notification.AnimatableProperty;
Selim Cinekf082fe22016-12-20 14:32:19 +010032import com.android.systemui.statusbar.notification.PropertyAnimator;
yoshiki iguchi4e30e762018-02-06 12:09:23 +090033import com.android.systemui.statusbar.policy.HeadsUpUtil;
Selim Cinekbbcebde2016-11-09 18:28:20 -080034
Selim Cinekb036ca42015-02-20 15:56:28 +010035/**
36 * A state of a view. This can be used to apply a set of view properties to a view with
37 * {@link com.android.systemui.statusbar.stack.StackScrollState} or start animations with
38 * {@link com.android.systemui.statusbar.stack.StackStateAnimator}.
39*/
40public class ViewState {
41
Selim Cinekc40c79a2016-11-08 09:52:52 -080042 /**
43 * Some animation properties that can be used to update running animations but not creating
44 * any new ones.
45 */
46 protected static final AnimationProperties NO_NEW_ANIMATIONS = new AnimationProperties() {
47 AnimationFilter mAnimationFilter = new AnimationFilter();
48 @Override
49 public AnimationFilter getAnimationFilter() {
50 return mAnimationFilter;
51 }
52 };
53 private static final int TAG_ANIMATOR_TRANSLATION_X = R.id.translation_x_animator_tag;
Selim Cinek0cfbef42016-11-09 19:06:36 -080054 private static final int TAG_ANIMATOR_TRANSLATION_Y = R.id.translation_y_animator_tag;
55 private static final int TAG_ANIMATOR_TRANSLATION_Z = R.id.translation_z_animator_tag;
56 private static final int TAG_ANIMATOR_ALPHA = R.id.alpha_animator_tag;
Selim Cinekc40c79a2016-11-08 09:52:52 -080057 private static final int TAG_END_TRANSLATION_X = R.id.translation_x_animator_end_value_tag;
Selim Cinek0cfbef42016-11-09 19:06:36 -080058 private static final int TAG_END_TRANSLATION_Y = R.id.translation_y_animator_end_value_tag;
59 private static final int TAG_END_TRANSLATION_Z = R.id.translation_z_animator_end_value_tag;
60 private static final int TAG_END_ALPHA = R.id.alpha_animator_end_value_tag;
Selim Cinekc40c79a2016-11-08 09:52:52 -080061 private static final int TAG_START_TRANSLATION_X = R.id.translation_x_animator_start_value_tag;
Selim Cinek0cfbef42016-11-09 19:06:36 -080062 private static final int TAG_START_TRANSLATION_Y = R.id.translation_y_animator_start_value_tag;
63 private static final int TAG_START_TRANSLATION_Z = R.id.translation_z_animator_start_value_tag;
64 private static final int TAG_START_ALPHA = R.id.alpha_animator_start_value_tag;
65
Selim Cinek0228a252017-11-10 17:47:27 -080066 private static final AnimatableProperty SCALE_X_PROPERTY
67 = new AnimatableProperty() {
Selim Cinekf082fe22016-12-20 14:32:19 +010068
69 @Override
70 public int getAnimationStartTag() {
71 return R.id.scale_x_animator_start_value_tag;
72 }
73
74 @Override
75 public int getAnimationEndTag() {
76 return R.id.scale_x_animator_end_value_tag;
77 }
78
79 @Override
80 public int getAnimatorTag() {
81 return R.id.scale_x_animator_tag;
82 }
83
84 @Override
85 public Property getProperty() {
86 return View.SCALE_X;
87 }
88 };
89
Selim Cinek0228a252017-11-10 17:47:27 -080090 private static final AnimatableProperty SCALE_Y_PROPERTY
91 = new AnimatableProperty() {
Selim Cinekf082fe22016-12-20 14:32:19 +010092
93 @Override
94 public int getAnimationStartTag() {
95 return R.id.scale_y_animator_start_value_tag;
96 }
97
98 @Override
99 public int getAnimationEndTag() {
100 return R.id.scale_y_animator_end_value_tag;
101 }
102
103 @Override
104 public int getAnimatorTag() {
105 return R.id.scale_y_animator_tag;
106 }
107
108 @Override
109 public Property getProperty() {
110 return View.SCALE_Y;
111 }
112 };
113
Selim Cinekb036ca42015-02-20 15:56:28 +0100114 public float alpha;
Selim Cinek281c2022016-10-13 19:14:43 -0700115 public float xTranslation;
Selim Cinekb036ca42015-02-20 15:56:28 +0100116 public float yTranslation;
117 public float zTranslation;
118 public boolean gone;
Selim Cinek277a8aa2016-01-22 12:12:37 -0800119 public boolean hidden;
Selim Cinek281c2022016-10-13 19:14:43 -0700120 public float scaleX = 1.0f;
121 public float scaleY = 1.0f;
Selim Cinekb036ca42015-02-20 15:56:28 +0100122
123 public void copyFrom(ViewState viewState) {
124 alpha = viewState.alpha;
Selim Cinek281c2022016-10-13 19:14:43 -0700125 xTranslation = viewState.xTranslation;
Selim Cinekb036ca42015-02-20 15:56:28 +0100126 yTranslation = viewState.yTranslation;
127 zTranslation = viewState.zTranslation;
128 gone = viewState.gone;
Selim Cinek277a8aa2016-01-22 12:12:37 -0800129 hidden = viewState.hidden;
Selim Cinek281c2022016-10-13 19:14:43 -0700130 scaleX = viewState.scaleX;
131 scaleY = viewState.scaleY;
Selim Cinekb036ca42015-02-20 15:56:28 +0100132 }
133
134 public void initFrom(View view) {
Selim Cinek277a8aa2016-01-22 12:12:37 -0800135 alpha = view.getAlpha();
Selim Cinek281c2022016-10-13 19:14:43 -0700136 xTranslation = view.getTranslationX();
Selim Cinekb036ca42015-02-20 15:56:28 +0100137 yTranslation = view.getTranslationY();
138 zTranslation = view.getTranslationZ();
139 gone = view.getVisibility() == View.GONE;
Selim Cinek414ad332017-02-24 19:06:12 -0800140 hidden = view.getVisibility() == View.INVISIBLE;
Selim Cinek281c2022016-10-13 19:14:43 -0700141 scaleX = view.getScaleX();
142 scaleY = view.getScaleY();
Selim Cinekb036ca42015-02-20 15:56:28 +0100143 }
Selim Cinekbbcebde2016-11-09 18:28:20 -0800144
145 /**
146 * Applies a {@link ViewState} to a normal view.
147 */
148 public void applyToView(View view) {
149 if (this.gone) {
150 // don't do anything with it
151 return;
152 }
Selim Cinekbbcebde2016-11-09 18:28:20 -0800153
Selim Cinek281c2022016-10-13 19:14:43 -0700154 // apply xTranslation
Selim Cinekc40c79a2016-11-08 09:52:52 -0800155 boolean animatingX = isAnimating(view, TAG_ANIMATOR_TRANSLATION_X);
156 if (animatingX) {
157 updateAnimationX(view);
158 } else if (view.getTranslationX() != this.xTranslation){
Selim Cinek281c2022016-10-13 19:14:43 -0700159 view.setTranslationX(this.xTranslation);
160 }
161
Selim Cinekbbcebde2016-11-09 18:28:20 -0800162 // apply yTranslation
Selim Cinekc40c79a2016-11-08 09:52:52 -0800163 boolean animatingY = isAnimating(view, TAG_ANIMATOR_TRANSLATION_Y);
164 if (animatingY) {
165 updateAnimationY(view);
166 } else if (view.getTranslationY() != this.yTranslation) {
Selim Cinek281c2022016-10-13 19:14:43 -0700167 view.setTranslationY(this.yTranslation);
Selim Cinekbbcebde2016-11-09 18:28:20 -0800168 }
169
170 // apply zTranslation
Selim Cinekc40c79a2016-11-08 09:52:52 -0800171 boolean animatingZ = isAnimating(view, TAG_ANIMATOR_TRANSLATION_Z);
172 if (animatingZ) {
173 updateAnimationZ(view);
174 } else if (view.getTranslationZ() != this.zTranslation) {
Selim Cinek281c2022016-10-13 19:14:43 -0700175 view.setTranslationZ(this.zTranslation);
176 }
177
178 // apply scaleX
Selim Cinekf082fe22016-12-20 14:32:19 +0100179 boolean animatingScaleX = isAnimating(view, SCALE_X_PROPERTY);
180 if (animatingScaleX) {
181 updateAnimation(view, SCALE_X_PROPERTY, scaleX);
182 } else if (view.getScaleX() != scaleX) {
183 view.setScaleX(scaleX);
Selim Cinek281c2022016-10-13 19:14:43 -0700184 }
185
186 // apply scaleY
Selim Cinekf082fe22016-12-20 14:32:19 +0100187 boolean animatingScaleY = isAnimating(view, SCALE_Y_PROPERTY);
188 if (animatingScaleY) {
189 updateAnimation(view, SCALE_Y_PROPERTY, scaleY);
190 } else if (view.getScaleY() != scaleY) {
191 view.setScaleY(scaleY);
Selim Cinekbbcebde2016-11-09 18:28:20 -0800192 }
Selim Cinek2b549f42016-11-22 16:38:51 -0800193
Selim Cinek47374632017-03-17 16:07:17 -0700194 int oldVisibility = view.getVisibility();
195 boolean becomesInvisible = this.alpha == 0.0f
196 || (this.hidden && (!isAnimating(view) || oldVisibility != View.VISIBLE));
Selim Cinek2b549f42016-11-22 16:38:51 -0800197 boolean animatingAlpha = isAnimating(view, TAG_ANIMATOR_ALPHA);
198 if (animatingAlpha) {
199 updateAlphaAnimation(view);
200 } else if (view.getAlpha() != this.alpha) {
201 // apply layer type
202 boolean becomesFullyVisible = this.alpha == 1.0f;
203 boolean newLayerTypeIsHardware = !becomesInvisible && !becomesFullyVisible
204 && view.hasOverlappingRendering();
205 int layerType = view.getLayerType();
206 int newLayerType = newLayerTypeIsHardware
207 ? View.LAYER_TYPE_HARDWARE
208 : View.LAYER_TYPE_NONE;
209 if (layerType != newLayerType) {
210 view.setLayerType(newLayerType, null);
211 }
212
213 // apply alpha
214 view.setAlpha(this.alpha);
215 }
216
217 // apply visibility
Selim Cinek2b549f42016-11-22 16:38:51 -0800218 int newVisibility = becomesInvisible ? View.INVISIBLE : View.VISIBLE;
219 if (newVisibility != oldVisibility) {
220 if (!(view instanceof ExpandableView) || !((ExpandableView) view).willBeGone()) {
221 // We don't want views to change visibility when they are animating to GONE
222 view.setVisibility(newVisibility);
223 }
224 }
225 }
226
Adrian Roos28f90c72017-05-08 17:24:26 -0700227 public boolean isAnimating(View view) {
Selim Cinek2b549f42016-11-22 16:38:51 -0800228 if (isAnimating(view, TAG_ANIMATOR_TRANSLATION_X)) {
229 return true;
230 }
231 if (isAnimating(view, TAG_ANIMATOR_TRANSLATION_Y)) {
232 return true;
233 }
234 if (isAnimating(view, TAG_ANIMATOR_TRANSLATION_Z)) {
235 return true;
236 }
237 if (isAnimating(view, TAG_ANIMATOR_ALPHA)) {
238 return true;
239 }
Selim Cinekf082fe22016-12-20 14:32:19 +0100240 if (isAnimating(view, SCALE_X_PROPERTY)) {
241 return true;
242 }
243 if (isAnimating(view, SCALE_Y_PROPERTY)) {
244 return true;
245 }
Selim Cinek2b549f42016-11-22 16:38:51 -0800246 return false;
Selim Cinekbbcebde2016-11-09 18:28:20 -0800247 }
Selim Cinek0cfbef42016-11-09 19:06:36 -0800248
Selim Cinekf082fe22016-12-20 14:32:19 +0100249 private static boolean isAnimating(View view, int tag) {
Selim Cinekc40c79a2016-11-08 09:52:52 -0800250 return getChildTag(view, tag) != null;
251 }
252
Selim Cinek0228a252017-11-10 17:47:27 -0800253 public static boolean isAnimating(View view, AnimatableProperty property) {
Selim Cinekf082fe22016-12-20 14:32:19 +0100254 return getChildTag(view, property.getAnimatorTag()) != null;
255 }
256
Selim Cinek0cfbef42016-11-09 19:06:36 -0800257 /**
258 * Start an animation to this viewstate
259 * @param child the view to animate
260 * @param animationProperties the properties of the animation
261 */
262 public void animateTo(View child, AnimationProperties animationProperties) {
263 boolean wasVisible = child.getVisibility() == View.VISIBLE;
264 final float alpha = this.alpha;
265 if (!wasVisible && (alpha != 0 || child.getAlpha() != 0)
266 && !this.gone && !this.hidden) {
267 child.setVisibility(View.VISIBLE);
268 }
Selim Cinek0cfbef42016-11-09 19:06:36 -0800269 float childAlpha = child.getAlpha();
270 boolean alphaChanging = this.alpha != childAlpha;
271 if (child instanceof ExpandableView) {
272 // We don't want views to change visibility when they are animating to GONE
273 alphaChanging &= !((ExpandableView) child).willBeGone();
274 }
275
Selim Cinekc40c79a2016-11-08 09:52:52 -0800276 // start translationX animation
277 if (child.getTranslationX() != this.xTranslation) {
278 startXTranslationAnimation(child, animationProperties);
279 } else {
280 abortAnimation(child, TAG_ANIMATOR_TRANSLATION_X);
281 }
282
Selim Cinek0cfbef42016-11-09 19:06:36 -0800283 // start translationY animation
Selim Cinekc40c79a2016-11-08 09:52:52 -0800284 if (child.getTranslationY() != this.yTranslation) {
Selim Cinek0cfbef42016-11-09 19:06:36 -0800285 startYTranslationAnimation(child, animationProperties);
286 } else {
287 abortAnimation(child, TAG_ANIMATOR_TRANSLATION_Y);
288 }
289
290 // start translationZ animation
Selim Cinekc40c79a2016-11-08 09:52:52 -0800291 if (child.getTranslationZ() != this.zTranslation) {
Selim Cinek0cfbef42016-11-09 19:06:36 -0800292 startZTranslationAnimation(child, animationProperties);
293 } else {
294 abortAnimation(child, TAG_ANIMATOR_TRANSLATION_Z);
295 }
296
Selim Cinekf082fe22016-12-20 14:32:19 +0100297 // start scaleX animation
298 if (child.getScaleX() != scaleX) {
299 PropertyAnimator.startAnimation(child, SCALE_X_PROPERTY, scaleX, animationProperties);
300 } else {
301 abortAnimation(child, SCALE_X_PROPERTY.getAnimatorTag());
302 }
303
304 // start scaleX animation
305 if (child.getScaleY() != scaleY) {
306 PropertyAnimator.startAnimation(child, SCALE_Y_PROPERTY, scaleY, animationProperties);
307 } else {
308 abortAnimation(child, SCALE_Y_PROPERTY.getAnimatorTag());
309 }
310
Selim Cinek0cfbef42016-11-09 19:06:36 -0800311 // start alpha animation
Selim Cinek5b5beb012016-11-08 18:11:58 -0800312 if (alphaChanging) {
Selim Cinek0cfbef42016-11-09 19:06:36 -0800313 startAlphaAnimation(child, animationProperties);
314 } else {
315 abortAnimation(child, TAG_ANIMATOR_ALPHA);
316 }
317 }
318
Selim Cinekc40c79a2016-11-08 09:52:52 -0800319 private void updateAlphaAnimation(View view) {
320 startAlphaAnimation(view, NO_NEW_ANIMATIONS);
321 }
322
Selim Cinek0cfbef42016-11-09 19:06:36 -0800323 private void startAlphaAnimation(final View child, AnimationProperties properties) {
324 Float previousStartValue = getChildTag(child,TAG_START_ALPHA);
325 Float previousEndValue = getChildTag(child,TAG_END_ALPHA);
326 final float newEndValue = this.alpha;
327 if (previousEndValue != null && previousEndValue == newEndValue) {
328 return;
329 }
330 ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_ALPHA);
331 AnimationFilter filter = properties.getAnimationFilter();
332 if (!filter.animateAlpha) {
333 // just a local update was performed
334 if (previousAnimator != null) {
335 // we need to increase all animation keyframes of the previous animator by the
336 // relative change to the end value
337 PropertyValuesHolder[] values = previousAnimator.getValues();
338 float relativeDiff = newEndValue - previousEndValue;
339 float newStartValue = previousStartValue + relativeDiff;
340 values[0].setFloatValues(newStartValue, newEndValue);
341 child.setTag(TAG_START_ALPHA, newStartValue);
342 child.setTag(TAG_END_ALPHA, newEndValue);
343 previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
344 return;
345 } else {
346 // no new animation needed, let's just apply the value
347 child.setAlpha(newEndValue);
348 if (newEndValue == 0) {
349 child.setVisibility(View.INVISIBLE);
350 }
351 }
352 }
353
354 ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.ALPHA,
355 child.getAlpha(), newEndValue);
356 animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
357 // Handle layer type
358 child.setLayerType(View.LAYER_TYPE_HARDWARE, null);
359 animator.addListener(new AnimatorListenerAdapter() {
360 public boolean mWasCancelled;
361
362 @Override
363 public void onAnimationEnd(Animator animation) {
364 child.setLayerType(View.LAYER_TYPE_NONE, null);
365 if (newEndValue == 0 && !mWasCancelled) {
366 child.setVisibility(View.INVISIBLE);
367 }
368 // remove the tag when the animation is finished
369 child.setTag(TAG_ANIMATOR_ALPHA, null);
370 child.setTag(TAG_START_ALPHA, null);
371 child.setTag(TAG_END_ALPHA, null);
372 }
373
374 @Override
375 public void onAnimationCancel(Animator animation) {
376 mWasCancelled = true;
377 }
378
379 @Override
380 public void onAnimationStart(Animator animation) {
381 mWasCancelled = false;
382 }
383 });
384 long newDuration = cancelAnimatorAndGetNewDuration(properties.duration, previousAnimator);
385 animator.setDuration(newDuration);
386 if (properties.delay > 0 && (previousAnimator == null
387 || previousAnimator.getAnimatedFraction() == 0)) {
388 animator.setStartDelay(properties.delay);
389 }
390 AnimatorListenerAdapter listener = properties.getAnimationFinishListener();
391 if (listener != null) {
392 animator.addListener(listener);
393 }
394
395 startAnimator(animator, listener);
396 child.setTag(TAG_ANIMATOR_ALPHA, animator);
397 child.setTag(TAG_START_ALPHA, child.getAlpha());
398 child.setTag(TAG_END_ALPHA, newEndValue);
399 }
400
Selim Cinekc40c79a2016-11-08 09:52:52 -0800401 private void updateAnimationZ(View view) {
402 startZTranslationAnimation(view, NO_NEW_ANIMATIONS);
403 }
404
Selim Cinek0228a252017-11-10 17:47:27 -0800405 private void updateAnimation(View view, AnimatableProperty property,
Selim Cinekf082fe22016-12-20 14:32:19 +0100406 float endValue) {
407 PropertyAnimator.startAnimation(view, property, endValue, NO_NEW_ANIMATIONS);
408 }
409
Selim Cinek0cfbef42016-11-09 19:06:36 -0800410 private void startZTranslationAnimation(final View child, AnimationProperties properties) {
411 Float previousStartValue = getChildTag(child,TAG_START_TRANSLATION_Z);
412 Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Z);
413 float newEndValue = this.zTranslation;
414 if (previousEndValue != null && previousEndValue == newEndValue) {
415 return;
416 }
417 ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TRANSLATION_Z);
418 AnimationFilter filter = properties.getAnimationFilter();
419 if (!filter.animateZ) {
420 // just a local update was performed
421 if (previousAnimator != null) {
422 // we need to increase all animation keyframes of the previous animator by the
423 // relative change to the end value
424 PropertyValuesHolder[] values = previousAnimator.getValues();
425 float relativeDiff = newEndValue - previousEndValue;
426 float newStartValue = previousStartValue + relativeDiff;
427 values[0].setFloatValues(newStartValue, newEndValue);
428 child.setTag(TAG_START_TRANSLATION_Z, newStartValue);
429 child.setTag(TAG_END_TRANSLATION_Z, newEndValue);
430 previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
431 return;
432 } else {
433 // no new animation needed, let's just apply the value
434 child.setTranslationZ(newEndValue);
435 }
436 }
437
438 ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Z,
439 child.getTranslationZ(), newEndValue);
440 animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
441 long newDuration = cancelAnimatorAndGetNewDuration(properties.duration, previousAnimator);
442 animator.setDuration(newDuration);
443 if (properties.delay > 0 && (previousAnimator == null
444 || previousAnimator.getAnimatedFraction() == 0)) {
445 animator.setStartDelay(properties.delay);
446 }
447 AnimatorListenerAdapter listener = properties.getAnimationFinishListener();
448 if (listener != null) {
449 animator.addListener(listener);
450 }
451 // remove the tag when the animation is finished
452 animator.addListener(new AnimatorListenerAdapter() {
453 @Override
454 public void onAnimationEnd(Animator animation) {
455 child.setTag(TAG_ANIMATOR_TRANSLATION_Z, null);
456 child.setTag(TAG_START_TRANSLATION_Z, null);
457 child.setTag(TAG_END_TRANSLATION_Z, null);
458 }
459 });
460 startAnimator(animator, listener);
461 child.setTag(TAG_ANIMATOR_TRANSLATION_Z, animator);
462 child.setTag(TAG_START_TRANSLATION_Z, child.getTranslationZ());
463 child.setTag(TAG_END_TRANSLATION_Z, newEndValue);
464 }
465
Selim Cinekc40c79a2016-11-08 09:52:52 -0800466 private void updateAnimationX(View view) {
467 startXTranslationAnimation(view, NO_NEW_ANIMATIONS);
468 }
469
470 private void startXTranslationAnimation(final View child, AnimationProperties properties) {
471 Float previousStartValue = getChildTag(child,TAG_START_TRANSLATION_X);
472 Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_X);
473 float newEndValue = this.xTranslation;
474 if (previousEndValue != null && previousEndValue == newEndValue) {
475 return;
476 }
477 ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TRANSLATION_X);
478 AnimationFilter filter = properties.getAnimationFilter();
479 if (!filter.animateX) {
480 // just a local update was performed
481 if (previousAnimator != null) {
482 // we need to increase all animation keyframes of the previous animator by the
483 // relative change to the end value
484 PropertyValuesHolder[] values = previousAnimator.getValues();
485 float relativeDiff = newEndValue - previousEndValue;
486 float newStartValue = previousStartValue + relativeDiff;
487 values[0].setFloatValues(newStartValue, newEndValue);
488 child.setTag(TAG_START_TRANSLATION_X, newStartValue);
489 child.setTag(TAG_END_TRANSLATION_X, newEndValue);
490 previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
491 return;
492 } else {
493 // no new animation needed, let's just apply the value
494 child.setTranslationX(newEndValue);
495 return;
496 }
497 }
498
499 ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_X,
500 child.getTranslationX(), newEndValue);
501 Interpolator customInterpolator = properties.getCustomInterpolator(child,
502 View.TRANSLATION_X);
503 Interpolator interpolator = customInterpolator != null ? customInterpolator
504 : Interpolators.FAST_OUT_SLOW_IN;
505 animator.setInterpolator(interpolator);
506 long newDuration = cancelAnimatorAndGetNewDuration(properties.duration, previousAnimator);
507 animator.setDuration(newDuration);
508 if (properties.delay > 0 && (previousAnimator == null
509 || previousAnimator.getAnimatedFraction() == 0)) {
510 animator.setStartDelay(properties.delay);
511 }
512 AnimatorListenerAdapter listener = properties.getAnimationFinishListener();
513 if (listener != null) {
514 animator.addListener(listener);
515 }
516 // remove the tag when the animation is finished
517 animator.addListener(new AnimatorListenerAdapter() {
518 @Override
519 public void onAnimationEnd(Animator animation) {
520 child.setTag(TAG_ANIMATOR_TRANSLATION_X, null);
521 child.setTag(TAG_START_TRANSLATION_X, null);
522 child.setTag(TAG_END_TRANSLATION_X, null);
523 }
524 });
525 startAnimator(animator, listener);
526 child.setTag(TAG_ANIMATOR_TRANSLATION_X, animator);
527 child.setTag(TAG_START_TRANSLATION_X, child.getTranslationX());
528 child.setTag(TAG_END_TRANSLATION_X, newEndValue);
529 }
530
531 private void updateAnimationY(View view) {
532 startYTranslationAnimation(view, NO_NEW_ANIMATIONS);
533 }
534
Selim Cinek0cfbef42016-11-09 19:06:36 -0800535 private void startYTranslationAnimation(final View child, AnimationProperties properties) {
536 Float previousStartValue = getChildTag(child,TAG_START_TRANSLATION_Y);
537 Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Y);
538 float newEndValue = this.yTranslation;
539 if (previousEndValue != null && previousEndValue == newEndValue) {
540 return;
541 }
542 ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TRANSLATION_Y);
543 AnimationFilter filter = properties.getAnimationFilter();
Adrian Roos28f90c72017-05-08 17:24:26 -0700544 if (!filter.shouldAnimateY(child)) {
Selim Cinek0cfbef42016-11-09 19:06:36 -0800545 // just a local update was performed
546 if (previousAnimator != null) {
547 // we need to increase all animation keyframes of the previous animator by the
548 // relative change to the end value
549 PropertyValuesHolder[] values = previousAnimator.getValues();
550 float relativeDiff = newEndValue - previousEndValue;
551 float newStartValue = previousStartValue + relativeDiff;
552 values[0].setFloatValues(newStartValue, newEndValue);
553 child.setTag(TAG_START_TRANSLATION_Y, newStartValue);
554 child.setTag(TAG_END_TRANSLATION_Y, newEndValue);
555 previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
556 return;
557 } else {
558 // no new animation needed, let's just apply the value
559 child.setTranslationY(newEndValue);
560 return;
561 }
562 }
563
564 ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Y,
565 child.getTranslationY(), newEndValue);
566 Interpolator customInterpolator = properties.getCustomInterpolator(child,
567 View.TRANSLATION_Y);
568 Interpolator interpolator = customInterpolator != null ? customInterpolator
569 : Interpolators.FAST_OUT_SLOW_IN;
570 animator.setInterpolator(interpolator);
571 long newDuration = cancelAnimatorAndGetNewDuration(properties.duration, previousAnimator);
572 animator.setDuration(newDuration);
573 if (properties.delay > 0 && (previousAnimator == null
574 || previousAnimator.getAnimatedFraction() == 0)) {
575 animator.setStartDelay(properties.delay);
576 }
577 AnimatorListenerAdapter listener = properties.getAnimationFinishListener();
578 if (listener != null) {
579 animator.addListener(listener);
580 }
581 // remove the tag when the animation is finished
582 animator.addListener(new AnimatorListenerAdapter() {
583 @Override
584 public void onAnimationEnd(Animator animation) {
yoshiki iguchi4e30e762018-02-06 12:09:23 +0900585 HeadsUpUtil.setIsClickedHeadsUpNotification(child, false);
Selim Cinek0cfbef42016-11-09 19:06:36 -0800586 child.setTag(TAG_ANIMATOR_TRANSLATION_Y, null);
587 child.setTag(TAG_START_TRANSLATION_Y, null);
588 child.setTag(TAG_END_TRANSLATION_Y, null);
Selim Cinek2b549f42016-11-22 16:38:51 -0800589 onYTranslationAnimationFinished(child);
Selim Cinek0cfbef42016-11-09 19:06:36 -0800590 }
591 });
592 startAnimator(animator, listener);
593 child.setTag(TAG_ANIMATOR_TRANSLATION_Y, animator);
594 child.setTag(TAG_START_TRANSLATION_Y, child.getTranslationY());
595 child.setTag(TAG_END_TRANSLATION_Y, newEndValue);
596 }
597
Selim Cinek2b549f42016-11-22 16:38:51 -0800598 protected void onYTranslationAnimationFinished(View view) {
Selim Cinekfc9fb152017-02-28 16:47:50 -0800599 if (hidden && !gone) {
Selim Cinek2b549f42016-11-22 16:38:51 -0800600 view.setVisibility(View.INVISIBLE);
601 }
Selim Cinek0cfbef42016-11-09 19:06:36 -0800602 }
603
Selim Cinekf082fe22016-12-20 14:32:19 +0100604 public static void startAnimator(Animator animator, AnimatorListenerAdapter listener) {
Selim Cinek0cfbef42016-11-09 19:06:36 -0800605 if (listener != null) {
606 // Even if there's a delay we'd want to notify it of the start immediately.
607 listener.onAnimationStart(animator);
608 }
609 animator.start();
610 }
611
612 public static <T> T getChildTag(View child, int tag) {
613 return (T) child.getTag(tag);
614 }
615
616 protected void abortAnimation(View child, int animatorTag) {
617 Animator previousAnimator = getChildTag(child, animatorTag);
618 if (previousAnimator != null) {
619 previousAnimator.cancel();
620 }
621 }
622
623 /**
624 * Cancel the previous animator and get the duration of the new animation.
625 *
626 * @param duration the new duration
627 * @param previousAnimator the animator which was running before
628 * @return the new duration
629 */
Selim Cinekf082fe22016-12-20 14:32:19 +0100630 public static long cancelAnimatorAndGetNewDuration(long duration,
631 ValueAnimator previousAnimator) {
Selim Cinek0cfbef42016-11-09 19:06:36 -0800632 long newDuration = duration;
633 if (previousAnimator != null) {
634 // We take either the desired length of the new animation or the remaining time of
635 // the previous animator, whichever is longer.
636 newDuration = Math.max(previousAnimator.getDuration()
637 - previousAnimator.getCurrentPlayTime(), newDuration);
638 previousAnimator.cancel();
639 }
640 return newDuration;
641 }
642
643 /**
644 * Get the end value of the yTranslation animation running on a view or the yTranslation
645 * if no animation is running.
646 */
647 public static float getFinalTranslationY(View view) {
648 if (view == null) {
649 return 0;
650 }
651 ValueAnimator yAnimator = getChildTag(view, TAG_ANIMATOR_TRANSLATION_Y);
652 if (yAnimator == null) {
653 return view.getTranslationY();
654 } else {
655 return getChildTag(view, TAG_END_TRANSLATION_Y);
656 }
657 }
658
Selim Cinek7e0f9482017-05-22 20:00:56 -0700659 /**
660 * Get the end value of the zTranslation animation running on a view or the zTranslation
661 * if no animation is running.
662 */
663 public static float getFinalTranslationZ(View view) {
664 if (view == null) {
665 return 0;
666 }
667 ValueAnimator zAnimator = getChildTag(view, TAG_ANIMATOR_TRANSLATION_Z);
668 if (zAnimator == null) {
669 return view.getTranslationZ();
670 } else {
671 return getChildTag(view, TAG_END_TRANSLATION_Z);
672 }
673 }
674
Selim Cinekd5ab6452016-12-08 16:34:00 -0800675 public static boolean isAnimatingY(View child) {
Selim Cinek0cfbef42016-11-09 19:06:36 -0800676 return getChildTag(child, TAG_ANIMATOR_TRANSLATION_Y) != null;
677 }
Selim Cinekd5ab6452016-12-08 16:34:00 -0800678
679 public void cancelAnimations(View view) {
680 Animator animator = getChildTag(view, TAG_ANIMATOR_TRANSLATION_X);
681 if (animator != null) {
682 animator.cancel();
683 }
684 animator = getChildTag(view, TAG_ANIMATOR_TRANSLATION_Y);
685 if (animator != null) {
686 animator.cancel();
687 }
688 animator = getChildTag(view, TAG_ANIMATOR_TRANSLATION_Z);
689 if (animator != null) {
690 animator.cancel();
691 }
692 animator = getChildTag(view, TAG_ANIMATOR_ALPHA);
693 if (animator != null) {
694 animator.cancel();
695 }
696 }
Selim Cinekb036ca42015-02-20 15:56:28 +0100697}