19630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown/*
29630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown * Copyright (C) 2012 The Android Open Source Project
39630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown *
49630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown * Licensed under the Apache License, Version 2.0 (the "License");
59630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown * you may not use this file except in compliance with the License.
69630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown * You may obtain a copy of the License at
79630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown *
89630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown *      http://www.apache.org/licenses/LICENSE-2.0
99630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown *
109630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown * Unless required by applicable law or agreed to in writing, software
119630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown * distributed under the License is distributed on an "AS IS" BASIS,
129630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown * See the License for the specific language governing permissions and
149630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown * limitations under the License.
159630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown */
169630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown
17ad9ef191f50767d8d5b6f0fbd4b59bb1400dcd25Jeff Brownpackage com.android.server.display;
189630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown
199630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brownimport android.animation.ValueAnimator;
209630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brownimport android.util.IntProperty;
219630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brownimport android.view.Choreographer;
229630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown
239630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown/**
249630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown * A custom animator that progressively updates a property value at
259630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown * a given variable rate until it reaches a particular target value.
269630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown */
279630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brownfinal class RampAnimator<T> {
289630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown    private final T mObject;
299630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown    private final IntProperty<T> mProperty;
309630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown    private final Choreographer mChoreographer;
319630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown
329630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown    private int mCurrentValue;
339630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown    private int mTargetValue;
349630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown    private int mRate;
359630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown
369630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown    private boolean mAnimating;
379630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown    private float mAnimatedValue; // higher precision copy of mCurrentValue
389630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown    private long mLastFrameTimeNanos;
399630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown
409630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown    private boolean mFirstTime = true;
419630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown
424255869ac00e700395832ee73b2294603d4b6eceJeff Brown    private Listener mListener;
434255869ac00e700395832ee73b2294603d4b6eceJeff Brown
449630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown    public RampAnimator(T object, IntProperty<T> property) {
459630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown        mObject = object;
469630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown        mProperty = property;
479630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown        mChoreographer = Choreographer.getInstance();
489630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown    }
499630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown
509630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown    /**
519630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown     * Starts animating towards the specified value.
529630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown     *
530a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown     * If this is the first time the property is being set or if the rate is 0,
540a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown     * the value jumps directly to the target.
559630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown     *
569630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown     * @param target The target value.
570a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown     * @param rate The convergence rate in units per second, or 0 to set the value immediately.
589630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown     * @return True if the target differs from the previous target.
599630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown     */
609630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown    public boolean animateTo(int target, int rate) {
619630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown        // Immediately jump to the target the first time.
620a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown        if (mFirstTime || rate <= 0) {
630a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown            if (mFirstTime || target != mCurrentValue) {
640a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown                mFirstTime = false;
650a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown                mRate = 0;
660a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown                mTargetValue = target;
670a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown                mCurrentValue = target;
680a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown                mProperty.setValue(mObject, target);
690a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown                if (mAnimating) {
700a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown                    mAnimating = false;
710a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown                    cancelAnimationCallback();
720a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown                }
730a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown                if (mListener != null) {
740a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown                    mListener.onAnimationEnd();
750a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown                }
760a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown                return true;
770a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown            }
780a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown            return false;
799630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown        }
809630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown
819630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown        // Adjust the rate based on the closest target.
829630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown        // If a faster rate is specified, then use the new rate so that we converge
839630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown        // more rapidly based on the new request.
849630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown        // If a slower rate is specified, then use the new rate only if the current
859630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown        // value is somewhere in between the new and the old target meaning that
869630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown        // we will be ramping in a different direction to get there.
879630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown        // Otherwise, continue at the previous rate.
889630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown        if (!mAnimating
899630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown                || rate > mRate
909630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown                || (target <= mCurrentValue && mCurrentValue <= mTargetValue)
919630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown                || (mTargetValue <= mCurrentValue && mCurrentValue <= target)) {
929630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown            mRate = rate;
939630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown        }
949630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown
959630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown        final boolean changed = (mTargetValue != target);
969630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown        mTargetValue = target;
979630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown
989630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown        // Start animating.
999630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown        if (!mAnimating && target != mCurrentValue) {
1009630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown            mAnimating = true;
1019630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown            mAnimatedValue = mCurrentValue;
1029630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown            mLastFrameTimeNanos = System.nanoTime();
1030a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown            postAnimationCallback();
1049630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown        }
1059630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown
1069630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown        return changed;
1079630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown    }
1089630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown
1094255869ac00e700395832ee73b2294603d4b6eceJeff Brown    /**
1104255869ac00e700395832ee73b2294603d4b6eceJeff Brown     * Returns true if the animation is running.
1114255869ac00e700395832ee73b2294603d4b6eceJeff Brown     */
1124255869ac00e700395832ee73b2294603d4b6eceJeff Brown    public boolean isAnimating() {
1134255869ac00e700395832ee73b2294603d4b6eceJeff Brown        return mAnimating;
1144255869ac00e700395832ee73b2294603d4b6eceJeff Brown    }
1154255869ac00e700395832ee73b2294603d4b6eceJeff Brown
1164255869ac00e700395832ee73b2294603d4b6eceJeff Brown    /**
1174255869ac00e700395832ee73b2294603d4b6eceJeff Brown     * Sets a listener to watch for animation events.
1184255869ac00e700395832ee73b2294603d4b6eceJeff Brown     */
1194255869ac00e700395832ee73b2294603d4b6eceJeff Brown    public void setListener(Listener listener) {
1204255869ac00e700395832ee73b2294603d4b6eceJeff Brown        mListener = listener;
1214255869ac00e700395832ee73b2294603d4b6eceJeff Brown    }
1224255869ac00e700395832ee73b2294603d4b6eceJeff Brown
1230a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown    private void postAnimationCallback() {
1240a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown        mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, mAnimationCallback, null);
1250a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown    }
1260a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown
1270a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown    private void cancelAnimationCallback() {
1280a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown        mChoreographer.removeCallbacks(Choreographer.CALLBACK_ANIMATION, mAnimationCallback, null);
1299630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown    }
1309630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown
1310a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown    private final Runnable mAnimationCallback = new Runnable() {
1329630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown        @Override // Choreographer callback
1339630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown        public void run() {
1349630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown            final long frameTimeNanos = mChoreographer.getFrameTimeNanos();
1359630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown            final float timeDelta = (frameTimeNanos - mLastFrameTimeNanos)
1369630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown                    * 0.000000001f;
1379630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown            mLastFrameTimeNanos = frameTimeNanos;
1389630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown
1399630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown            // Advance the animated value towards the target at the specified rate
1409630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown            // and clamp to the target. This gives us the new current value but
1419630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown            // we keep the animated value around to allow for fractional increments
1429630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown            // towards the target.
143a7233fe31a8144f54eb041d12e631afdce25af4eCraig Mautner            final float scale = ValueAnimator.getDurationScale();
144a7233fe31a8144f54eb041d12e631afdce25af4eCraig Mautner            if (scale == 0) {
145a7233fe31a8144f54eb041d12e631afdce25af4eCraig Mautner                // Animation off.
146a7233fe31a8144f54eb041d12e631afdce25af4eCraig Mautner                mAnimatedValue = mTargetValue;
1479630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown            } else {
148a7233fe31a8144f54eb041d12e631afdce25af4eCraig Mautner                final float amount = timeDelta * mRate / scale;
149a7233fe31a8144f54eb041d12e631afdce25af4eCraig Mautner                if (mTargetValue > mCurrentValue) {
150a7233fe31a8144f54eb041d12e631afdce25af4eCraig Mautner                    mAnimatedValue = Math.min(mAnimatedValue + amount, mTargetValue);
151a7233fe31a8144f54eb041d12e631afdce25af4eCraig Mautner                } else {
152a7233fe31a8144f54eb041d12e631afdce25af4eCraig Mautner                    mAnimatedValue = Math.max(mAnimatedValue - amount, mTargetValue);
153a7233fe31a8144f54eb041d12e631afdce25af4eCraig Mautner                }
1549630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown            }
155a7233fe31a8144f54eb041d12e631afdce25af4eCraig Mautner            final int oldCurrentValue = mCurrentValue;
156a7233fe31a8144f54eb041d12e631afdce25af4eCraig Mautner            mCurrentValue = Math.round(mAnimatedValue);
1579630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown
1589630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown            if (oldCurrentValue != mCurrentValue) {
1599630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown                mProperty.setValue(mObject, mCurrentValue);
1609630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown            }
1619630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown
1629630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown            if (mTargetValue != mCurrentValue) {
1630a434776b836f8fbea1f84b7bfe158b4ddafc876Jeff Brown                postAnimationCallback();
1649630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown            } else {
1659630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown                mAnimating = false;
1664255869ac00e700395832ee73b2294603d4b6eceJeff Brown                if (mListener != null) {
1674255869ac00e700395832ee73b2294603d4b6eceJeff Brown                    mListener.onAnimationEnd();
1684255869ac00e700395832ee73b2294603d4b6eceJeff Brown                }
1699630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown            }
1709630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown        }
1719630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown    };
1724255869ac00e700395832ee73b2294603d4b6eceJeff Brown
1734255869ac00e700395832ee73b2294603d4b6eceJeff Brown    public interface Listener {
1744255869ac00e700395832ee73b2294603d4b6eceJeff Brown        void onAnimationEnd();
1754255869ac00e700395832ee73b2294603d4b6eceJeff Brown    }
1769630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown}
177