summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohn Spurlock <jspurlock@google.com>2015-06-17 11:56:12 -0400
committerJohn Spurlock <jspurlock@google.com>2015-06-24 14:04:18 -0400
commit22def3d4ebe47d39a03447f46a945228f565a1bf (patch)
tree7db99f2589005dbe0ce1f409daa940c3c2d3589e
parentf8fbc2a7cc1449ba1f8ebc3e8f09359903293ad1 (diff)
downloadframeworks_base-22def3d4ebe47d39a03447f46a945228f565a1bf.zip
frameworks_base-22def3d4ebe47d39a03447f46a945228f565a1bf.tar.gz
frameworks_base-22def3d4ebe47d39a03447f46a945228f565a1bf.tar.bz2
Volume Motion: Initial show and expand transition.
Also re-enable the content collapse layout transition animations, supported by ensuring the dialog window is tall enough for long enough to complete the transition, avoiding clipping. Bug: 21335976 Change-Id: Ibc4cbb1e882c1e11c4406463752afa177fb1e6d7
-rw-r--r--packages/SystemUI/res/layout/volume_dialog.xml2
-rw-r--r--packages/SystemUI/res/layout/volume_dialog_row.xml1
-rw-r--r--packages/SystemUI/res/values/dimens.xml3
-rw-r--r--packages/SystemUI/res/values/styles.xml5
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java117
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumeDialogMotion.java304
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java5
7 files changed, 412 insertions, 25 deletions
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index 0ed1e2a..7617ed4 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -18,7 +18,7 @@
android:id="@+id/volume_dialog"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginBottom="4dp"
+ android:layout_marginBottom="@dimen/volume_dialog_margin_bottom"
android:layout_marginLeft="@dimen/notification_side_padding"
android:layout_marginRight="@dimen/notification_side_padding"
android:background="@drawable/volume_dialog_background"
diff --git a/packages/SystemUI/res/layout/volume_dialog_row.xml b/packages/SystemUI/res/layout/volume_dialog_row.xml
index c6aa588..1a6d34e 100644
--- a/packages/SystemUI/res/layout/volume_dialog_row.xml
+++ b/packages/SystemUI/res/layout/volume_dialog_row.xml
@@ -17,6 +17,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipChildren="false"
+ android:id="@+id/volume_dialog_row"
android:paddingEnd="8dp"
android:paddingStart="8dp" >
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 869b03a..005077f 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -576,6 +576,9 @@
<!-- Standard image button size for volume dialog buttons -->
<dimen name="volume_button_size">48dp</dimen>
+ <!-- Volume dialog root view bottom margin, at rest -->
+ <dimen name="volume_dialog_margin_bottom">4dp</dimen>
+
<!-- Padding between icon and text for managed profile toast -->
<dimen name="managed_profile_toast_padding">4dp</dimen>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 67d3312..1889862 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -291,11 +291,6 @@
<item name="android:textColor">#ffb0b3c5</item>
</style>
- <style name="VolumeDialogAnimations">
- <item name="android:windowEnterAnimation">@android:anim/fade_in</item>
- <item name="android:windowExitAnimation">@android:anim/fade_out</item>
- </style>
-
<style name="VolumeButtons" parent="@android:style/Widget.Material.Button.Borderless">
<item name="android:background">@drawable/btn_borderless_rect</item>
</style>
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
index 065523f..0ab0392 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
@@ -111,6 +111,7 @@ public class VolumeDialog {
private final Accessibility mAccessibility = new Accessibility();
private final ColorStateList mActiveSliderTint;
private final ColorStateList mInactiveSliderTint;
+ private final VolumeDialogMotion mMotion;
private boolean mShowing;
private boolean mExpanded;
@@ -120,9 +121,12 @@ public class VolumeDialog {
private boolean mSilentMode = VolumePrefs.DEFAULT_ENABLE_SILENT_MODE;
private State mState;
private int mExpandButtonRes;
- private boolean mExpanding;
+ private boolean mExpandButtonAnimationRunning;
private SafetyWarningDialog mSafetyWarning;
private Callback mCallback;
+ private boolean mPendingStateChanged;
+ private boolean mPendingRecheckAll;
+ private long mCollapseTime;
public VolumeDialog(Context context, int windowType, VolumeDialogController controller,
ZenModeController zenModeController, Callback callback) {
@@ -151,7 +155,6 @@ public class VolumeDialog {
lp.format = PixelFormat.TRANSLUCENT;
lp.setTitle(VolumeDialog.class.getSimpleName());
lp.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
- lp.windowAnimations = R.style.VolumeDialogAnimations;
lp.y = res.getDimensionPixelSize(R.dimen.volume_offset_top);
lp.gravity = Gravity.TOP;
window.setAttributes(lp);
@@ -168,9 +171,22 @@ public class VolumeDialog {
updateExpandButtonH();
mLayoutTransition = new LayoutTransition();
mLayoutTransition.setDuration(new ValueAnimator().getDuration() / 2);
- mLayoutTransition.disableTransitionType(LayoutTransition.DISAPPEARING);
- mLayoutTransition.disableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
mDialogContentView.setLayoutTransition(mLayoutTransition);
+ mMotion = new VolumeDialogMotion(mDialog, mDialogView, mDialogContentView, mExpandButton,
+ new VolumeDialogMotion.Callback() {
+ @Override
+ public void onAnimatingChanged(boolean animating) {
+ if (animating) return;
+ if (mPendingStateChanged) {
+ mHandler.sendEmptyMessage(H.STATE_CHANGED);
+ mPendingStateChanged = false;
+ }
+ if (mPendingRecheckAll) {
+ mHandler.sendEmptyMessage(H.RECHECK_ALL);
+ mPendingRecheckAll = false;
+ }
+ }
+ });
addRow(AudioManager.STREAM_RING,
R.drawable.ic_volume_ringer, R.drawable.ic_volume_ringer_mute, true);
@@ -242,6 +258,7 @@ public class VolumeDialog {
final VolumeRow row = initRow(stream, iconRes, iconMuteRes, important);
if (!mRows.isEmpty()) {
final View v = new View(mContext);
+ v.setId(android.R.id.background);
final int h = mContext.getResources()
.getDimensionPixelSize(R.dimen.volume_slider_interspacing);
final LinearLayout.LayoutParams lp =
@@ -253,10 +270,11 @@ public class VolumeDialog {
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom) {
- if (D.BUG) Log.d(TAG, "onLayoutChange"
+ final boolean moved = oldLeft != left || oldTop != top;
+ if (D.BUG) Log.d(TAG, "onLayoutChange moved=" + moved
+ " old=" + new Rect(oldLeft, oldTop, oldRight, oldBottom).toShortString()
+ " new=" + new Rect(left,top,right,bottom).toShortString());
- if (oldLeft != left || oldTop != top) {
+ if (moved) {
for (int i = 0; i < mDialogContentView.getChildCount(); i++) {
final View c = mDialogContentView.getChildAt(i);
if (!c.isShown()) continue;
@@ -302,18 +320,21 @@ public class VolumeDialog {
if (D.BUG) Log.d(TAG, "repositionExpandAnim x=" + x + " y=" + y);
mExpandButton.setTranslationX(x);
mExpandButton.setTranslationY(y);
+ mExpandButton.setTag((Integer) y);
}
public void dump(PrintWriter writer) {
writer.println(VolumeDialog.class.getSimpleName() + " state:");
writer.print(" mShowing: "); writer.println(mShowing);
writer.print(" mExpanded: "); writer.println(mExpanded);
- writer.print(" mExpanding: "); writer.println(mExpanding);
+ writer.print(" mExpandButtonAnimationRunning: ");
+ writer.println(mExpandButtonAnimationRunning);
writer.print(" mActiveStream: "); writer.println(mActiveStream);
writer.print(" mDynamic: "); writer.println(mDynamic);
writer.print(" mShowHeaders: "); writer.println(mShowHeaders);
writer.print(" mAutomute: "); writer.println(mAutomute);
writer.print(" mSilentMode: "); writer.println(mSilentMode);
+ writer.print(" mCollapseTime: "); writer.println(mCollapseTime);
writer.print(" mAccessibility.mFeedbackEnabled: ");
writer.println(mAccessibility.mFeedbackEnabled);
}
@@ -412,12 +433,13 @@ public class VolumeDialog {
}
private void showH(int reason) {
+ if (D.BUG) Log.d(TAG, "showH r=" + Events.DISMISS_REASONS[reason]);
mHandler.removeMessages(H.SHOW);
mHandler.removeMessages(H.DISMISS);
rescheduleTimeoutH();
if (mShowing) return;
mShowing = true;
- mDialog.show();
+ mMotion.startShow();
Events.writeEvent(mContext, Events.EVENT_SHOW_DIALOG, reason, mKeyguard.isKeyguardLocked());
mController.notifyVisible(true);
}
@@ -434,7 +456,7 @@ public class VolumeDialog {
private int computeTimeoutH() {
if (mAccessibility.mFeedbackEnabled) return 20000;
if (mSafetyWarning != null) return 5000;
- if (mExpanded || mExpanding) return 5000;
+ if (mExpanded || mExpandButtonAnimationRunning) return 5000;
if (mActiveStream == AudioManager.STREAM_MUSIC) return 1500;
return 3000;
}
@@ -444,9 +466,13 @@ public class VolumeDialog {
mHandler.removeMessages(H.SHOW);
if (!mShowing) return;
mShowing = false;
- mDialog.dismiss();
+ mMotion.startDismiss(new Runnable() {
+ @Override
+ public void run() {
+ setExpandedH(false);
+ }
+ });
Events.writeEvent(mContext, Events.EVENT_DISMISS_DIALOG, reason);
- setExpandedH(false);
mController.notifyVisible(false);
synchronized (mSafetyWarningLock) {
if (mSafetyWarning != null) {
@@ -456,13 +482,40 @@ public class VolumeDialog {
}
}
+ private void updateDialogBottomMarginH() {
+ final long diff = System.currentTimeMillis() - mCollapseTime;
+ final boolean collapsing = mCollapseTime != 0 && diff < getConservativeCollapseDuration();
+ final ViewGroup.MarginLayoutParams mlp = (MarginLayoutParams) mDialogView.getLayoutParams();
+ final int bottomMargin = collapsing ? mDialogContentView.getHeight() :
+ mContext.getResources().getDimensionPixelSize(R.dimen.volume_dialog_margin_bottom);
+ if (bottomMargin != mlp.bottomMargin) {
+ if (D.BUG) Log.d(TAG, "bottomMargin " + mlp.bottomMargin + " -> " + bottomMargin);
+ mlp.bottomMargin = bottomMargin;
+ mDialogView.setLayoutParams(mlp);
+ }
+ }
+
+ private long getConservativeCollapseDuration() {
+ return mExpandButtonAnimationDuration * 3;
+ }
+
+ private void prepareForCollapse() {
+ mHandler.removeMessages(H.UPDATE_BOTTOM_MARGIN);
+ mCollapseTime = System.currentTimeMillis();
+ updateDialogBottomMarginH();
+ mHandler.sendEmptyMessageDelayed(H.UPDATE_BOTTOM_MARGIN, getConservativeCollapseDuration());
+ }
+
private void setExpandedH(boolean expanded) {
if (mExpanded == expanded) return;
mExpanded = expanded;
- mExpanding = isAttached();
+ mExpandButtonAnimationRunning = isAttached();
if (D.BUG) Log.d(TAG, "setExpandedH " + expanded);
+ if (!mExpanded && mExpandButtonAnimationRunning) {
+ prepareForCollapse();
+ }
updateRowsH();
- if (mExpanding) {
+ if (mExpandButtonAnimationRunning) {
final Drawable d = mExpandButton.getDrawable();
if (d instanceof AnimatedVectorDrawable) {
// workaround to reset drawable
@@ -473,7 +526,7 @@ public class VolumeDialog {
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
- mExpanding = false;
+ mExpandButtonAnimationRunning = false;
updateExpandButtonH();
rescheduleTimeoutH();
}
@@ -484,8 +537,9 @@ public class VolumeDialog {
}
private void updateExpandButtonH() {
- mExpandButton.setClickable(!mExpanding);
- if (mExpanding && isAttached()) return;
+ if (D.BUG) Log.d(TAG, "updateExpandButtonH");
+ mExpandButton.setClickable(!mExpandButtonAnimationRunning);
+ if (mExpandButtonAnimationRunning && isAttached()) return;
final int res = mExpanded ? R.drawable.ic_volume_collapse_animation
: R.drawable.ic_volume_expand_animation;
if (res == mExpandButtonRes) return;
@@ -502,6 +556,7 @@ public class VolumeDialog {
}
private void updateRowsH() {
+ if (D.BUG) Log.d(TAG, "updateRowsH");
final VolumeRow activeRow = getActiveRow();
updateFooterH();
updateExpandButtonH();
@@ -531,6 +586,7 @@ public class VolumeDialog {
}
private void trimObsoleteH() {
+ if (D.BUG) Log.d(TAG, "trimObsoleteH");
for (int i = mRows.size() -1; i >= 0; i--) {
final VolumeRow row = mRows.get(i);
if (row.ss == null || !row.ss.dynamic) continue;
@@ -543,7 +599,13 @@ public class VolumeDialog {
}
private void onStateChangedH(State state) {
+ final boolean animating = mMotion.isAnimating();
+ if (D.BUG) Log.d(TAG, "onStateChangedH animating=" + animating);
mState = state;
+ if (animating) {
+ mPendingStateChanged = true;
+ return;
+ }
mDynamic.clear();
// add any new dynamic rows
for (int i = 0; i < state.states.size(); i++) {
@@ -568,11 +630,18 @@ public class VolumeDialog {
}
private void updateFooterH() {
- Util.setVisOrGone(mZenFooter, mState.zenMode != Global.ZEN_MODE_OFF);
+ if (D.BUG) Log.d(TAG, "updateFooterH");
+ final boolean wasVisible = mZenFooter.getVisibility() == View.VISIBLE;
+ final boolean visible = mState.zenMode != Global.ZEN_MODE_OFF;
+ if (wasVisible != visible && !visible) {
+ prepareForCollapse();
+ }
+ Util.setVisOrGone(mZenFooter, visible);
mZenFooter.update();
}
private void updateVolumeRowH(VolumeRow row) {
+ if (D.BUG) Log.d(TAG, "updateVolumeRowH s=" + row.stream);
if (mState == null) return;
final StreamState ss = mState.states.get(row.stream);
if (ss == null) return;
@@ -841,7 +910,7 @@ public class VolumeDialog {
private final OnClickListener mClickExpand = new OnClickListener() {
@Override
public void onClick(View v) {
- if (mExpanding) return;
+ if (mExpandButtonAnimationRunning) return;
final boolean newExpand = !mExpanded;
Events.writeEvent(mContext, Events.EVENT_EXPAND, newExpand);
setExpandedH(newExpand);
@@ -870,6 +939,8 @@ public class VolumeDialog {
private static final int RECHECK_ALL = 4;
private static final int SET_STREAM_IMPORTANT = 5;
private static final int RESCHEDULE_TIMEOUT = 6;
+ private static final int STATE_CHANGED = 7;
+ private static final int UPDATE_BOTTOM_MARGIN = 8;
public H() {
super(Looper.getMainLooper());
@@ -884,6 +955,8 @@ public class VolumeDialog {
case RECHECK_ALL: recheckH(null); break;
case SET_STREAM_IMPORTANT: setStreamImportantH(msg.arg1, msg.arg2 != 0); break;
case RESCHEDULE_TIMEOUT: rescheduleTimeoutH(); break;
+ case STATE_CHANGED: onStateChangedH(mState); break;
+ case UPDATE_BOTTOM_MARGIN: updateDialogBottomMarginH(); break;
}
}
}
@@ -902,6 +975,12 @@ public class VolumeDialog {
@Override
protected void onStop() {
super.onStop();
+ final boolean animating = mMotion.isAnimating();
+ if (D.BUG) Log.d(TAG, "onStop animating=" + animating);
+ if (animating) {
+ mPendingRecheckAll = true;
+ return;
+ }
mHandler.sendEmptyMessage(H.RECHECK_ALL);
}
@@ -978,11 +1057,13 @@ public class VolumeDialog {
mDialogView.addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
@Override
public void onViewDetachedFromWindow(View v) {
+ if (D.BUG) Log.d(TAG, "onViewDetachedFromWindow");
// noop
}
@Override
public void onViewAttachedToWindow(View v) {
+ if (D.BUG) Log.d(TAG, "onViewAttachedToWindow");
updateFeedbackEnabled();
}
});
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogMotion.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogMotion.java
new file mode 100644
index 0000000..fdf1840
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogMotion.java
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2015 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.volume;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.TimeInterpolator;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnDismissListener;
+import android.content.DialogInterface.OnShowListener;
+import android.os.Handler;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.PathInterpolator;
+
+public class VolumeDialogMotion {
+ private static final String TAG = Util.logTag(VolumeDialogMotion.class);
+
+ private static final float ANIMATION_SCALE = 1.0f;
+ private static final int PRE_DISMISS_DELAY = 50;
+ private static final int POST_SHOW_DELAY = 200;
+
+ private final Dialog mDialog;
+ private final View mDialogView;
+ private final ViewGroup mContents; // volume rows + zen footer
+ private final View mChevron;
+ private final Handler mHandler = new Handler();
+ private final Callback mCallback;
+
+ private boolean mAnimating; // show or dismiss animation is running
+ private boolean mShowing; // show animation is running
+ private boolean mDismissing; // dismiss animation is running
+ private ValueAnimator mChevronPositionAnimator;
+ private ValueAnimator mContentsPositionAnimator;
+
+ public VolumeDialogMotion(Dialog dialog, View dialogView, ViewGroup contents, View chevron,
+ Callback callback) {
+ mDialog = dialog;
+ mDialogView = dialogView;
+ mContents = contents;
+ mChevron = chevron;
+ mCallback = callback;
+ mDialog.setOnDismissListener(new OnDismissListener() {
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ if (D.BUG) Log.d(TAG, "mDialog.onDismiss");
+ }
+ });
+ mDialog.setOnShowListener(new OnShowListener() {
+ @Override
+ public void onShow(DialogInterface dialog) {
+ if (D.BUG) Log.d(TAG, "mDialog.onShow");
+ final int h = mDialogView.getHeight();
+ mDialogView.setTranslationY(-h);
+ mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ startShowAnimation();
+ }
+ }, POST_SHOW_DELAY);
+ }
+ });
+ }
+
+ public boolean isAnimating() {
+ return mAnimating;
+ }
+
+ private void setShowing(boolean showing) {
+ if (showing == mShowing) return;
+ mShowing = showing;
+ if (D.BUG) Log.d(TAG, "mShowing = " + mShowing);
+ updateAnimating();
+ }
+
+ private void setDismissing(boolean dismissing) {
+ if (dismissing == mDismissing) return;
+ mDismissing = dismissing;
+ if (D.BUG) Log.d(TAG, "mDismissing = " + mDismissing);
+ updateAnimating();
+ }
+
+ private void updateAnimating() {
+ final boolean animating = mShowing || mDismissing;
+ if (animating == mAnimating) return;
+ mAnimating = animating;
+ if (D.BUG) Log.d(TAG, "mAnimating = " + mAnimating);
+ if (mCallback != null) {
+ mCallback.onAnimatingChanged(mAnimating);
+ }
+ }
+
+ public void startShow() {
+ if (D.BUG) Log.d(TAG, "startShow");
+ if (mShowing) return;
+ setShowing(true);
+ if (mDismissing) {
+ mDialogView.animate().cancel();
+ setDismissing(false);
+ startShowAnimation();
+ return;
+ }
+ if (D.BUG) Log.d(TAG, "mDialog.show()");
+ mDialog.show();
+ }
+
+ private int chevronDistance() {
+ return mChevron.getHeight() / 6;
+ }
+
+ private void startShowAnimation() {
+ if (D.BUG) Log.d(TAG, "startShowAnimation");
+ mDialogView.animate()
+ .translationY(0)
+ .setDuration(scaledDuration(300))
+ .setInterpolator(new LogDecelerateInterpolator())
+ .setListener(null)
+ .setUpdateListener(new AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ if (mChevronPositionAnimator == null) return;
+ // reposition chevron
+ final float v = (Float) mChevronPositionAnimator.getAnimatedValue();
+ final int posY = (Integer) mChevron.getTag();
+ mChevron.setTranslationY(posY + v + -mDialogView.getTranslationY());
+ }})
+ .start();
+
+ mContentsPositionAnimator = ValueAnimator.ofFloat(-chevronDistance(), 0)
+ .setDuration(scaledDuration(400));
+ mContentsPositionAnimator.addListener(new AnimatorListenerAdapter() {
+ private boolean mCancelled;
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (mCancelled) return;
+ if (D.BUG) Log.d(TAG, "show.onAnimationEnd");
+ setShowing(false);
+ }
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ if (D.BUG) Log.d(TAG, "show.onAnimationCancel");
+ mCancelled = true;
+ }
+ });
+ mContentsPositionAnimator.addUpdateListener(new AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ float v = (Float) animation.getAnimatedValue();
+ mContents.setTranslationY(v + -mDialogView.getTranslationY());
+ }
+ });
+ mContentsPositionAnimator.setInterpolator(new LogDecelerateInterpolator());
+ mContentsPositionAnimator.start();
+
+ mContents.setAlpha(0);
+ mContents.animate()
+ .alpha(1)
+ .setDuration(scaledDuration(150))
+ .setInterpolator(new PathInterpolator(0f, 0f, .2f, 1f))
+ .start();
+
+ mChevronPositionAnimator = ValueAnimator.ofFloat(-chevronDistance(), 0)
+ .setDuration(scaledDuration(250));
+ mChevronPositionAnimator.setInterpolator(new PathInterpolator(.4f, 0f, .2f, 1f));
+ mChevronPositionAnimator.start();
+
+ mChevron.setAlpha(0);
+ mChevron.animate()
+ .alpha(1)
+ .setStartDelay(scaledDuration(50))
+ .setDuration(scaledDuration(150))
+ .setInterpolator(new PathInterpolator(.4f, 0f, 1f, 1f))
+ .start();
+ }
+
+ public void startDismiss(final Runnable onComplete) {
+ if (D.BUG) Log.d(TAG, "startDismiss");
+ if (mDismissing) return;
+ setDismissing(true);
+ if (mShowing) {
+ mDialogView.animate().cancel();
+ mContentsPositionAnimator.cancel();
+ mContents.animate().cancel();
+ mChevronPositionAnimator.cancel();
+ mChevron.animate().cancel();
+ setShowing(false);
+ }
+ mDialogView.animate()
+ .translationY(-mDialogView.getHeight())
+ .setDuration(scaledDuration(250))
+ .setInterpolator(new LogAccelerateInterpolator())
+ .setUpdateListener(new AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ mContents.setTranslationY(-mDialogView.getTranslationY());
+ int posY = (Integer) mChevron.getTag();
+ mChevron.setTranslationY(posY + -mDialogView.getTranslationY());
+ }
+ })
+ .setListener(new AnimatorListenerAdapter() {
+ private boolean mCancelled;
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (mCancelled) return;
+ if (D.BUG) Log.d(TAG, "dismiss.onAnimationEnd");
+ mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ if (D.BUG) Log.d(TAG, "mDialog.dismiss()");
+ mDialog.dismiss();
+ onComplete.run();
+ setDismissing(false);
+ }
+ }, PRE_DISMISS_DELAY);
+
+ }
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ if (D.BUG) Log.d(TAG, "dismiss.onAnimationCancel");
+ mCancelled = true;
+ }
+ }).start();
+ }
+
+ private static int scaledDuration(int base) {
+ return (int) (base * ANIMATION_SCALE);
+ }
+
+ private static final class LogDecelerateInterpolator implements TimeInterpolator {
+ private final float mBase;
+ private final float mDrift;
+ private final float mTimeScale;
+ private final float mOutputScale;
+
+ private LogDecelerateInterpolator() {
+ this(400f, 1.4f, 0);
+ }
+
+ private LogDecelerateInterpolator(float base, float timeScale, float drift) {
+ mBase = base;
+ mDrift = drift;
+ mTimeScale = 1f / timeScale;
+
+ mOutputScale = 1f / computeLog(1f);
+ }
+
+ private float computeLog(float t) {
+ return 1f - (float) Math.pow(mBase, -t * mTimeScale) + (mDrift * t);
+ }
+
+ @Override
+ public float getInterpolation(float t) {
+ return computeLog(t) * mOutputScale;
+ }
+ }
+
+ private static final class LogAccelerateInterpolator implements TimeInterpolator {
+ private final int mBase;
+ private final int mDrift;
+ private final float mLogScale;
+
+ private LogAccelerateInterpolator() {
+ this(100, 0);
+ }
+
+ private LogAccelerateInterpolator(int base, int drift) {
+ mBase = base;
+ mDrift = drift;
+ mLogScale = 1f / computeLog(1, mBase, mDrift);
+ }
+
+ private static float computeLog(float t, int base, int drift) {
+ return (float) -Math.pow(base, -t) + 1 + (drift * t);
+ }
+
+ @Override
+ public float getInterpolation(float t) {
+ return 1 - computeLog(1 - t, mBase, mDrift) * mLogScale;
+ }
+ }
+
+ public interface Callback {
+ void onAnimatingChanged(boolean animating);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java b/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java
index 3f6294d..af7ee08 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java
@@ -16,6 +16,7 @@
package com.android.systemui.volume;
import android.animation.LayoutTransition;
+import android.animation.ValueAnimator;
import android.content.Context;
import android.provider.Settings.Global;
import android.service.notification.ZenModeConfig;
@@ -51,7 +52,9 @@ public class ZenFooter extends LinearLayout {
super(context, attrs);
mContext = context;
mSpTexts = new SpTexts(mContext);
- setLayoutTransition(new LayoutTransition());
+ final LayoutTransition layoutTransition = new LayoutTransition();
+ layoutTransition.setDuration(new ValueAnimator().getDuration() / 2);
+ setLayoutTransition(layoutTransition);
}
@Override