diff options
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 0ed1e2ac4cdb..7617ed425998 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 c6aa588ac5d6..1a6d34e4f965 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 869b03a8a7b5..005077fbf494 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 67d3312b25b3..188986274e77 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 065523f4458b..0ab0392d412b 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 000000000000..fdf18406de94 --- /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 3f6294d47634..af7ee0856cb6 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  |