diff options
10 files changed, 773 insertions, 637 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java index 0616db594cdb..903a9dc389f5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java @@ -37,6 +37,7 @@ import com.android.systemui.R; import com.android.systemui.classifier.FalsingManager; import com.android.systemui.statusbar.notification.FakeShadowView; import com.android.systemui.statusbar.notification.NotificationUtils; +import com.android.systemui.statusbar.stack.ExpandableViewState; import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.stack.StackStateAnimator; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java index bba411c3f99a..f7fb0796ae9b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java @@ -51,11 +51,11 @@ import com.android.systemui.classifier.FalsingManager; import com.android.systemui.statusbar.notification.HybridNotificationView; import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.stack.AnimationProperties; +import com.android.systemui.statusbar.stack.ExpandableViewState; import com.android.systemui.statusbar.stack.NotificationChildrenContainer; import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.stack.StackScrollState; -import com.android.systemui.statusbar.stack.StackStateAnimator; -import com.android.systemui.statusbar.stack.ExpandableViewState; import java.util.ArrayList; import java.util.List; @@ -485,11 +485,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { } } - public void startChildAnimation(StackScrollState finalState, - StackStateAnimator stateAnimator, long delay, long duration) { + public void startChildAnimation(StackScrollState finalState, AnimationProperties properties) { if (mIsSummaryWithChildren) { - mChildrenContainer.startAnimationToState(finalState, stateAnimator, delay, - duration); + mChildrenContainer.startAnimationToState(finalState, properties); } } @@ -1762,7 +1760,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { return new NotificationViewState(stackScrollState); } - public static class NotificationViewState extends ExpandableViewState { + public class NotificationViewState extends ExpandableViewState { private final StackScrollState mOverallState; @@ -1782,5 +1780,22 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { row.applyChildrenState(mOverallState); } } + + @Override + protected void onYTranslationAnimationFinished() { + super.onYTranslationAnimationFinished(); + if (mHeadsupDisappearRunning) { + setHeadsupDisappearRunning(false); + } + } + + @Override + public void animateTo(View child, AnimationProperties properties) { + super.animateTo(child, properties); + if (child instanceof ExpandableNotificationRow) { + ExpandableNotificationRow row = (ExpandableNotificationRow) child; + row.startChildAnimation(mOverallState, properties); + } + } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java index 8a6ce56913b3..f094f529f4ec 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java @@ -30,6 +30,7 @@ import com.android.systemui.statusbar.notification.NotificationUtils; import com.android.systemui.statusbar.phone.NotificationIconContainer; import com.android.systemui.statusbar.phone.NotificationPanelView; import com.android.systemui.statusbar.stack.AmbientState; +import com.android.systemui.statusbar.stack.AnimationProperties; import com.android.systemui.statusbar.stack.ExpandableViewState; import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.stack.StackScrollAlgorithm; @@ -300,5 +301,12 @@ public class NotificationShelf extends ActivatableNotificationView { updateAppearance(); setIconContainerTranslation(iconContainerTranslation); } + + @Override + public void animateTo(View child, AnimationProperties properties) { + super.animateTo(child, properties); + updateAppearance(); + setIconContainerTranslation(iconContainerTranslation); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index a4b76ebb53dd..249022e4dd4b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -21,7 +21,6 @@ import android.animation.AnimatorListenerAdapter; import android.animation.PropertyValuesHolder; import android.animation.ValueAnimator; import android.content.Context; -import android.graphics.Color; import android.graphics.Rect; import android.support.v4.graphics.ColorUtils; import android.view.View; @@ -37,7 +36,7 @@ import com.android.systemui.statusbar.ExpandableNotificationRow; import com.android.systemui.statusbar.NotificationData; import com.android.systemui.statusbar.ScrimView; import com.android.systemui.statusbar.policy.HeadsUpManager; -import com.android.systemui.statusbar.stack.StackStateAnimator; +import com.android.systemui.statusbar.stack.ViewState; /** * Controls both the scrim behind the notifications and in front of the notifications (when a @@ -478,14 +477,14 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, return; } - ValueAnimator previousAnimator = StackStateAnimator.getChildTag(scrim, + ValueAnimator previousAnimator = ViewState.getChildTag(scrim, TAG_KEY_ANIM); float animEndValue = -1; if (previousAnimator != null) { if (animate || alpha == currentAlpha) { previousAnimator.cancel(); } else { - animEndValue = StackStateAnimator.getChildTag(scrim, TAG_END_ALPHA); + animEndValue = ViewState.getChildTag(scrim, TAG_END_ALPHA); } } if (alpha != currentAlpha && alpha != animEndValue) { @@ -495,10 +494,8 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, scrim.setTag(TAG_END_ALPHA, alpha); } else { if (previousAnimator != null) { - float previousStartValue = StackStateAnimator.getChildTag(scrim, - TAG_START_ALPHA); - float previousEndValue = StackStateAnimator.getChildTag(scrim, - TAG_END_ALPHA); + float previousStartValue = ViewState.getChildTag(scrim, TAG_START_ALPHA); + float previousEndValue = ViewState.getChildTag(scrim, TAG_END_ALPHA); // we need to increase all animation keyframes of the previous animator by the // relative change to the end value PropertyValuesHolder[] values = previousAnimator.getValues(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationProperties.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationProperties.java new file mode 100644 index 000000000000..3cb6e5b1ca2f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationProperties.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.statusbar.stack; + +import android.animation.AnimatorListenerAdapter; +import android.util.Property; +import android.view.View; +import android.view.animation.Interpolator; + +/** + * Properties for a View animation + */ +public class AnimationProperties { + public long duration; + public long delay; + + /** + * @return an animation filter for this animation. + */ + public AnimationFilter getAnimationFilter() { + return null; + } + + /** + * @return a listener that should be run whenever any property finished its animation + */ + public AnimatorListenerAdapter getAnimationFinishListener() { + return null; + } + + public boolean wasAdded(View view) { + return false; + } + + /** + * Get a custom interpolator for a property instead of the normal one. + */ + public Interpolator getCustomInterpolator(View child, Property translationY) { + return null; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ExpandableViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ExpandableViewState.java index c78def4c89d8..0472db092e40 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ExpandableViewState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ExpandableViewState.java @@ -16,8 +16,15 @@ package com.android.systemui.statusbar.stack; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.PropertyValuesHolder; +import android.animation.ValueAnimator; import android.view.View; +import com.android.systemui.Interpolators; +import com.android.systemui.R; +import com.android.systemui.statusbar.ExpandableNotificationRow; import com.android.systemui.statusbar.ExpandableView; /** @@ -25,6 +32,16 @@ import com.android.systemui.statusbar.ExpandableView; */ public class ExpandableViewState extends ViewState { + private static final int TAG_ANIMATOR_HEIGHT = R.id.height_animator_tag; + private static final int TAG_ANIMATOR_TOP_INSET = R.id.top_inset_animator_tag; + private static final int TAG_ANIMATOR_SHADOW_ALPHA = R.id.shadow_alpha_animator_tag; + private static final int TAG_END_HEIGHT = R.id.height_animator_end_value_tag; + private static final int TAG_END_TOP_INSET = R.id.top_inset_animator_end_value_tag; + private static final int TAG_END_SHADOW_ALPHA = R.id.shadow_alpha_animator_end_value_tag; + private static final int TAG_START_HEIGHT = R.id.height_animator_start_value_tag; + private static final int TAG_START_TOP_INSET = R.id.top_inset_animator_start_value_tag; + private static final int TAG_START_SHADOW_ALPHA = R.id.shadow_alpha_animator_start_value_tag; + // These are flags such that we can create masks for filtering. /** @@ -156,4 +173,274 @@ public class ExpandableViewState extends ViewState { } } } + + @Override + public void animateTo(View child, AnimationProperties properties) { + super.animateTo(child, properties); + if (!(child instanceof ExpandableView)) { + return; + } + ExpandableView expandableView = (ExpandableView) child; + AnimationFilter animationFilter = properties.getAnimationFilter(); + + // start height animation + if (this.height != expandableView.getActualHeight()) { + startHeightAnimation(expandableView, properties); + } else { + abortAnimation(child, TAG_ANIMATOR_HEIGHT); + } + + // start shadow alpha animation + if (this.shadowAlpha != expandableView.getShadowAlpha()) { + startShadowAlphaAnimation(expandableView, properties); + } else { + abortAnimation(child, TAG_ANIMATOR_SHADOW_ALPHA); + } + + // start top inset animation + if (this.clipTopAmount != expandableView.getClipTopAmount()) { + startInsetAnimation(expandableView, properties); + } else { + abortAnimation(child, TAG_ANIMATOR_TOP_INSET); + } + + // start dimmed animation + expandableView.setDimmed(this.dimmed, animationFilter.animateDimmed); + + // apply below shelf state + expandableView.setBelowShelf(this.belowShelf); + + // start hiding sensitive animation + expandableView.setHideSensitive(this.hideSensitive, animationFilter.animateHideSensitive, + properties.delay, properties.duration); + + // start dark animation + expandableView.setDark(this.dark, animationFilter.animateDark, properties.delay); + + if (properties.wasAdded(child)) { + expandableView.performAddAnimation(properties.delay, properties.duration); + } + } + + private void startHeightAnimation(final ExpandableView child, AnimationProperties properties) { + Integer previousStartValue = getChildTag(child, TAG_START_HEIGHT); + Integer previousEndValue = getChildTag(child, TAG_END_HEIGHT); + int newEndValue = this.height; + if (previousEndValue != null && previousEndValue == newEndValue) { + return; + } + ValueAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_HEIGHT); + AnimationFilter filter = properties.getAnimationFilter(); + if (!filter.animateHeight) { + // just a local update was performed + if (previousAnimator != null) { + // we need to increase all animation keyframes of the previous animator by the + // relative change to the end value + PropertyValuesHolder[] values = previousAnimator.getValues(); + int relativeDiff = newEndValue - previousEndValue; + int newStartValue = previousStartValue + relativeDiff; + values[0].setIntValues(newStartValue, newEndValue); + child.setTag(TAG_START_HEIGHT, newStartValue); + child.setTag(TAG_END_HEIGHT, newEndValue); + previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); + return; + } else { + // no new animation needed, let's just apply the value + child.setActualHeight(newEndValue, false); + return; + } + } + + ValueAnimator animator = ValueAnimator.ofInt(child.getActualHeight(), newEndValue); + animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + child.setActualHeight((int) animation.getAnimatedValue(), + false /* notifyListeners */); + } + }); + animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); + long newDuration = cancelAnimatorAndGetNewDuration(properties.duration, previousAnimator); + animator.setDuration(newDuration); + if (properties.delay > 0 && (previousAnimator == null + || previousAnimator.getAnimatedFraction() == 0)) { + animator.setStartDelay(properties.delay); + } + AnimatorListenerAdapter listener = properties.getAnimationFinishListener(); + if (listener != null) { + animator.addListener(listener); + } + // remove the tag when the animation is finished + animator.addListener(new AnimatorListenerAdapter() { + boolean mWasCancelled; + + @Override + public void onAnimationEnd(Animator animation) { + child.setTag(TAG_ANIMATOR_HEIGHT, null); + child.setTag(TAG_START_HEIGHT, null); + child.setTag(TAG_END_HEIGHT, null); + child.setActualHeightAnimating(false); + if (!mWasCancelled && child instanceof ExpandableNotificationRow) { + ((ExpandableNotificationRow) child).setGroupExpansionChanging( + false /* isExpansionChanging */); + } + } + + @Override + public void onAnimationStart(Animator animation) { + mWasCancelled = false; + } + + @Override + public void onAnimationCancel(Animator animation) { + mWasCancelled = true; + } + }); + startAnimator(animator, listener); + child.setTag(TAG_ANIMATOR_HEIGHT, animator); + child.setTag(TAG_START_HEIGHT, child.getActualHeight()); + child.setTag(TAG_END_HEIGHT, newEndValue); + child.setActualHeightAnimating(true); + } + + private void startShadowAlphaAnimation(final ExpandableView child, + AnimationProperties properties) { + Float previousStartValue = getChildTag(child, TAG_START_SHADOW_ALPHA); + Float previousEndValue = getChildTag(child, TAG_END_SHADOW_ALPHA); + float newEndValue = this.shadowAlpha; + if (previousEndValue != null && previousEndValue == newEndValue) { + return; + } + ValueAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_SHADOW_ALPHA); + AnimationFilter filter = properties.getAnimationFilter(); + if (!filter.animateShadowAlpha) { + // just a local update was performed + if (previousAnimator != null) { + // we need to increase all animation keyframes of the previous animator by the + // relative change to the end value + PropertyValuesHolder[] values = previousAnimator.getValues(); + float relativeDiff = newEndValue - previousEndValue; + float newStartValue = previousStartValue + relativeDiff; + values[0].setFloatValues(newStartValue, newEndValue); + child.setTag(TAG_START_SHADOW_ALPHA, newStartValue); + child.setTag(TAG_END_SHADOW_ALPHA, newEndValue); + previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); + return; + } else { + // no new animation needed, let's just apply the value + child.setShadowAlpha(newEndValue); + return; + } + } + + ValueAnimator animator = ValueAnimator.ofFloat(child.getShadowAlpha(), newEndValue); + animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + child.setShadowAlpha((float) animation.getAnimatedValue()); + } + }); + animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); + long newDuration = cancelAnimatorAndGetNewDuration(properties.duration, previousAnimator); + animator.setDuration(newDuration); + if (properties.delay > 0 && (previousAnimator == null + || previousAnimator.getAnimatedFraction() == 0)) { + animator.setStartDelay(properties.delay); + } + AnimatorListenerAdapter listener = properties.getAnimationFinishListener(); + if (listener != null) { + animator.addListener(listener); + } + // remove the tag when the animation is finished + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + child.setTag(TAG_ANIMATOR_SHADOW_ALPHA, null); + child.setTag(TAG_START_SHADOW_ALPHA, null); + child.setTag(TAG_END_SHADOW_ALPHA, null); + } + }); + startAnimator(animator, listener); + child.setTag(TAG_ANIMATOR_SHADOW_ALPHA, animator); + child.setTag(TAG_START_SHADOW_ALPHA, child.getShadowAlpha()); + child.setTag(TAG_END_SHADOW_ALPHA, newEndValue); + } + + private void startInsetAnimation(final ExpandableView child, AnimationProperties properties) { + Integer previousStartValue = getChildTag(child, TAG_START_TOP_INSET); + Integer previousEndValue = getChildTag(child, TAG_END_TOP_INSET); + int newEndValue = this.clipTopAmount; + if (previousEndValue != null && previousEndValue == newEndValue) { + return; + } + ValueAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TOP_INSET); + AnimationFilter filter = properties.getAnimationFilter(); + if (!filter.animateTopInset) { + // just a local update was performed + if (previousAnimator != null) { + // we need to increase all animation keyframes of the previous animator by the + // relative change to the end value + PropertyValuesHolder[] values = previousAnimator.getValues(); + int relativeDiff = newEndValue - previousEndValue; + int newStartValue = previousStartValue + relativeDiff; + values[0].setIntValues(newStartValue, newEndValue); + child.setTag(TAG_START_TOP_INSET, newStartValue); + child.setTag(TAG_END_TOP_INSET, newEndValue); + previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); + return; + } else { + // no new animation needed, let's just apply the value + child.setClipTopAmount(newEndValue); + return; + } + } + + ValueAnimator animator = ValueAnimator.ofInt(child.getClipTopAmount(), newEndValue); + animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + child.setClipTopAmount((int) animation.getAnimatedValue()); + } + }); + animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); + long newDuration = cancelAnimatorAndGetNewDuration(properties.duration, previousAnimator); + animator.setDuration(newDuration); + if (properties.delay > 0 && (previousAnimator == null + || previousAnimator.getAnimatedFraction() == 0)) { + animator.setStartDelay(properties.delay); + } + AnimatorListenerAdapter listener = properties.getAnimationFinishListener(); + if (listener != null) { + animator.addListener(listener); + } + // remove the tag when the animation is finished + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + child.setTag(TAG_ANIMATOR_TOP_INSET, null); + child.setTag(TAG_START_TOP_INSET, null); + child.setTag(TAG_END_TOP_INSET, null); + } + }); + startAnimator(animator, listener); + child.setTag(TAG_ANIMATOR_TOP_INSET, animator); + child.setTag(TAG_START_TOP_INSET, child.getClipTopAmount()); + child.setTag(TAG_END_TOP_INSET, newEndValue); + } + + /** + * Get the end value of the height animation running on a view or the actualHeight + * if no animation is running. + */ + public static int getFinalActualHeight(ExpandableView view) { + if (view == null) { + return 0; + } + ValueAnimator heightAnimator = getChildTag(view, TAG_ANIMATOR_HEIGHT); + if (heightAnimator == null) { + return view.getActualHeight(); + } else { + return getChildTag(view, TAG_END_HEIGHT); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java index dfbc31f4f9eb..33b9d38bc6bd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java @@ -594,11 +594,11 @@ public class NotificationChildrenContainer extends ViewGroup { // There is no fake shadow to be drawn on the children child.setFakeShadowIntensity(0.0f, 0.0f, 0, 0); } - if (mOverflowNumber != null) { + if (mGroupOverFlowState != null) { mGroupOverFlowState.applyToView(mOverflowNumber); mNeverAppliedGroupState = false; } - if (mNotificationHeader != null) { + if (mHeaderViewState != null) { mHeaderViewState.applyToView(mNotificationHeader); } } @@ -614,8 +614,7 @@ public class NotificationChildrenContainer extends ViewGroup { return; } - public void startAnimationToState(StackScrollState state, StackStateAnimator stateAnimator, - long baseDelay, long duration) { + public void startAnimationToState(StackScrollState state, AnimationProperties properties) { int childCount = mChildren.size(); ViewState tmpState = new ViewState(); float expandFraction = getGroupExpandFraction(); @@ -624,7 +623,7 @@ public class NotificationChildrenContainer extends ViewGroup { for (int i = childCount - 1; i >= 0; i--) { ExpandableNotificationRow child = mChildren.get(i); ExpandableViewState viewState = state.getViewStateForView(child); - stateAnimator.startStackAnimations(child, viewState, state, -1, baseDelay); + viewState.animateTo(child, properties); // layout the divider View divider = mDividers.get(i); @@ -637,7 +636,7 @@ public class NotificationChildrenContainer extends ViewGroup { } tmpState.hidden = !dividersVisible; tmpState.alpha = alpha; - stateAnimator.startViewAnimations(divider, tmpState, baseDelay, duration); + tmpState.animateTo(divider, properties); // There is no fake shadow to be drawn on the children child.setFakeShadowIntensity(0.0f, 0.0f, 0, 0); } @@ -649,8 +648,7 @@ public class NotificationChildrenContainer extends ViewGroup { mGroupOverFlowState.alpha = alpha; mNeverAppliedGroupState = false; } - stateAnimator.startViewAnimations(mOverflowNumber, mGroupOverFlowState, - baseDelay, duration); + mGroupOverFlowState.animateTo(mOverflowNumber, properties); } if (mNotificationHeader != null) { mHeaderViewState.applyToView(mNotificationHeader); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java index d35e4a5d6b47..89c1d6e5071a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java @@ -69,9 +69,9 @@ import com.android.systemui.statusbar.EmptyShadeView; import com.android.systemui.statusbar.ExpandableNotificationRow; import com.android.systemui.statusbar.ExpandableView; import com.android.systemui.statusbar.NotificationGuts; -import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.NotificationSettingsIconRow; import com.android.systemui.statusbar.NotificationSettingsIconRow.SettingsIconRowListener; +import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.StackScrollerDecorView; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.notification.FakeShadowView; @@ -2095,7 +2095,7 @@ public class NotificationStackScrollLayout extends ViewGroup ActivatableNotificationView firstView = mFirstVisibleBackgroundChild; int top = 0; if (firstView != null) { - int finalTranslationY = (int) StackStateAnimator.getFinalTranslationY(firstView); + int finalTranslationY = (int) ViewState.getFinalTranslationY(firstView); if (mAnimateNextBackgroundTop || mTopAnimator == null && mCurrentBounds.top == finalTranslationY || mTopAnimator != null && mEndAnimationRect.top == finalTranslationY) { @@ -2108,8 +2108,8 @@ public class NotificationStackScrollLayout extends ViewGroup ActivatableNotificationView lastView = mLastVisibleBackgroundChild; int bottom = 0; if (lastView != null) { - int finalTranslationY = (int) StackStateAnimator.getFinalTranslationY(lastView); - int finalHeight = StackStateAnimator.getFinalActualHeight(lastView); + int finalTranslationY = (int) ViewState.getFinalTranslationY(lastView); + int finalHeight = ExpandableViewState.getFinalActualHeight(lastView); int finalBottom = finalTranslationY + finalHeight; finalBottom = Math.min(finalBottom, getHeight()); if (mAnimateNextBackgroundBottom diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java index 3ba7774dfd88..5cf0a27fcb90 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java @@ -18,9 +18,8 @@ package com.android.systemui.statusbar.stack; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; -import android.animation.ObjectAnimator; -import android.animation.PropertyValuesHolder; import android.animation.ValueAnimator; +import android.util.Property; import android.view.View; import android.view.ViewGroup; import android.view.animation.Interpolator; @@ -29,7 +28,6 @@ import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.statusbar.ExpandableNotificationRow; import com.android.systemui.statusbar.ExpandableView; -import com.android.systemui.statusbar.policy.HeadsUpManager; import java.util.ArrayList; import java.util.HashSet; @@ -54,28 +52,10 @@ public class StackStateAnimator { public static final int DELAY_EFFECT_MAX_INDEX_DIFFERENCE = 2; public static final int ANIMATION_DELAY_HEADS_UP = 120; - private static final int TAG_ANIMATOR_TRANSLATION_Y = R.id.translation_y_animator_tag; - private static final int TAG_ANIMATOR_TRANSLATION_Z = R.id.translation_z_animator_tag; - private static final int TAG_ANIMATOR_ALPHA = R.id.alpha_animator_tag; - private static final int TAG_ANIMATOR_HEIGHT = R.id.height_animator_tag; - private static final int TAG_ANIMATOR_TOP_INSET = R.id.top_inset_animator_tag; - private static final int TAG_ANIMATOR_SHADOW_ALPHA = R.id.shadow_alpha_animator_tag; - private static final int TAG_END_TRANSLATION_Y = R.id.translation_y_animator_end_value_tag; - private static final int TAG_END_TRANSLATION_Z = R.id.translation_z_animator_end_value_tag; - private static final int TAG_END_ALPHA = R.id.alpha_animator_end_value_tag; - private static final int TAG_END_HEIGHT = R.id.height_animator_end_value_tag; - private static final int TAG_END_TOP_INSET = R.id.top_inset_animator_end_value_tag; - private static final int TAG_END_SHADOW_ALPHA = R.id.shadow_alpha_animator_end_value_tag; - private static final int TAG_START_TRANSLATION_Y = R.id.translation_y_animator_start_value_tag; - private static final int TAG_START_TRANSLATION_Z = R.id.translation_z_animator_start_value_tag; - private static final int TAG_START_ALPHA = R.id.alpha_animator_start_value_tag; - private static final int TAG_START_HEIGHT = R.id.height_animator_start_value_tag; - private static final int TAG_START_TOP_INSET = R.id.top_inset_animator_start_value_tag; - private static final int TAG_START_SHADOW_ALPHA = R.id.shadow_alpha_animator_start_value_tag; - private final Interpolator mHeadsUpAppearInterpolator; private final int mGoToFullShadeAppearingTranslation; private final ExpandableViewState mTmpState = new ExpandableViewState(); + private final AnimationProperties mAnimationProperties; public NotificationStackScrollLayout mHostLayout; private ArrayList<NotificationStackScrollLayout.AnimationEvent> mNewEvents = new ArrayList<>(); @@ -102,6 +82,30 @@ public class StackStateAnimator { hostLayout.getContext().getResources().getDimensionPixelSize( R.dimen.go_to_full_shade_appearing_translation); mHeadsUpAppearInterpolator = new HeadsUpAppearInterpolator(); + mAnimationProperties = new AnimationProperties() { + @Override + public AnimationFilter getAnimationFilter() { + return mAnimationFilter; + } + + @Override + public AnimatorListenerAdapter getAnimationFinishListener() { + return getGlobalAnimationFinishedListener(); + } + + @Override + public boolean wasAdded(View view) { + return mNewAddChildren.contains(view); + } + + @Override + public Interpolator getCustomInterpolator(View child, Property property) { + if (mHeadsUpAppearChildren.contains(child) && View.TRANSLATION_Y.equals(property)) { + return mHeadsUpAppearInterpolator; + } + return null; + } + }; } public boolean isRunning() { @@ -128,7 +132,8 @@ public class StackStateAnimator { continue; } - startStackAnimations(child, viewState, finalState, i, -1 /* fixedDelay */); + initAnimationProperties(finalState, child, viewState); + viewState.animateTo(child, mAnimationProperties); } if (!isRunning()) { // no child has preformed any animation, lets finish @@ -140,6 +145,36 @@ public class StackStateAnimator { mNewAddChildren.clear(); } + private void initAnimationProperties(StackScrollState finalState, ExpandableView child, + ExpandableViewState viewState) { + boolean wasAdded = mAnimationProperties.wasAdded(child); + mAnimationProperties.duration = mCurrentLength; + adaptDurationWhenGoingToFullShade(child, viewState, wasAdded); + mAnimationProperties.delay = 0; + if (wasAdded || mAnimationFilter.hasDelays + && (viewState.yTranslation != child.getTranslationY() + || viewState.zTranslation != child.getTranslationZ() + || viewState.alpha != child.getAlpha() + || viewState.height != child.getActualHeight() + || viewState.clipTopAmount != child.getClipTopAmount() + || viewState.dark != child.isDark() + || viewState.shadowAlpha != child.getShadowAlpha())) { + mAnimationProperties.delay = mCurrentAdditionalDelay + + calculateChildAnimationDelay(viewState, finalState); + } + } + + private void adaptDurationWhenGoingToFullShade(ExpandableView child, + ExpandableViewState viewState, boolean wasAdded) { + if (wasAdded && mAnimationFilter.hasGoToFullShadeEvent) { + child.setTranslationY(child.getTranslationY() + mGoToFullShadeAppearingTranslation); + float longerDurationFactor = viewState.notGoneIndex - mCurrentLastNotAddedIndex; + longerDurationFactor = (float) Math.pow(longerDurationFactor, 0.7f); + mAnimationProperties.duration = ANIMATION_DURATION_APPEAR_DISAPPEAR + 50 + + (long) (100 * longerDurationFactor); + } + } + /** * Determines if a view should not perform an animation and applies it directly. * @@ -150,7 +185,7 @@ public class StackStateAnimator { if (mShadeExpanded) { return false; } - if (getChildTag(child, TAG_ANIMATOR_TRANSLATION_Y) != null) { + if (ViewState.isAnimatingY(child)) { // A Y translation animation is running return false; } @@ -182,143 +217,6 @@ public class StackStateAnimator { return -1; } - - /** - * Start an animation to the given {@link ExpandableViewState}. - * - * @param child the child to start the animation on - * @param viewState the {@link ExpandableViewState} of the view to animate to - * @param finalState the final state after the animation - * @param i the index of the view; only relevant if the view is the speed bump and is - * ignored otherwise - * @param fixedDelay a fixed delay if desired or -1 if the delay should be calculated - */ - public void startStackAnimations(final ExpandableView child, ExpandableViewState viewState, - StackScrollState finalState, int i, long fixedDelay) { - boolean wasAdded = mNewAddChildren.contains(child); - long duration = mCurrentLength; - if (wasAdded && mAnimationFilter.hasGoToFullShadeEvent) { - child.setTranslationY(child.getTranslationY() + mGoToFullShadeAppearingTranslation); - float longerDurationFactor = viewState.notGoneIndex - mCurrentLastNotAddedIndex; - longerDurationFactor = (float) Math.pow(longerDurationFactor, 0.7f); - duration = ANIMATION_DURATION_APPEAR_DISAPPEAR + 50 + - (long) (100 * longerDurationFactor); - } - boolean yTranslationChanging = child.getTranslationY() != viewState.yTranslation; - boolean zTranslationChanging = child.getTranslationZ() != viewState.zTranslation; - boolean alphaChanging = viewState.alpha != child.getAlpha(); - boolean heightChanging = viewState.height != child.getActualHeight(); - boolean shadowAlphaChanging = viewState.shadowAlpha != child.getShadowAlpha(); - boolean darkChanging = viewState.dark != child.isDark(); - boolean topInsetChanging = viewState.clipTopAmount != child.getClipTopAmount(); - boolean hasDelays = mAnimationFilter.hasDelays; - boolean isDelayRelevant = yTranslationChanging || zTranslationChanging || alphaChanging - || heightChanging || topInsetChanging || darkChanging || shadowAlphaChanging; - long delay = 0; - if (fixedDelay != -1) { - delay = fixedDelay; - } else if (hasDelays && isDelayRelevant || wasAdded) { - delay = mCurrentAdditionalDelay + calculateChildAnimationDelay(viewState, finalState); - } - - startViewAnimations(child, viewState, delay, duration); - - // start height animation - if (heightChanging) { - startHeightAnimation(child, viewState, duration, delay); - } else { - abortAnimation(child, TAG_ANIMATOR_HEIGHT); - } - - // start shadow alpha animation - if (shadowAlphaChanging) { - startShadowAlphaAnimation(child, viewState, duration, delay); - } else { - abortAnimation(child, TAG_ANIMATOR_SHADOW_ALPHA); - } - - // start top inset animation - if (topInsetChanging) { - startInsetAnimation(child, viewState, duration, delay); - } else { - abortAnimation(child, TAG_ANIMATOR_TOP_INSET); - } - - // start dimmed animation - child.setDimmed(viewState.dimmed, mAnimationFilter.animateDimmed); - - // apply below shelf state - child.setBelowShelf(viewState.belowShelf); - - // start hiding sensitive animation - child.setHideSensitive(viewState.hideSensitive, mAnimationFilter.animateHideSensitive, - delay, duration); - - // start dark animation - child.setDark(viewState.dark, mAnimationFilter.animateDark, delay); - - if (wasAdded) { - child.performAddAnimation(delay, mCurrentLength); - } - if (child instanceof ExpandableNotificationRow) { - ExpandableNotificationRow row = (ExpandableNotificationRow) child; - row.startChildAnimation(finalState, this, delay, duration); - } - } - - /** - * Start an animation to a new {@link ViewState}. - * - * @param child the child to start the animation on - * @param viewState the {@link ExpandableViewState} of the view to animate to - * @param delay a fixed delay - * @param duration the duration of the animation - */ - public void startViewAnimations(View child, ViewState viewState, long delay, long duration) { - boolean wasVisible = child.getVisibility() == View.VISIBLE; - final float alpha = viewState.alpha; - if (!wasVisible && (alpha != 0 || child.getAlpha() != 0) - && !viewState.gone && !viewState.hidden) { - child.setVisibility(View.VISIBLE); - } - boolean yTranslationChanging = child.getTranslationY() != viewState.yTranslation; - boolean zTranslationChanging = child.getTranslationZ() != viewState.zTranslation; - float childAlpha = child.getAlpha(); - boolean alphaChanging = viewState.alpha != childAlpha; - if (child instanceof ExpandableView) { - // We don't want views to change visibility when they are animating to GONE - alphaChanging &= !((ExpandableView) child).willBeGone(); - } - - // start translationY animation - if (yTranslationChanging) { - startYTranslationAnimation(child, viewState, duration, delay); - } else { - abortAnimation(child, TAG_ANIMATOR_TRANSLATION_Y); - } - - // start translationZ animation - if (zTranslationChanging) { - startZTranslationAnimation(child, viewState, duration, delay); - } else { - abortAnimation(child, TAG_ANIMATOR_TRANSLATION_Z); - } - - // start alpha animation - if (alphaChanging && child.getTranslationX() == 0) { - startAlphaAnimation(child, viewState, duration, delay); - } else { - abortAnimation(child, TAG_ANIMATOR_ALPHA); - } - } - - private void abortAnimation(View child, int animatorTag) { - Animator previousAnimator = getChildTag(child, animatorTag); - if (previousAnimator != null) { - previousAnimator.cancel(); - } - } - private long calculateChildAnimationDelay(ExpandableViewState viewState, StackScrollState finalState) { if (mAnimationFilter.hasDarkEvent) { @@ -394,396 +292,6 @@ public class StackStateAnimator { return (long) (index * ANIMATION_DELAY_PER_ELEMENT_GO_TO_FULL_SHADE); } - private void startShadowAlphaAnimation(final ExpandableView child, - ExpandableViewState viewState, long duration, long delay) { - Float previousStartValue = getChildTag(child, TAG_START_SHADOW_ALPHA); - Float previousEndValue = getChildTag(child, TAG_END_SHADOW_ALPHA); - float newEndValue = viewState.shadowAlpha; - if (previousEndValue != null && previousEndValue == newEndValue) { - return; - } - ValueAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_SHADOW_ALPHA); - if (!mAnimationFilter.animateShadowAlpha) { - // just a local update was performed - if (previousAnimator != null) { - // we need to increase all animation keyframes of the previous animator by the - // relative change to the end value - PropertyValuesHolder[] values = previousAnimator.getValues(); - float relativeDiff = newEndValue - previousEndValue; - float newStartValue = previousStartValue + relativeDiff; - values[0].setFloatValues(newStartValue, newEndValue); - child.setTag(TAG_START_SHADOW_ALPHA, newStartValue); - child.setTag(TAG_END_SHADOW_ALPHA, newEndValue); - previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); - return; - } else { - // no new animation needed, let's just apply the value - child.setShadowAlpha(newEndValue); - return; - } - } - - ValueAnimator animator = ValueAnimator.ofFloat(child.getShadowAlpha(), newEndValue); - animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - child.setShadowAlpha((float) animation.getAnimatedValue()); - } - }); - animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); - long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator); - animator.setDuration(newDuration); - if (delay > 0 && (previousAnimator == null - || previousAnimator.getAnimatedFraction() == 0)) { - animator.setStartDelay(delay); - } - animator.addListener(getGlobalAnimationFinishedListener()); - // remove the tag when the animation is finished - animator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - child.setTag(TAG_ANIMATOR_SHADOW_ALPHA, null); - child.setTag(TAG_START_SHADOW_ALPHA, null); - child.setTag(TAG_END_SHADOW_ALPHA, null); - } - }); - startAnimator(animator); - child.setTag(TAG_ANIMATOR_SHADOW_ALPHA, animator); - child.setTag(TAG_START_SHADOW_ALPHA, child.getShadowAlpha()); - child.setTag(TAG_END_SHADOW_ALPHA, newEndValue); - } - - private void startHeightAnimation(final ExpandableView child, - ExpandableViewState viewState, long duration, long delay) { - Integer previousStartValue = getChildTag(child, TAG_START_HEIGHT); - Integer previousEndValue = getChildTag(child, TAG_END_HEIGHT); - int newEndValue = viewState.height; - if (previousEndValue != null && previousEndValue == newEndValue) { - return; - } - ValueAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_HEIGHT); - if (!mAnimationFilter.animateHeight) { - // just a local update was performed - if (previousAnimator != null) { - // we need to increase all animation keyframes of the previous animator by the - // relative change to the end value - PropertyValuesHolder[] values = previousAnimator.getValues(); - int relativeDiff = newEndValue - previousEndValue; - int newStartValue = previousStartValue + relativeDiff; - values[0].setIntValues(newStartValue, newEndValue); - child.setTag(TAG_START_HEIGHT, newStartValue); - child.setTag(TAG_END_HEIGHT, newEndValue); - previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); - return; - } else { - // no new animation needed, let's just apply the value - child.setActualHeight(newEndValue, false); - return; - } - } - - ValueAnimator animator = ValueAnimator.ofInt(child.getActualHeight(), newEndValue); - animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - child.setActualHeight((int) animation.getAnimatedValue(), - false /* notifyListeners */); - } - }); - animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); - long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator); - animator.setDuration(newDuration); - if (delay > 0 && (previousAnimator == null - || previousAnimator.getAnimatedFraction() == 0)) { - animator.setStartDelay(delay); - } - animator.addListener(getGlobalAnimationFinishedListener()); - // remove the tag when the animation is finished - animator.addListener(new AnimatorListenerAdapter() { - boolean mWasCancelled; - - @Override - public void onAnimationEnd(Animator animation) { - child.setTag(TAG_ANIMATOR_HEIGHT, null); - child.setTag(TAG_START_HEIGHT, null); - child.setTag(TAG_END_HEIGHT, null); - child.setActualHeightAnimating(false); - if (!mWasCancelled && child instanceof ExpandableNotificationRow) { - ((ExpandableNotificationRow) child).setGroupExpansionChanging( - false /* isExpansionChanging */); - } - } - - @Override - public void onAnimationStart(Animator animation) { - mWasCancelled = false; - } - - @Override - public void onAnimationCancel(Animator animation) { - mWasCancelled = true; - } - }); - startAnimator(animator); - child.setTag(TAG_ANIMATOR_HEIGHT, animator); - child.setTag(TAG_START_HEIGHT, child.getActualHeight()); - child.setTag(TAG_END_HEIGHT, newEndValue); - child.setActualHeightAnimating(true); - } - - private void startInsetAnimation(final ExpandableView child, - ExpandableViewState viewState, long duration, long delay) { - Integer previousStartValue = getChildTag(child, TAG_START_TOP_INSET); - Integer previousEndValue = getChildTag(child, TAG_END_TOP_INSET); - int newEndValue = viewState.clipTopAmount; - if (previousEndValue != null && previousEndValue == newEndValue) { - return; - } - ValueAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TOP_INSET); - if (!mAnimationFilter.animateTopInset) { - // just a local update was performed - if (previousAnimator != null) { - // we need to increase all animation keyframes of the previous animator by the - // relative change to the end value - PropertyValuesHolder[] values = previousAnimator.getValues(); - int relativeDiff = newEndValue - previousEndValue; - int newStartValue = previousStartValue + relativeDiff; - values[0].setIntValues(newStartValue, newEndValue); - child.setTag(TAG_START_TOP_INSET, newStartValue); - child.setTag(TAG_END_TOP_INSET, newEndValue); - previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); - return; - } else { - // no new animation needed, let's just apply the value - child.setClipTopAmount(newEndValue); - return; - } - } - - ValueAnimator animator = ValueAnimator.ofInt(child.getClipTopAmount(), newEndValue); - animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - child.setClipTopAmount((int) animation.getAnimatedValue()); - } - }); - animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); - long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator); - animator.setDuration(newDuration); - if (delay > 0 && (previousAnimator == null - || previousAnimator.getAnimatedFraction() == 0)) { - animator.setStartDelay(delay); - } - animator.addListener(getGlobalAnimationFinishedListener()); - // remove the tag when the animation is finished - animator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - child.setTag(TAG_ANIMATOR_TOP_INSET, null); - child.setTag(TAG_START_TOP_INSET, null); - child.setTag(TAG_END_TOP_INSET, null); - } - }); - startAnimator(animator); - child.setTag(TAG_ANIMATOR_TOP_INSET, animator); - child.setTag(TAG_START_TOP_INSET, child.getClipTopAmount()); - child.setTag(TAG_END_TOP_INSET, newEndValue); - } - - private void startAlphaAnimation(final View child, - final ViewState viewState, long duration, long delay) { - Float previousStartValue = getChildTag(child,TAG_START_ALPHA); - Float previousEndValue = getChildTag(child,TAG_END_ALPHA); - final float newEndValue = viewState.alpha; - if (previousEndValue != null && previousEndValue == newEndValue) { - return; - } - ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_ALPHA); - if (!mAnimationFilter.animateAlpha) { - // just a local update was performed - if (previousAnimator != null) { - // we need to increase all animation keyframes of the previous animator by the - // relative change to the end value - PropertyValuesHolder[] values = previousAnimator.getValues(); - float relativeDiff = newEndValue - previousEndValue; - float newStartValue = previousStartValue + relativeDiff; - values[0].setFloatValues(newStartValue, newEndValue); - child.setTag(TAG_START_ALPHA, newStartValue); - child.setTag(TAG_END_ALPHA, newEndValue); - previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); - return; - } else { - // no new animation needed, let's just apply the value - child.setAlpha(newEndValue); - if (newEndValue == 0) { - child.setVisibility(View.INVISIBLE); - } - } - } - - ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.ALPHA, - child.getAlpha(), newEndValue); - animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); - // Handle layer type - child.setLayerType(View.LAYER_TYPE_HARDWARE, null); - animator.addListener(new AnimatorListenerAdapter() { - public boolean mWasCancelled; - - @Override - public void onAnimationEnd(Animator animation) { - child.setLayerType(View.LAYER_TYPE_NONE, null); - if (newEndValue == 0 && !mWasCancelled) { - child.setVisibility(View.INVISIBLE); - } - // remove the tag when the animation is finished - child.setTag(TAG_ANIMATOR_ALPHA, null); - child.setTag(TAG_START_ALPHA, null); - child.setTag(TAG_END_ALPHA, null); - } - - @Override - public void onAnimationCancel(Animator animation) { - mWasCancelled = true; - } - - @Override - public void onAnimationStart(Animator animation) { - mWasCancelled = false; - } - }); - long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator); - animator.setDuration(newDuration); - if (delay > 0 && (previousAnimator == null - || previousAnimator.getAnimatedFraction() == 0)) { - animator.setStartDelay(delay); - } - animator.addListener(getGlobalAnimationFinishedListener()); - - startAnimator(animator); - child.setTag(TAG_ANIMATOR_ALPHA, animator); - child.setTag(TAG_START_ALPHA, child.getAlpha()); - child.setTag(TAG_END_ALPHA, newEndValue); - } - - private void startZTranslationAnimation(final View child, - final ViewState viewState, long duration, long delay) { - Float previousStartValue = getChildTag(child,TAG_START_TRANSLATION_Z); - Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Z); - float newEndValue = viewState.zTranslation; - if (previousEndValue != null && previousEndValue == newEndValue) { - return; - } - ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TRANSLATION_Z); - if (!mAnimationFilter.animateZ) { - // just a local update was performed - if (previousAnimator != null) { - // we need to increase all animation keyframes of the previous animator by the - // relative change to the end value - PropertyValuesHolder[] values = previousAnimator.getValues(); - float relativeDiff = newEndValue - previousEndValue; - float newStartValue = previousStartValue + relativeDiff; - values[0].setFloatValues(newStartValue, newEndValue); - child.setTag(TAG_START_TRANSLATION_Z, newStartValue); - child.setTag(TAG_END_TRANSLATION_Z, newEndValue); - previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); - return; - } else { - // no new animation needed, let's just apply the value - child.setTranslationZ(newEndValue); - } - } - - ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Z, - child.getTranslationZ(), newEndValue); - animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); - long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator); - animator.setDuration(newDuration); - if (delay > 0 && (previousAnimator == null - || previousAnimator.getAnimatedFraction() == 0)) { - animator.setStartDelay(delay); - } - animator.addListener(getGlobalAnimationFinishedListener()); - // remove the tag when the animation is finished - animator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - child.setTag(TAG_ANIMATOR_TRANSLATION_Z, null); - child.setTag(TAG_START_TRANSLATION_Z, null); - child.setTag(TAG_END_TRANSLATION_Z, null); - } - }); - startAnimator(animator); - child.setTag(TAG_ANIMATOR_TRANSLATION_Z, animator); - child.setTag(TAG_START_TRANSLATION_Z, child.getTranslationZ()); - child.setTag(TAG_END_TRANSLATION_Z, newEndValue); - } - - private void startYTranslationAnimation(final View child, - ViewState viewState, long duration, long delay) { - Float previousStartValue = getChildTag(child,TAG_START_TRANSLATION_Y); - Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Y); - float newEndValue = viewState.yTranslation; - if (previousEndValue != null && previousEndValue == newEndValue) { - return; - } - ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TRANSLATION_Y); - if (!mAnimationFilter.animateY) { - // just a local update was performed - if (previousAnimator != null) { - // we need to increase all animation keyframes of the previous animator by the - // relative change to the end value - PropertyValuesHolder[] values = previousAnimator.getValues(); - float relativeDiff = newEndValue - previousEndValue; - float newStartValue = previousStartValue + relativeDiff; - values[0].setFloatValues(newStartValue, newEndValue); - child.setTag(TAG_START_TRANSLATION_Y, newStartValue); - child.setTag(TAG_END_TRANSLATION_Y, newEndValue); - previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); - return; - } else { - // no new animation needed, let's just apply the value - child.setTranslationY(newEndValue); - return; - } - } - - ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Y, - child.getTranslationY(), newEndValue); - Interpolator interpolator = mHeadsUpAppearChildren.contains(child) ? - mHeadsUpAppearInterpolator :Interpolators.FAST_OUT_SLOW_IN; - animator.setInterpolator(interpolator); - long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator); - animator.setDuration(newDuration); - if (delay > 0 && (previousAnimator == null - || previousAnimator.getAnimatedFraction() == 0)) { - animator.setStartDelay(delay); - } - animator.addListener(getGlobalAnimationFinishedListener()); - final boolean isHeadsUpDisappear = mHeadsUpDisappearChildren.contains(child); - // remove the tag when the animation is finished - animator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - HeadsUpManager.setIsClickedNotification(child, false); - child.setTag(TAG_ANIMATOR_TRANSLATION_Y, null); - child.setTag(TAG_START_TRANSLATION_Y, null); - child.setTag(TAG_END_TRANSLATION_Y, null); - if (isHeadsUpDisappear) { - ((ExpandableNotificationRow) child).setHeadsupDisappearRunning(false); - } - } - }); - startAnimator(animator); - child.setTag(TAG_ANIMATOR_TRANSLATION_Y, animator); - child.setTag(TAG_START_TRANSLATION_Y, child.getTranslationY()); - child.setTag(TAG_END_TRANSLATION_Y, newEndValue); - } - - private void startAnimator(ValueAnimator animator) { - mAnimatorSet.add(animator); - animator.start(); - } - /** * @return an adapter which ensures that onAnimationFinished is called once no animation is * running anymore @@ -814,33 +322,11 @@ public class StackStateAnimator { @Override public void onAnimationStart(Animator animation) { mWasCancelled = false; + mAnimatorSet.add(animation); } }; } - public static <T> T getChildTag(View child, int tag) { - return (T) child.getTag(tag); - } - - /** - * Cancel the previous animator and get the duration of the new animation. - * - * @param duration the new duration - * @param previousAnimator the animator which was running before - * @return the new duration - */ - private long cancelAnimatorAndGetNewDuration(long duration, ValueAnimator previousAnimator) { - long newDuration = duration; - if (previousAnimator != null) { - // We take either the desired length of the new animation or the remaining time of - // the previous animator, whichever is longer. - newDuration = Math.max(previousAnimator.getDuration() - - previousAnimator.getCurrentPlayTime(), newDuration); - previousAnimator.cancel(); - } - return newDuration; - } - private void onAnimationFinished() { mHostLayout.onChildAnimationFinished(); for (View v : mChildrenToClearFromOverlay) { @@ -942,12 +428,13 @@ public class StackStateAnimator { // We temporarily enable Y animations, the real filter will be combined // afterwards anyway mAnimationFilter.animateY = true; - startViewAnimations(changingView, mTmpState, + mAnimationProperties.delay = event.animationType == NotificationStackScrollLayout .AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK - ? ANIMATION_DELAY_HEADS_UP - : 0, - ANIMATION_DURATION_HEADS_UP_DISAPPEAR); + ? ANIMATION_DELAY_HEADS_UP + : 0; + mAnimationProperties.duration = ANIMATION_DURATION_HEADS_UP_DISAPPEAR; + mTmpState.animateTo(changingView, mAnimationProperties); mChildrenToClearFromOverlay.add(changingView); } } @@ -1007,38 +494,6 @@ public class StackStateAnimator { } } - /** - * Get the end value of the height animation running on a view or the actualHeight - * if no animation is running. - */ - public static int getFinalActualHeight(ExpandableView view) { - if (view == null) { - return 0; - } - ValueAnimator heightAnimator = getChildTag(view, TAG_ANIMATOR_HEIGHT); - if (heightAnimator == null) { - return view.getActualHeight(); - } else { - return getChildTag(view, TAG_END_HEIGHT); - } - } - - /** - * Get the end value of the yTranslation animation running on a view or the yTranslation - * if no animation is running. - */ - public static float getFinalTranslationY(View view) { - if (view == null) { - return 0; - } - ValueAnimator yAnimator = getChildTag(view, TAG_ANIMATOR_TRANSLATION_Y); - if (yAnimator == null) { - return view.getTranslationY(); - } else { - return getChildTag(view, TAG_END_TRANSLATION_Y); - } - } - public void setHeadsUpAppearHeightBottom(int headsUpAppearHeightBottom) { mHeadsUpAppearHeightBottom = headsUpAppearHeightBottom; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java index 108f0e16589f..c120bbbe23ce 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java @@ -16,9 +16,18 @@ package com.android.systemui.statusbar.stack; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ObjectAnimator; +import android.animation.PropertyValuesHolder; +import android.animation.ValueAnimator; import android.view.View; +import android.view.animation.Interpolator; +import com.android.systemui.Interpolators; +import com.android.systemui.R; import com.android.systemui.statusbar.ExpandableView; +import com.android.systemui.statusbar.policy.HeadsUpManager; /** * A state of a view. This can be used to apply a set of view properties to a view with @@ -27,6 +36,16 @@ import com.android.systemui.statusbar.ExpandableView; */ public class ViewState { + private static final int TAG_ANIMATOR_TRANSLATION_Y = R.id.translation_y_animator_tag; + private static final int TAG_ANIMATOR_TRANSLATION_Z = R.id.translation_z_animator_tag; + private static final int TAG_ANIMATOR_ALPHA = R.id.alpha_animator_tag; + private static final int TAG_END_TRANSLATION_Y = R.id.translation_y_animator_end_value_tag; + private static final int TAG_END_TRANSLATION_Z = R.id.translation_z_animator_end_value_tag; + private static final int TAG_END_ALPHA = R.id.alpha_animator_end_value_tag; + private static final int TAG_START_TRANSLATION_Y = R.id.translation_y_animator_start_value_tag; + private static final int TAG_START_TRANSLATION_Z = R.id.translation_z_animator_start_value_tag; + private static final int TAG_START_ALPHA = R.id.alpha_animator_start_value_tag; + public float alpha; public float xTranslation; public float yTranslation; @@ -119,4 +138,305 @@ public class ViewState { view.setScaleY(this.scaleY); } } + + /** + * Start an animation to this viewstate + * @param child the view to animate + * @param animationProperties the properties of the animation + */ + public void animateTo(View child, AnimationProperties animationProperties) { + boolean wasVisible = child.getVisibility() == View.VISIBLE; + final float alpha = this.alpha; + if (!wasVisible && (alpha != 0 || child.getAlpha() != 0) + && !this.gone && !this.hidden) { + child.setVisibility(View.VISIBLE); + } + boolean yTranslationChanging = child.getTranslationY() != this.yTranslation; + boolean zTranslationChanging = child.getTranslationZ() != this.zTranslation; + float childAlpha = child.getAlpha(); + boolean alphaChanging = this.alpha != childAlpha; + if (child instanceof ExpandableView) { + // We don't want views to change visibility when they are animating to GONE + alphaChanging &= !((ExpandableView) child).willBeGone(); + } + + // start translationY animation + if (yTranslationChanging) { + startYTranslationAnimation(child, animationProperties); + } else { + abortAnimation(child, TAG_ANIMATOR_TRANSLATION_Y); + } + + // start translationZ animation + if (zTranslationChanging) { + startZTranslationAnimation(child, animationProperties); + } else { + abortAnimation(child, TAG_ANIMATOR_TRANSLATION_Z); + } + + // start alpha animation + if (alphaChanging && child.getTranslationX() == 0) { + startAlphaAnimation(child, animationProperties); + } else { + abortAnimation(child, TAG_ANIMATOR_ALPHA); + } + } + + private void startAlphaAnimation(final View child, AnimationProperties properties) { + Float previousStartValue = getChildTag(child,TAG_START_ALPHA); + Float previousEndValue = getChildTag(child,TAG_END_ALPHA); + final float newEndValue = this.alpha; + if (previousEndValue != null && previousEndValue == newEndValue) { + return; + } + ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_ALPHA); + AnimationFilter filter = properties.getAnimationFilter(); + if (!filter.animateAlpha) { + // just a local update was performed + if (previousAnimator != null) { + // we need to increase all animation keyframes of the previous animator by the + // relative change to the end value + PropertyValuesHolder[] values = previousAnimator.getValues(); + float relativeDiff = newEndValue - previousEndValue; + float newStartValue = previousStartValue + relativeDiff; + values[0].setFloatValues(newStartValue, newEndValue); + child.setTag(TAG_START_ALPHA, newStartValue); + child.setTag(TAG_END_ALPHA, newEndValue); + previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); + return; + } else { + // no new animation needed, let's just apply the value + child.setAlpha(newEndValue); + if (newEndValue == 0) { + child.setVisibility(View.INVISIBLE); + } + } + } + + ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.ALPHA, + child.getAlpha(), newEndValue); + animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); + // Handle layer type + child.setLayerType(View.LAYER_TYPE_HARDWARE, null); + animator.addListener(new AnimatorListenerAdapter() { + public boolean mWasCancelled; + + @Override + public void onAnimationEnd(Animator animation) { + child.setLayerType(View.LAYER_TYPE_NONE, null); + if (newEndValue == 0 && !mWasCancelled) { + child.setVisibility(View.INVISIBLE); + } + // remove the tag when the animation is finished + child.setTag(TAG_ANIMATOR_ALPHA, null); + child.setTag(TAG_START_ALPHA, null); + child.setTag(TAG_END_ALPHA, null); + } + + @Override + public void onAnimationCancel(Animator animation) { + mWasCancelled = true; + } + + @Override + public void onAnimationStart(Animator animation) { + mWasCancelled = false; + } + }); + long newDuration = cancelAnimatorAndGetNewDuration(properties.duration, previousAnimator); + animator.setDuration(newDuration); + if (properties.delay > 0 && (previousAnimator == null + || previousAnimator.getAnimatedFraction() == 0)) { + animator.setStartDelay(properties.delay); + } + AnimatorListenerAdapter listener = properties.getAnimationFinishListener(); + if (listener != null) { + animator.addListener(listener); + } + + startAnimator(animator, listener); + child.setTag(TAG_ANIMATOR_ALPHA, animator); + child.setTag(TAG_START_ALPHA, child.getAlpha()); + child.setTag(TAG_END_ALPHA, newEndValue); + } + + private void startZTranslationAnimation(final View child, AnimationProperties properties) { + Float previousStartValue = getChildTag(child,TAG_START_TRANSLATION_Z); + Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Z); + float newEndValue = this.zTranslation; + if (previousEndValue != null && previousEndValue == newEndValue) { + return; + } + ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TRANSLATION_Z); + AnimationFilter filter = properties.getAnimationFilter(); + if (!filter.animateZ) { + // just a local update was performed + if (previousAnimator != null) { + // we need to increase all animation keyframes of the previous animator by the + // relative change to the end value + PropertyValuesHolder[] values = previousAnimator.getValues(); + float relativeDiff = newEndValue - previousEndValue; + float newStartValue = previousStartValue + relativeDiff; + values[0].setFloatValues(newStartValue, newEndValue); + child.setTag(TAG_START_TRANSLATION_Z, newStartValue); + child.setTag(TAG_END_TRANSLATION_Z, newEndValue); + previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); + return; + } else { + // no new animation needed, let's just apply the value + child.setTranslationZ(newEndValue); + } + } + + ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Z, + child.getTranslationZ(), newEndValue); + animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); + long newDuration = cancelAnimatorAndGetNewDuration(properties.duration, previousAnimator); + animator.setDuration(newDuration); + if (properties.delay > 0 && (previousAnimator == null + || previousAnimator.getAnimatedFraction() == 0)) { + animator.setStartDelay(properties.delay); + } + AnimatorListenerAdapter listener = properties.getAnimationFinishListener(); + if (listener != null) { + animator.addListener(listener); + } + // remove the tag when the animation is finished + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + child.setTag(TAG_ANIMATOR_TRANSLATION_Z, null); + child.setTag(TAG_START_TRANSLATION_Z, null); + child.setTag(TAG_END_TRANSLATION_Z, null); + } + }); + startAnimator(animator, listener); + child.setTag(TAG_ANIMATOR_TRANSLATION_Z, animator); + child.setTag(TAG_START_TRANSLATION_Z, child.getTranslationZ()); + child.setTag(TAG_END_TRANSLATION_Z, newEndValue); + } + + private void startYTranslationAnimation(final View child, AnimationProperties properties) { + Float previousStartValue = getChildTag(child,TAG_START_TRANSLATION_Y); + Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Y); + float newEndValue = this.yTranslation; + if (previousEndValue != null && previousEndValue == newEndValue) { + return; + } + ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TRANSLATION_Y); + AnimationFilter filter = properties.getAnimationFilter(); + if (!filter.animateY) { + // just a local update was performed + if (previousAnimator != null) { + // we need to increase all animation keyframes of the previous animator by the + // relative change to the end value + PropertyValuesHolder[] values = previousAnimator.getValues(); + float relativeDiff = newEndValue - previousEndValue; + float newStartValue = previousStartValue + relativeDiff; + values[0].setFloatValues(newStartValue, newEndValue); + child.setTag(TAG_START_TRANSLATION_Y, newStartValue); + child.setTag(TAG_END_TRANSLATION_Y, newEndValue); + previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); + return; + } else { + // no new animation needed, let's just apply the value + child.setTranslationY(newEndValue); + return; + } + } + + ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Y, + child.getTranslationY(), newEndValue); + Interpolator customInterpolator = properties.getCustomInterpolator(child, + View.TRANSLATION_Y); + Interpolator interpolator = customInterpolator != null ? customInterpolator + : Interpolators.FAST_OUT_SLOW_IN; + animator.setInterpolator(interpolator); + long newDuration = cancelAnimatorAndGetNewDuration(properties.duration, previousAnimator); + animator.setDuration(newDuration); + if (properties.delay > 0 && (previousAnimator == null + || previousAnimator.getAnimatedFraction() == 0)) { + animator.setStartDelay(properties.delay); + } + AnimatorListenerAdapter listener = properties.getAnimationFinishListener(); + if (listener != null) { + animator.addListener(listener); + } + // remove the tag when the animation is finished + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + HeadsUpManager.setIsClickedNotification(child, false); + child.setTag(TAG_ANIMATOR_TRANSLATION_Y, null); + child.setTag(TAG_START_TRANSLATION_Y, null); + child.setTag(TAG_END_TRANSLATION_Y, null); + onYTranslationAnimationFinished(); + } + }); + startAnimator(animator, listener); + child.setTag(TAG_ANIMATOR_TRANSLATION_Y, animator); + child.setTag(TAG_START_TRANSLATION_Y, child.getTranslationY()); + child.setTag(TAG_END_TRANSLATION_Y, newEndValue); + } + + protected void onYTranslationAnimationFinished() { + } + + protected void startAnimator(Animator animator, AnimatorListenerAdapter listener) { + if (listener != null) { + // Even if there's a delay we'd want to notify it of the start immediately. + listener.onAnimationStart(animator); + } + animator.start(); + } + + public static <T> T getChildTag(View child, int tag) { + return (T) child.getTag(tag); + } + + protected void abortAnimation(View child, int animatorTag) { + Animator previousAnimator = getChildTag(child, animatorTag); + if (previousAnimator != null) { + previousAnimator.cancel(); + } + } + + /** + * Cancel the previous animator and get the duration of the new animation. + * + * @param duration the new duration + * @param previousAnimator the animator which was running before + * @return the new duration + */ + protected long cancelAnimatorAndGetNewDuration(long duration, ValueAnimator previousAnimator) { + long newDuration = duration; + if (previousAnimator != null) { + // We take either the desired length of the new animation or the remaining time of + // the previous animator, whichever is longer. + newDuration = Math.max(previousAnimator.getDuration() + - previousAnimator.getCurrentPlayTime(), newDuration); + previousAnimator.cancel(); + } + return newDuration; + } + + /** + * Get the end value of the yTranslation animation running on a view or the yTranslation + * if no animation is running. + */ + public static float getFinalTranslationY(View view) { + if (view == null) { + return 0; + } + ValueAnimator yAnimator = getChildTag(view, TAG_ANIMATOR_TRANSLATION_Y); + if (yAnimator == null) { + return view.getTranslationY(); + } else { + return getChildTag(view, TAG_END_TRANSLATION_Y); + } + } + + public static boolean isAnimatingY(ExpandableView child) { + return getChildTag(child, TAG_ANIMATOR_TRANSLATION_Y) != null; + } } |