copy AnimatorSet and related classes from AOSP

This makes AnimatedVectorDrawableCompat from androidx functional and
probably also helps other animation stuff.
This commit is contained in:
Julian Winkler 2025-02-10 18:15:38 +01:00
parent 87b254156d
commit c7f1e05f5d
11 changed files with 6252 additions and 121 deletions

View file

@ -0,0 +1,518 @@
/*
* 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 android.animation;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import android.annotation.Nullable;
import android.os.SystemClock;
import android.util.ArrayMap;
import android.util.Log;
import android.view.Choreographer;
/**
* This custom, static handler handles the timing pulse that is shared by all active
* ValueAnimators. This approach ensures that the setting of animation values will happen on the
* same thread that animations start on, and that all animations will share the same times for
* calculating their values, which makes synchronizing animations possible.
*
* The handler uses the Choreographer by default for doing periodic callbacks. A custom
* AnimationFrameCallbackProvider can be set on the handler to provide timing pulse that
* may be independent of UI frame update. This could be useful in testing.
*
* @hide
*/
public class AnimationHandler {
private static final String TAG = "AnimationHandler";
private static final boolean LOCAL_LOGV = false;
/**
* Internal per-thread collections used to avoid set collisions as animations start and end
* while being processed.
*/
private final ArrayMap<AnimationFrameCallback, Long> mDelayedCallbackStartTime =
new ArrayMap<>();
private final ArrayList<AnimationFrameCallback> mAnimationCallbacks =
new ArrayList<>();
private final ArrayList<AnimationFrameCallback> mCommitCallbacks =
new ArrayList<>();
private AnimationFrameCallbackProvider mProvider;
// Static flag which allows the pausing behavior to be globally disabled/enabled.
private static boolean sAnimatorPausingEnabled = isPauseBgAnimationsEnabledInSystemProperties();
// Static flag which prevents the system property from overriding sAnimatorPausingEnabled field.
private static boolean sOverrideAnimatorPausingSystemProperty = false;
/**
* This paused list is used to store animators forcibly paused when the activity
* went into the background (to avoid unnecessary background processing work).
* These animators should be resume()'d when the activity returns to the foreground.
*/
private final ArrayList<Animator> mPausedAnimators = new ArrayList<>();
/**
* This structure is used to store the currently active objects (ViewRootImpls or
* WallpaperService.Engines) in the process. Each of these objects sends a request to
* AnimationHandler when it goes into the background (request to pause) or foreground
* (request to resume). Because all animators are managed by AnimationHandler on the same
* thread, it should only ever pause animators when *all* requestors are in the background.
* This list tracks the background/foreground state of all requestors and only ever
* pauses animators when all items are in the background (false). To simplify, we only ever
* store visible (foreground) requestors; if the set size reaches zero, there are no
* objects in the foreground and it is time to pause animators.
*/
private final ArrayList<WeakReference<Object>> mAnimatorRequestors = new ArrayList<>();
private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
doAnimationFrame(getProvider().getFrameTime());
if (mAnimationCallbacks.size() > 0) {
getProvider().postFrameCallback(this);
}
}
};
public final static ThreadLocal<AnimationHandler> sAnimatorHandler = new ThreadLocal<>();
private static AnimationHandler sTestHandler = null;
private boolean mListDirty = false;
public static AnimationHandler getInstance() {
if (sTestHandler != null) {
return sTestHandler;
}
if (sAnimatorHandler.get() == null) {
sAnimatorHandler.set(new AnimationHandler());
}
return sAnimatorHandler.get();
}
/**
* Sets an instance that will be returned by {@link #getInstance()} on every thread.
* @return the previously active test handler, if any.
* @hide
*/
public static @Nullable AnimationHandler setTestHandler(@Nullable AnimationHandler handler) {
AnimationHandler oldHandler = sTestHandler;
sTestHandler = handler;
return oldHandler;
}
/**
* System property that controls the behavior of pausing infinite animators when an app
* is moved to the background.
*
* @return the value of 'framework.pause_bg_animations.enabled' system property
*/
private static boolean isPauseBgAnimationsEnabledInSystemProperties() {
if (sOverrideAnimatorPausingSystemProperty) return sAnimatorPausingEnabled;
return true; /*SystemProperties
.getBoolean("framework.pause_bg_animations.enabled", true);*/
}
/**
* Disable the default behavior of pausing infinite animators when
* apps go into the background.
*
* @param enable Enable (default behavior) or disable background pausing behavior.
*/
public static void setAnimatorPausingEnabled(boolean enable) {
sAnimatorPausingEnabled = enable;
}
/**
* Prevents the setAnimatorPausingEnabled behavior from being overridden
* by the 'framework.pause_bg_animations.enabled' system property value.
*
* This is for testing purposes only.
*
* @param enable Enable or disable (default behavior) overriding the system
* property.
*/
public static void setOverrideAnimatorPausingSystemProperty(boolean enable) {
sOverrideAnimatorPausingSystemProperty = enable;
}
/**
* This is called when a window goes away. We should remove
* it from the requestors list to ensure that we are counting requests correctly and not
* tracking obsolete+enabled requestors.
*/
public static void removeRequestor(Object requestor) {
getInstance().requestAnimatorsEnabledImpl(false, requestor);
if (LOCAL_LOGV) {
Log.v(TAG, "removeRequestor for " + requestor);
}
}
/**
* This method is called from ViewRootImpl or WallpaperService when either a window is no
* longer visible (enable == false) or when a window becomes visible (enable == true).
* If animators are not properly disabled when activities are backgrounded, it can lead to
* unnecessary processing, particularly for infinite animators, as the system will continue
* to pulse timing events even though the results are not visible. As a workaround, we
* pause all un-paused infinite animators, and resume them when any window in the process
* becomes visible.
*/
public static void requestAnimatorsEnabled(boolean enable, Object requestor) {
getInstance().requestAnimatorsEnabledImpl(enable, requestor);
}
private void requestAnimatorsEnabledImpl(boolean enable, Object requestor) {
boolean wasEmpty = mAnimatorRequestors.isEmpty();
setAnimatorPausingEnabled(isPauseBgAnimationsEnabledInSystemProperties());
synchronized (mAnimatorRequestors) {
// Only store WeakRef objects to avoid leaks
if (enable) {
// First, check whether such a reference is already on the list
WeakReference<Object> weakRef = null;
for (int i = mAnimatorRequestors.size() - 1; i >= 0; --i) {
WeakReference<Object> ref = mAnimatorRequestors.get(i);
Object referent = ref.get();
if (referent == requestor) {
weakRef = ref;
} else if (referent == null) {
// Remove any reference that has been cleared
mAnimatorRequestors.remove(i);
}
}
if (weakRef == null) {
weakRef = new WeakReference<>(requestor);
mAnimatorRequestors.add(weakRef);
}
} else {
for (int i = mAnimatorRequestors.size() - 1; i >= 0; --i) {
WeakReference<Object> ref = mAnimatorRequestors.get(i);
Object referent = ref.get();
if (referent == requestor || referent == null) {
// remove requested item or item that has been cleared
mAnimatorRequestors.remove(i);
}
}
// If a reference to the requestor wasn't in the list, nothing to remove
}
}
if (!sAnimatorPausingEnabled) {
// Resume any animators that have been paused in the meantime, otherwise noop
// Leave logic above so that if pausing gets re-enabled, the state of the requestors
// list is valid
resumeAnimators();
return;
}
boolean isEmpty = mAnimatorRequestors.isEmpty();
if (wasEmpty != isEmpty) {
// only paused/resume animators if there was a visibility change
if (!isEmpty) {
// If any requestors are enabled, resume currently paused animators
resumeAnimators();
} else {
// Wait before pausing to avoid thrashing animator state for temporary backgrounding
Choreographer.getInstance().postFrameCallbackDelayed(mPauser,
/*Animator.getBackgroundPauseDelay()*/100);
}
}
if (LOCAL_LOGV) {
Log.v(TAG, (enable ? "enable" : "disable") + " animators for " + requestor
+ " with pauseDelay of " + /*Animator.getBackgroundPauseDelay()*/100);
for (int i = 0; i < mAnimatorRequestors.size(); ++i) {
Log.v(TAG, "animatorRequestors " + i + " = "
+ mAnimatorRequestors.get(i) + " with referent "
+ mAnimatorRequestors.get(i).get());
}
}
}
private void resumeAnimators() {
Choreographer.getInstance().removeFrameCallback(mPauser);
for (int i = mPausedAnimators.size() - 1; i >= 0; --i) {
mPausedAnimators.get(i).resume();
}
mPausedAnimators.clear();
}
private Choreographer.FrameCallback mPauser = new Choreographer.FrameCallback() { @Override public void doFrame(long frameTimeNanos) {
if (mAnimatorRequestors.size() > 0) {
// something enabled animators since this callback was scheduled - bail
return;
}
for (int i = 0; i < mAnimationCallbacks.size(); ++i) {
AnimationFrameCallback callback = mAnimationCallbacks.get(i);
if (callback instanceof Animator) {
Animator animator = ((Animator) callback);
if (animator.getTotalDuration() == Animator.DURATION_INFINITE
&& !animator.isPaused()) {
mPausedAnimators.add(animator);
animator.pause();
}
}
}
}};
/**
* By default, the Choreographer is used to provide timing for frame callbacks. A custom
* provider can be used here to provide different timing pulse.
*/
public void setProvider(AnimationFrameCallbackProvider provider) {
if (provider == null) {
mProvider = new MyFrameCallbackProvider();
} else {
mProvider = provider;
}
}
private AnimationFrameCallbackProvider getProvider() {
if (mProvider == null) {
mProvider = new MyFrameCallbackProvider();
}
return mProvider;
}
/**
* Register to get a callback on the next frame after the delay.
*/
public void addAnimationFrameCallback(final AnimationFrameCallback callback, long delay) {
if (mAnimationCallbacks.size() == 0) {
getProvider().postFrameCallback(mFrameCallback);
}
if (!mAnimationCallbacks.contains(callback)) {
mAnimationCallbacks.add(callback);
}
if (delay > 0) {
mDelayedCallbackStartTime.put(callback, (SystemClock.uptimeMillis() + delay));
}
}
/**
* Register to get a one shot callback for frame commit timing. Frame commit timing is the
* time *after* traversals are done, as opposed to the animation frame timing, which is
* before any traversals. This timing can be used to adjust the start time of an animation
* when expensive traversals create big delta between the animation frame timing and the time
* that animation is first shown on screen.
*
* Note this should only be called when the animation has already registered to receive
* animation frame callbacks. This callback will be guaranteed to happen *after* the next
* animation frame callback.
*/
public void addOneShotCommitCallback(final AnimationFrameCallback callback) {
if (!mCommitCallbacks.contains(callback)) {
mCommitCallbacks.add(callback);
}
}
/**
* Removes the given callback from the list, so it will no longer be called for frame related
* timing.
*/
public void removeCallback(AnimationFrameCallback callback) {
mCommitCallbacks.remove(callback);
mDelayedCallbackStartTime.remove(callback);
int id = mAnimationCallbacks.indexOf(callback);
if (id >= 0) {
mAnimationCallbacks.set(id, null);
mListDirty = true;
}
}
private void doAnimationFrame(long frameTime) {
long currentTime = SystemClock.uptimeMillis();
final int size = mAnimationCallbacks.size();
for (int i = 0; i < size; i++) {
final AnimationFrameCallback callback = mAnimationCallbacks.get(i);
if (callback == null) {
continue;
}
if (isCallbackDue(callback, currentTime)) {
callback.doAnimationFrame(frameTime);
if (mCommitCallbacks.contains(callback)) {
getProvider().postCommitCallback(new Runnable() {
@Override
public void run() {
commitAnimationFrame(callback, getProvider().getFrameTime());
}
});
}
}
}
cleanUpList();
}
private void commitAnimationFrame(AnimationFrameCallback callback, long frameTime) {
if (!mDelayedCallbackStartTime.containsKey(callback) &&
mCommitCallbacks.contains(callback)) {
callback.commitAnimationFrame(frameTime);
mCommitCallbacks.remove(callback);
}
}
/**
* Remove the callbacks from mDelayedCallbackStartTime once they have passed the initial delay
* so that they can start getting frame callbacks.
*
* @return true if they have passed the initial delay or have no delay, false otherwise.
*/
private boolean isCallbackDue(AnimationFrameCallback callback, long currentTime) {
Long startTime = mDelayedCallbackStartTime.get(callback);
if (startTime == null) {
return true;
}
if (startTime < currentTime) {
mDelayedCallbackStartTime.remove(callback);
return true;
}
return false;
}
/**
* Return the number of callbacks that have registered for frame callbacks.
*/
public static int getAnimationCount() {
AnimationHandler handler = sTestHandler;
if (handler == null) {
handler = sAnimatorHandler.get();
}
if (handler == null) {
return 0;
}
return handler.getCallbackSize();
}
public static void setFrameDelay(long delay) {
getInstance().getProvider().setFrameDelay(delay);
}
public static long getFrameDelay() {
return getInstance().getProvider().getFrameDelay();
}
void autoCancelBasedOn(ObjectAnimator objectAnimator) {
for (int i = mAnimationCallbacks.size() - 1; i >= 0; i--) {
AnimationFrameCallback cb = mAnimationCallbacks.get(i);
if (cb == null) {
continue;
}
if (objectAnimator.shouldAutoCancel(cb)) {
((Animator) mAnimationCallbacks.get(i)).cancel();
}
}
}
private void cleanUpList() {
if (mListDirty) {
for (int i = mAnimationCallbacks.size() - 1; i >= 0; i--) {
if (mAnimationCallbacks.get(i) == null) {
mAnimationCallbacks.remove(i);
}
}
mListDirty = false;
}
}
private int getCallbackSize() {
int count = 0;
int size = mAnimationCallbacks.size();
for (int i = size - 1; i >= 0; i--) {
if (mAnimationCallbacks.get(i) != null) {
count++;
}
}
return count;
}
/**
* Default provider of timing pulse that uses Choreographer for frame callbacks.
*/
private class MyFrameCallbackProvider implements AnimationFrameCallbackProvider {
final Choreographer mChoreographer = Choreographer.getInstance();
@Override
public void postFrameCallback(Choreographer.FrameCallback callback) {
mChoreographer.postFrameCallback(callback);
}
@Override
public void postCommitCallback(Runnable runnable) {
mChoreographer.postCallback(/*Choreographer.CALLBACK_COMMIT*/4, runnable, null);
}
@Override
public long getFrameTime() {
return mChoreographer.getFrameTime();
}
@Override
public long getFrameDelay() {
return Choreographer.getFrameDelay();
}
@Override
public void setFrameDelay(long delay) {
Choreographer.setFrameDelay(delay);
}
}
/**
* Callbacks that receives notifications for animation timing and frame commit timing.
* @hide
*/
public interface AnimationFrameCallback {
/**
* Run animation based on the frame time.
* @param frameTime The frame start time, in the {@link SystemClock#uptimeMillis()} time
* base.
* @return if the animation has finished.
*/
boolean doAnimationFrame(long frameTime);
/**
* This notifies the callback of frame commit time. Frame commit time is the time after
* traversals happen, as opposed to the normal animation frame time that is before
* traversals. This is used to compensate expensive traversals that happen as the
* animation starts. When traversals take a long time to complete, the rendering of the
* initial frame will be delayed (by a long time). But since the startTime of the
* animation is set before the traversal, by the time of next frame, a lot of time would
* have passed since startTime was set, the animation will consequently skip a few frames
* to respect the new frameTime. By having the commit time, we can adjust the start time to
* when the first frame was drawn (after any expensive traversals) so that no frames
* will be skipped.
*
* @param frameTime The frame time after traversals happen, if any, in the
* {@link SystemClock#uptimeMillis()} time base.
*/
void commitAnimationFrame(long frameTime);
}
/**
* The intention for having this interface is to increase the testability of ValueAnimator.
* Specifically, we can have a custom implementation of the interface below and provide
* timing pulse without using Choreographer. That way we could use any arbitrary interval for
* our timing pulse in the tests.
*
* @hide
*/
public interface AnimationFrameCallbackProvider {
void postFrameCallback(Choreographer.FrameCallback callback);
void postCommitCallback(Runnable runnable);
long getFrameTime();
long getFrameDelay();
void setFrameDelay(long delay);
}
}

View file

@ -1,55 +1,824 @@
/*
* Copyright (C) 2010 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 android.animation;
import android.os.Handler;
import android.os.Looper;
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicReference;
public class Animator {
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.res.ConstantState;
import android.util.LongArray;
public interface AnimatorListener {
public abstract void onAnimationEnd (Animator animation);
}
/**
* This is the superclass for classes which provide basic support for animations which can be
* started, ended, and have <code>AnimatorListeners</code> added to them.
*/
public abstract class Animator implements Cloneable {
private long duration = 50;
private AnimatorListener listener;
/**
* The value used to indicate infinite duration (e.g. when Animators repeat infinitely).
*/
public static final long DURATION_INFINITE = -1;
/**
* The set of listeners to be sent events through the life of an animation.
*/
ArrayList<AnimatorListener> mListeners = null;
public void setTarget(Object target) {}
/**
* The set of listeners to be sent pause/resume events through the life
* of an animation.
*/
ArrayList<AnimatorPauseListener> mPauseListeners = null;
/**
* Whether this animator is currently in a paused state.
*/
boolean mPaused = false;
/**
* A set of flags which identify the type of configuration changes that can affect this
* Animator. Used by the Animator cache.
*/
int mChangingConfigurations = 0;
/**
* If this animator is inflated from a constant state, keep a reference to it so that
* ConstantState will not be garbage collected until this animator is collected
*/
private AnimatorConstantState mConstantState;
/**
* A cache of the values in a list. Used so that when calling the list, we have a copy
* of it in case the list is modified while iterating. The array can be reused to avoid
* allocation on every notification.
*/
private AtomicReference<Object[]> mCachedList = new AtomicReference<>();
/**
* Tracks whether we've notified listeners of the onAnimationStart() event. This can be
* complex to keep track of since we notify listeners at different times depending on
* startDelay and whether start() was called before end().
*/
boolean mStartListenersCalled = false;
/**
* Starts this animation. If the animation has a nonzero startDelay, the animation will start
* running after that delay elapses. A non-delayed animation will have its initial
* value(s) set immediately, followed by calls to
* {@link AnimatorListener#onAnimationStart(Animator)} for any listeners of this animator.
*
* <p>The animation started by calling this method will be run on the thread that called
* this method. This thread should have a Looper on it (a runtime exception will be thrown if
* this is not the case). Also, if the animation will animate
* properties of objects in the view hierarchy, then the calling thread should be the UI
* thread for that view hierarchy.</p>
*
*/
public void start() {
if (listener == null)
return;
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
public void run() {
if (listener != null)
listener.onAnimationEnd(Animator.this);
}
}, duration);
}
/**
* Cancels the animation. Unlike {@link #end()}, <code>cancel()</code> causes the animation to
* stop in its tracks, sending an
* {@link android.animation.Animator.AnimatorListener#onAnimationCancel(Animator)} to
* its listeners, followed by an
* {@link android.animation.Animator.AnimatorListener#onAnimationEnd(Animator)} message.
*
* <p>This method must be called on the thread that is running the animation.</p>
*/
public void cancel() {
}
/**
* Ends the animation. This causes the animation to assign the end value of the property being
* animated, then calling the
* {@link android.animation.Animator.AnimatorListener#onAnimationEnd(Animator)} method on
* its listeners.
*
* <p>This method must be called on the thread that is running the animation.</p>
*/
public void end() {
}
/**
* Pauses a running animation. This method should only be called on the same thread on
* which the animation was started. If the animation has not yet been {@link
* #isStarted() started} or has since ended, then the call is ignored. Paused
* animations can be resumed by calling {@link #resume()}.
*
* @see #resume()
* @see #isPaused()
* @see AnimatorPauseListener
*/
public void pause() {
// We only want to pause started Animators or animators that setCurrentPlayTime()
// have been called on. mStartListenerCalled will be true if seek has happened.
if ((isStarted() || mStartListenersCalled) && !mPaused) {
mPaused = true;
notifyPauseListeners(AnimatorCaller.ON_PAUSE);
}
}
/**
* Resumes a paused animation, causing the animator to pick up where it left off
* when it was paused. This method should only be called on the same thread on
* which the animation was started. Calls to resume() on an animator that is
* not currently paused will be ignored.
*
* @see #pause()
* @see #isPaused()
* @see AnimatorPauseListener
*/
public void resume() {
if (mPaused) {
mPaused = false;
notifyPauseListeners(AnimatorCaller.ON_RESUME);
}
}
/**
* Returns whether this animator is currently in a paused state.
*
* @return True if the animator is currently paused, false otherwise.
*
* @see #pause()
* @see #resume()
*/
public boolean isPaused() {
return mPaused;
}
/**
* The amount of time, in milliseconds, to delay processing the animation
* after {@link #start()} is called.
*
* @return the number of milliseconds to delay running the animation
*/
public abstract long getStartDelay();
/**
* The amount of time, in milliseconds, to delay processing the animation
* after {@link #start()} is called.
* @param startDelay The amount of the delay, in milliseconds
*/
public abstract void setStartDelay(long startDelay);
/**
* Sets the duration of the animation.
*
* @param duration The length of the animation, in milliseconds.
*/
public abstract Animator setDuration(long duration);
/**
* Gets the duration of the animation.
*
* @return The length of the animation, in milliseconds.
*/
public abstract long getDuration();
/**
* Gets the total duration of the animation, accounting for animation sequences, start delay,
* and repeating. Return {@link #DURATION_INFINITE} if the duration is infinite.
*
* @return Total time an animation takes to finish, starting from the time {@link #start()}
* is called. {@link #DURATION_INFINITE} will be returned if the animation or any
* child animation repeats infinite times.
*/
public long getTotalDuration() {
long duration = getDuration();
if (duration == DURATION_INFINITE) {
return DURATION_INFINITE;
} else {
return getStartDelay() + duration;
}
}
/**
* The time interpolator used in calculating the elapsed fraction of the
* animation. The interpolator determines whether the animation runs with
* linear or non-linear motion, such as acceleration and deceleration. The
* default value is {@link android.view.animation.AccelerateDecelerateInterpolator}.
*
* @param value the interpolator to be used by this animation
*/
public abstract void setInterpolator(TimeInterpolator value);
/**
* Returns the timing interpolator that this animation uses.
*
* @return The timing interpolator for this animation.
*/
public TimeInterpolator getInterpolator() {
return null;
}
/**
* Returns whether this Animator is currently running (having been started and gone past any
* initial startDelay period and not yet ended).
*
* @return Whether the Animator is running.
*/
public abstract boolean isRunning();
/**
* Returns whether this Animator has been started and not yet ended. For reusable
* Animators (which most Animators are, apart from the one-shot animator produced by
* {@link android.view.ViewAnimationUtils#createCircularReveal(
* android.view.View, int, int, float, float) createCircularReveal()}),
* this state is a superset of {@link #isRunning()}, because an Animator with a
* nonzero {@link #getStartDelay() startDelay} will return true for {@link #isStarted()} during
* the delay phase, whereas {@link #isRunning()} will return true only after the delay phase
* is complete. Non-reusable animators will always return true after they have been
* started, because they cannot return to a non-started state.
*
* @return Whether the Animator has been started and not yet ended.
*/
public boolean isStarted() {
// Default method returns value for isRunning(). Subclasses should override to return a
// real value.
return isRunning();
}
/**
* Adds a listener to the set of listeners that are sent events through the life of an
* animation, such as start, repeat, and end.
*
* @param listener the listener to be added to the current set of listeners for this animation.
*/
public void addListener(AnimatorListener listener) {
this.listener = listener;
if (mListeners == null) {
mListeners = new ArrayList<AnimatorListener>();
}
mListeners.add(listener);
}
public void cancel() {}
public long getStartDelay() { return 0; }
public long getDuration() { return duration; }
public Animator setDuration(long duration) {
this.duration = duration;
return this;
/**
* Removes a listener from the set listening to this animation.
*
* @param listener the listener to be removed from the current set of listeners for this
* animation.
*/
public void removeListener(AnimatorListener listener) {
if (mListeners == null) {
return;
}
mListeners.remove(listener);
if (mListeners.size() == 0) {
mListeners = null;
}
}
public void setInterpolator(TimeInterpolator i) {}
/**
* Gets the set of {@link android.animation.Animator.AnimatorListener} objects that are currently
* listening for events on this <code>Animator</code> object.
*
* @return ArrayList<AnimatorListener> The set of listeners.
*/
public ArrayList<AnimatorListener> getListeners() {
return mListeners;
}
public void setStartDelay(long startDelay) {}
/**
* Adds a pause listener to this animator.
*
* @param listener the listener to be added to the current set of pause listeners
* for this animation.
*/
public void addPauseListener(AnimatorPauseListener listener) {
if (mPauseListeners == null) {
mPauseListeners = new ArrayList<AnimatorPauseListener>();
}
mPauseListeners.add(listener);
}
public boolean isStarted() { return false; }
/**
* Removes a pause listener from the set listening to this animation.
*
* @param listener the listener to be removed from the current set of pause
* listeners for this animation.
*/
public void removePauseListener(AnimatorPauseListener listener) {
if (mPauseListeners == null) {
return;
}
mPauseListeners.remove(listener);
if (mPauseListeners.size() == 0) {
mPauseListeners = null;
}
}
public void end() {}
/**
* Removes all {@link #addListener(android.animation.Animator.AnimatorListener) listeners}
* and {@link #addPauseListener(android.animation.Animator.AnimatorPauseListener)
* pauseListeners} from this object.
*/
public void removeAllListeners() {
if (mListeners != null) {
mListeners.clear();
mListeners = null;
}
if (mPauseListeners != null) {
mPauseListeners.clear();
mPauseListeners = null;
}
}
public TimeInterpolator getInterpolator() { return null; }
/**
* Return a mask of the configuration parameters for which this animator may change, requiring
* that it should be re-created from Resources. The default implementation returns whatever
* value was provided through setChangingConfigurations(int) or 0 by default.
*
* @return Returns a mask of the changing configuration parameters, as defined by
* {@link android.content.pm.ActivityInfo}.
* @see android.content.pm.ActivityInfo
* @hide
*/
public int getChangingConfigurations() {
return mChangingConfigurations;
}
public boolean isRunning() { return false; }
/**
* Set a mask of the configuration parameters for which this animator may change, requiring
* that it be re-created from resource.
*
* @param configs A mask of the changing configuration parameters, as
* defined by {@link android.content.pm.ActivityInfo}.
*
* @see android.content.pm.ActivityInfo
* @hide
*/
public void setChangingConfigurations(int configs) {
mChangingConfigurations = configs;
}
/**
* Sets the changing configurations value to the union of the current changing configurations
* and the provided configs.
* This method is called while loading the animator.
* @hide
*/
public void appendChangingConfigurations(int configs) {
mChangingConfigurations |= configs;
}
/**
* Return a {@link android.content.res.ConstantState} instance that holds the shared state of
* this Animator.
* <p>
* This constant state is used to create new instances of this animator when needed, instead
* of re-loading it from resources. Default implementation creates a new
* {@link AnimatorConstantState}. You can override this method to provide your custom logic or
* return null if you don't want this animator to be cached.
*
* @return The ConfigurationBoundResourceCache.BaseConstantState associated to this Animator.
* @see android.content.res.ConstantState
* @see #clone()
* @hide
*/
public ConstantState<Animator> createConstantState() {
return new AnimatorConstantState(this);
}
@Override
public Animator clone() {
try {
final Animator anim = (Animator) super.clone();
if (mListeners != null) {
anim.mListeners = new ArrayList<AnimatorListener>(mListeners);
}
if (mPauseListeners != null) {
anim.mPauseListeners = new ArrayList<AnimatorPauseListener>(mPauseListeners);
}
anim.mCachedList.set(null);
anim.mStartListenersCalled = false;
return anim;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
/**
* This method tells the object to use appropriate information to extract
* starting values for the animation. For example, a AnimatorSet object will pass
* this call to its child objects to tell them to set up the values. A
* ObjectAnimator object will use the information it has about its target object
* and PropertyValuesHolder objects to get the start values for its properties.
* A ValueAnimator object will ignore the request since it does not have enough
* information (such as a target object) to gather these values.
*/
public void setupStartValues() {
}
/**
* This method tells the object to use appropriate information to extract
* ending values for the animation. For example, a AnimatorSet object will pass
* this call to its child objects to tell them to set up the values. A
* ObjectAnimator object will use the information it has about its target object
* and PropertyValuesHolder objects to get the start values for its properties.
* A ValueAnimator object will ignore the request since it does not have enough
* information (such as a target object) to gather these values.
*/
public void setupEndValues() {
}
/**
* Sets the target object whose property will be animated by this animation. Not all subclasses
* operate on target objects (for example, {@link ValueAnimator}, but this method
* is on the superclass for the convenience of dealing generically with those subclasses
* that do handle targets.
* <p>
* <strong>Note:</strong> The target is stored as a weak reference internally to avoid leaking
* resources by having animators directly reference old targets. Therefore, you should
* ensure that animator targets always have a hard reference elsewhere.
*
* @param target The object being animated
*/
public void setTarget(@Nullable Object target) {
}
// Hide reverse() and canReverse() for now since reverse() only work for simple
// cases, like we don't support sequential, neither startDelay.
// TODO: make reverse() works for all the Animators.
/**
* @hide
*/
public boolean canReverse() {
return false;
}
/**
* @hide
*/
public void reverse() {
throw new IllegalStateException("Reverse is not supported");
}
// Pulse an animation frame into the animation.
boolean pulseAnimationFrame(long frameTime) {
// TODO: Need to find a better signal than this. There's a bug in SystemUI that's preventing
// returning !isStarted() from working.
return false;
}
/**
* Internal use only.
* This call starts the animation in regular or reverse direction without requiring them to
* register frame callbacks. The caller will be responsible for all the subsequent animation
* pulses. Specifically, the caller needs to call doAnimationFrame(...) for the animation on
* every frame.
*
* @param inReverse whether the animation should play in reverse direction
*/
void startWithoutPulsing(boolean inReverse) {
if (inReverse) {
reverse();
} else {
start();
}
}
/**
* Internal use only.
* Skips the animation value to end/start, depending on whether the play direction is forward
* or backward.
*
* @param inReverse whether the end value is based on a reverse direction. If yes, this is
* equivalent to skip to start value in a forward playing direction.
*/
void skipToEndValue(boolean inReverse) {}
/**
* Internal use only.
*
* Returns whether the animation has start/end values setup. For most of the animations, this
* should always be true. For ObjectAnimators, the start values are setup in the initialization
* of the animation.
*/
boolean isInitialized() {
return true;
}
/**
* Internal use only. Changes the value of the animator as if currentPlayTime has passed since
* the start of the animation. Therefore, currentPlayTime includes the start delay, and any
* repetition. lastPlayTime is similar and is used to calculate how many repeats have been
* done between the two times.
*/
void animateValuesInRange(long currentPlayTime, long lastPlayTime) {}
/**
* Internal use only. This animates any animation that has ended since lastPlayTime.
* If an animation hasn't been finished, no change will be made.
*/
void animateSkipToEnds(long currentPlayTime, long lastPlayTime) {}
/**
* Internal use only. Adds all start times (after delay) to and end times to times.
* The value must include offset.
*/
void getStartAndEndTimes(LongArray times, long offset) {
long startTime = offset + getStartDelay();
if (times.indexOf(startTime) < 0) {
times.add(startTime);
}
long duration = getTotalDuration();
if (duration != DURATION_INFINITE) {
long endTime = duration + offset;
if (times.indexOf(endTime) < 0) {
times.add(endTime);
}
}
}
/**
* Calls notification for each AnimatorListener.
*
* @param notification The notification method to call on each listener.
* @param isReverse When this is used with start/end, this is the isReverse parameter. For
* other calls, this is ignored.
*/
void notifyListeners(
AnimatorCaller<AnimatorListener, Animator> notification,
boolean isReverse
) {
callOnList(mListeners, notification, this, isReverse);
}
/**
* Call pause/resume on each AnimatorPauseListener.
*
* @param notification Either ON_PAUSE or ON_RESUME to call onPause or onResume on each
* listener.
*/
void notifyPauseListeners(AnimatorCaller<AnimatorPauseListener, Animator> notification) {
callOnList(mPauseListeners, notification, this, false);
}
void notifyStartListeners(boolean isReversing) {
boolean startListenersCalled = mStartListenersCalled;
mStartListenersCalled = true;
if (mListeners != null && !startListenersCalled) {
notifyListeners(AnimatorCaller.ON_START, isReversing);
}
}
void notifyEndListeners(boolean isReversing) {
boolean startListenersCalled = mStartListenersCalled;
mStartListenersCalled = false;
if (mListeners != null && startListenersCalled) {
notifyListeners(AnimatorCaller.ON_END, isReversing);
}
}
/**
* Calls <code>call</code> for every item in <code>list</code> with <code>animator</code> and
* <code>isReverse</code> as parameters.
*
* @param list The list of items to make calls on.
* @param call The method to call for each item in list.
* @param animator The animator parameter of call.
* @param isReverse The isReverse parameter of call.
* @param <T> The item type of list
* @param <A> The Animator type of animator.
*/
<T, A> void callOnList(
ArrayList<T> list,
AnimatorCaller<T, A> call,
A animator,
boolean isReverse
) {
int size = list == null ? 0 : list.size();
if (size > 0) {
// Try to reuse mCacheList to store the items of list.
Object[] array = mCachedList.getAndSet(null);
if (array == null || array.length < size) {
array = new Object[size];
}
list.toArray(array);
for (int i = 0; i < size; i++) {
//noinspection unchecked
T item = (T) array[i];
call.call(item, animator, isReverse);
array[i] = null;
}
// Store it for the next call so we can reuse this array, if needed.
mCachedList.compareAndSet(null, array);
}
}
/**
* <p>An animation listener receives notifications from an animation.
* Notifications indicate animation related events, such as the end or the
* repetition of the animation.</p>
*/
public static interface AnimatorListener {
/**
* <p>Notifies the start of the animation as well as the animation's overall play direction.
* This method's default behavior is to call {@link #onAnimationStart(Animator)}. This
* method can be overridden, though not required, to get the additional play direction info
* when an animation starts. Skipping calling super when overriding this method results in
* {@link #onAnimationStart(Animator)} not getting called.
*
* @param animation The started animation.
* @param isReverse Whether the animation is playing in reverse.
*/
default void onAnimationStart(@NonNull Animator animation, boolean isReverse) {
onAnimationStart(animation);
}
/**
* <p>Notifies the end of the animation. This callback is not invoked
* for animations with repeat count set to INFINITE.</p>
*
* <p>This method's default behavior is to call {@link #onAnimationEnd(Animator)}. This
* method can be overridden, though not required, to get the additional play direction info
* when an animation ends. Skipping calling super when overriding this method results in
* {@link #onAnimationEnd(Animator)} not getting called.
*
* @param animation The animation which reached its end.
* @param isReverse Whether the animation is playing in reverse.
*/
default void onAnimationEnd(@NonNull Animator animation, boolean isReverse) {
onAnimationEnd(animation);
}
/**
* <p>Notifies the start of the animation.</p>
*
* @param animation The started animation.
*/
void onAnimationStart(@NonNull Animator animation);
/**
* <p>Notifies the end of the animation. This callback is not invoked
* for animations with repeat count set to INFINITE.</p>
*
* @param animation The animation which reached its end.
*/
void onAnimationEnd(@NonNull Animator animation);
/**
* <p>Notifies the cancellation of the animation. This callback is not invoked
* for animations with repeat count set to INFINITE.</p>
*
* @param animation The animation which was canceled.
*/
void onAnimationCancel(@NonNull Animator animation);
/**
* <p>Notifies the repetition of the animation.</p>
*
* @param animation The animation which was repeated.
*/
void onAnimationRepeat(@NonNull Animator animation);
}
/**
* A pause listener receives notifications from an animation when the
* animation is {@link #pause() paused} or {@link #resume() resumed}.
*
* @see #addPauseListener(AnimatorPauseListener)
*/
public static interface AnimatorPauseListener {
/**
* <p>Notifies that the animation was paused.</p>
*
* @param animation The animaton being paused.
* @see #pause()
*/
void onAnimationPause(@NonNull Animator animation);
/**
* <p>Notifies that the animation was resumed, after being
* previously paused.</p>
*
* @param animation The animation being resumed.
* @see #resume()
*/
void onAnimationResume(@NonNull Animator animation);
}
/**
* <p>Whether or not the Animator is allowed to run asynchronously off of
* the UI thread. This is a hint that informs the Animator that it is
* OK to run the animation off-thread, however the Animator may decide
* that it must run the animation on the UI thread anyway.
*
* <p>Regardless of whether or not the animation runs asynchronously, all
* listener callbacks will be called on the UI thread.</p>
*
* <p>To be able to use this hint the following must be true:</p>
* <ol>
* <li>The animator is immutable while {@link #isStarted()} is true. Requests
* to change duration, delay, etc... may be ignored.</li>
* <li>Lifecycle callback events may be asynchronous. Events such as
* {@link Animator.AnimatorListener#onAnimationEnd(Animator)} or
* {@link Animator.AnimatorListener#onAnimationRepeat(Animator)} may end up delayed
* as they must be posted back to the UI thread, and any actions performed
* by those callbacks (such as starting new animations) will not happen
* in the same frame.</li>
* <li>State change requests ({@link #cancel()}, {@link #end()}, {@link #reverse()}, etc...)
* may be asynchronous. It is guaranteed that all state changes that are
* performed on the UI thread in the same frame will be applied as a single
* atomic update, however that frame may be the current frame,
* the next frame, or some future frame. This will also impact the observed
* state of the Animator. For example, {@link #isStarted()} may still return true
* after a call to {@link #end()}. Using the lifecycle callbacks is preferred over
* queries to {@link #isStarted()}, {@link #isRunning()}, and {@link #isPaused()}
* for this reason.</li>
* </ol>
* @hide
*/
public void setAllowRunningAsynchronously(boolean mayRunAsync) {
// It is up to subclasses to support this, if they can.
}
/**
* Creates a {@link ConstantState} which holds changing configurations information associated
* with the given Animator.
* <p>
* When {@link #newInstance()} is called, default implementation clones the Animator.
*/
private static class AnimatorConstantState extends ConstantState<Animator> {
final Animator mAnimator;
int mChangingConf;
public AnimatorConstantState(Animator animator) {
mAnimator = animator;
// ensure a reference back to here so that constante state is not gc'ed.
mAnimator.mConstantState = this;
mChangingConf = mAnimator.getChangingConfigurations();
}
@Override
public int getChangingConfigurations() {
return mChangingConf;
}
@Override
public Animator newInstance() {
final Animator clone = mAnimator.clone();
clone.mConstantState = this;
return clone;
}
}
/**
* Internally used by {@link #callOnList(ArrayList, AnimatorCaller, Object, boolean)} to
* make a call on all children of a list. This can be for start, stop, pause, cancel, update,
* etc notifications.
*
* @param <T> The type of listener to make the call on
* @param <A> The type of animator that is passed as a parameter
*/
interface AnimatorCaller<T, A> {
void call(T listener, A animator, boolean isReverse);
AnimatorCaller<AnimatorListener, Animator> ON_START = new AnimatorCaller<Animator.AnimatorListener,Animator>() {
@Override
public void call(AnimatorListener listener, Animator animator, boolean isReverse) {
listener.onAnimationStart(animator);
}
};
AnimatorCaller<AnimatorListener, Animator> ON_END = new AnimatorCaller<Animator.AnimatorListener,Animator>() {
@Override
public void call(AnimatorListener listener, Animator animator, boolean isReverse) {
listener.onAnimationEnd(animator);
}
};
AnimatorCaller<AnimatorListener, Animator> ON_CANCEL =
new AnimatorCaller<AnimatorListener, Animator>() { @Override public void call(AnimatorListener listener, Animator animator, boolean isReverse) { listener.onAnimationCancel(animator); }};
AnimatorCaller<AnimatorListener, Animator> ON_REPEAT =
new AnimatorCaller<AnimatorListener, Animator>() { @Override public void call(AnimatorListener listener, Animator animator, boolean isReverse) { listener.onAnimationRepeat(animator); }};
AnimatorCaller<AnimatorPauseListener, Animator> ON_PAUSE =
new AnimatorCaller<AnimatorPauseListener, Animator>() { @Override public void call(AnimatorPauseListener listener, Animator animator, boolean isReverse) { listener.onAnimationPause(animator); }};
AnimatorCaller<AnimatorPauseListener, Animator> ON_RESUME =
new AnimatorCaller<AnimatorPauseListener, Animator>() { @Override public void call(AnimatorPauseListener listener, Animator animator, boolean isReverse) { listener.onAnimationResume(animator); }};
AnimatorCaller<ValueAnimator.AnimatorUpdateListener, ValueAnimator> ON_UPDATE =
new AnimatorCaller<ValueAnimator.AnimatorUpdateListener, ValueAnimator>() {
@Override
public void call(ValueAnimator.AnimatorUpdateListener listener, ValueAnimator animator, boolean isReverse) {
listener.onAnimationUpdate(animator);
}
};
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,9 @@
package android.animation;
public class ArgbEvaluator {
public class ArgbEvaluator implements TypeEvaluator {
public static ArgbEvaluator getInstance() {
return null;
}
}

View file

@ -1,46 +1,648 @@
/*
* Copyright (C) 2010 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 android.animation;
import java.lang.reflect.Method;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Path;
import android.graphics.PointF;
import android.util.Log;
import android.util.Property;
import android.view.animation.AccelerateDecelerateInterpolator;
public class ObjectAnimator extends ValueAnimator {
/**
* This subclass of {@link ValueAnimator} provides support for animating properties on target objects.
* The constructors of this class take parameters to define the target object that will be animated
* as well as the name of the property that will be animated. Appropriate set/get functions
* are then determined internally and the animation will call these functions as necessary to
* animate the property.
*
* <p>Animators can be created from either code or resource files, as shown here:</p>
*
* {@sample development/samples/ApiDemos/res/anim/object_animator.xml ObjectAnimatorResources}
*
* <p>Starting from API 23, it is possible to use {@link PropertyValuesHolder} and
* {@link Keyframe} in resource files to create more complex animations. Using PropertyValuesHolders
* allows animators to animate several properties in parallel, as shown in this sample:</p>
*
* {@sample development/samples/ApiDemos/res/anim/object_animator_pvh.xml
* PropertyValuesHolderResources}
*
* <p>Using Keyframes allows animations to follow more complex paths from the start
* to the end values. Note that you can specify explicit fractional values (from 0 to 1) for
* each keyframe to determine when, in the overall duration, the animation should arrive at that
* value. Alternatively, you can leave the fractions off and the keyframes will be equally
* distributed within the total duration. Also, a keyframe with no value will derive its value
* from the target object when the animator starts, just like animators with only one
* value specified. In addition, an optional interpolator can be specified. The interpolator will
* be applied on the interval between the keyframe that the interpolator is set on and the previous
* keyframe. When no interpolator is supplied, the default {@link AccelerateDecelerateInterpolator}
* will be used. </p>
*
* {@sample development/samples/ApiDemos/res/anim/object_animator_pvh_kf_interpolated.xml KeyframeResources}
*
* <div class="special reference">
* <h3>Developer Guides</h3>
* <p>For more information about animating with {@code ObjectAnimator}, read the
* <a href="{@docRoot}guide/topics/graphics/prop-animation.html#object-animator">Property
* Animation</a> developer guide.</p>
* </div>
*
* @see #setPropertyName(String)
*
*/
public final class ObjectAnimator extends ValueAnimator {
private static final String LOG_TAG = "ObjectAnimator";
private static final boolean DBG = false;
private Object mTarget;
private String mPropertyName;
private Property mProperty;
private boolean mAutoCancel = false;
/**
* Sets the name of the property that will be animated. This name is used to derive
* a setter function that will be called to set animated values.
* For example, a property name of <code>foo</code> will result
* in a call to the function <code>setFoo()</code> on the target object. If either
* <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will
* also be derived and called.
*
* <p>For best performance of the mechanism that calls the setter function determined by the
* name of the property being animated, use <code>float</code> or <code>int</code> typed values,
* and make the setter function for those properties have a <code>void</code> return value. This
* will cause the code to take an optimized path for these constrained circumstances. Other
* property types and return types will work, but will have more overhead in processing
* the requests due to normal reflection mechanisms.</p>
*
* <p>Note that the setter function derived from this property name
* must take the same parameter type as the
* <code>valueFrom</code> and <code>valueTo</code> properties, otherwise the call to
* the setter function will fail.</p>
*
* <p>If this ObjectAnimator has been set up to animate several properties together,
* using more than one PropertyValuesHolder objects, then setting the propertyName simply
* sets the propertyName in the first of those PropertyValuesHolder objects.</p>
*
* @param propertyName The name of the property being animated. Should not be null.
*/
public void setPropertyName(@NonNull String propertyName) {
// mValues could be null if this is being constructed piecemeal. Just record the
// propertyName to be used later when setValues() is called if so.
if (mValues != null) {
PropertyValuesHolder valuesHolder = mValues[0];
String oldName = valuesHolder.getProperty_name();
valuesHolder.setProperty_name(propertyName);
mValuesMap.remove(oldName);
mValuesMap.put(propertyName, valuesHolder);
}
mPropertyName = propertyName;
// New property/values/target should cause re-initialization prior to starting
mInitialized = false;
}
/**
* Sets the property that will be animated. Property objects will take precedence over
* properties specified by the {@link #setPropertyName(String)} method. Animations should
* be set up to use one or the other, not both.
*
* @param property The property being animated. Should not be null.
*/
public void setProperty(@NonNull Property property) {
// mValues could be null if this is being constructed piecemeal. Just record the
// propertyName to be used later when setValues() is called if so.
if (mValues != null) {
PropertyValuesHolder valuesHolder = mValues[0];
String oldName = valuesHolder.getProperty_name();
valuesHolder.setProperty(property);
mValuesMap.remove(oldName);
mValuesMap.put(mPropertyName, valuesHolder);
}
if (mProperty != null) {
mPropertyName = property.getName();
}
mProperty = property;
// New property/values/target should cause re-initialization prior to starting
mInitialized = false;
}
/**
* Gets the name of the property that will be animated. This name will be used to derive
* a setter function that will be called to set animated values.
* For example, a property name of <code>foo</code> will result
* in a call to the function <code>setFoo()</code> on the target object. If either
* <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will
* also be derived and called.
*
* <p>If this animator was created with a {@link Property} object instead of the
* string name of a property, then this method will return the {@link
* Property#getName() name} of that Property object instead. If this animator was
* created with one or more {@link PropertyValuesHolder} objects, then this method
* will return the {@link PropertyValuesHolder#getProperty_name() name} of that
* object (if there was just one) or a comma-separated list of all of the
* names (if there are more than one).</p>
*/
@Nullable
public String getPropertyName() {
return null;
String propertyName = null;
if (mPropertyName != null) {
propertyName = mPropertyName;
} else if (mProperty != null) {
propertyName = mProperty.getName();
} else if (mValues != null && mValues.length > 0) {
for (int i = 0; i < mValues.length; ++i) {
if (i == 0) {
propertyName = "";
} else {
propertyName += ",";
}
propertyName += mValues[i].getProperty_name();
}
}
return propertyName;
}
public static ObjectAnimator ofFloat(Object target, String xPropertyName, String yPropertyName, Path path) {
return new ObjectAnimator();
@Override
String getNameForTrace() {
return "animator:" + getPropertyName();
}
public static <T> ObjectAnimator ofFloat(T target, Property<T, Float> xProperty, Property<T, Float> yProperty, Path path) {
return new ObjectAnimator();
/**
* Creates a new ObjectAnimator object. This default constructor is primarily for
* use internally; the other constructors which take parameters are more generally
* useful.
*/
public ObjectAnimator() {
}
public static <T> ObjectAnimator ofFloat(T target, Property<T, Float> property, float... values) {
return new ObjectAnimator();
/**
* Private utility constructor that initializes the target object and name of the
* property being animated.
*
* @param target The object whose property is to be animated. This object should
* have a public method on it called <code>setName()</code>, where <code>name</code> is
* the value of the <code>propertyName</code> parameter.
* @param propertyName The name of the property being animated.
*/
private ObjectAnimator(Object target, String propertyName) {
setTarget(target);
setPropertyName(propertyName);
}
/**
* Private utility constructor that initializes the target object and property being animated.
*
* @param target The object whose property is to be animated.
* @param property The property being animated.
*/
private <T> ObjectAnimator(T target, Property<T, ?> property) {
setTarget(target);
setProperty(property);
}
/**
* Constructs and returns an ObjectAnimator that animates between int values. A single
* value implies that that value is the one being animated to, in which case the start value
* will be derived from the property being animated and the target object when {@link #start()}
* is called for the first time. Two values imply starting and ending values. More than two
* values imply a starting value, values to animate through along the way, and an ending value
* (these values will be distributed evenly across the duration of the animation).
*
* @param target The object whose property is to be animated. This object should
* have a public method on it called <code>setName()</code>, where <code>name</code> is
* the value of the <code>propertyName</code> parameter.
* @param propertyName The name of the property being animated.
* @param values A set of values that the animation will animate between over time.
* @return An ObjectAnimator object that is set up to animate between the given values.
*/
public static ObjectAnimator ofInt(Object target, String propertyName, int... values) {
ObjectAnimator anim = new ObjectAnimator(target, propertyName);
anim.setIntValues(values);
return anim;
}
/**
* Constructs and returns an ObjectAnimator that animates between int values. A single
* value implies that that value is the one being animated to, in which case the start value
* will be derived from the property being animated and the target object when {@link #start()}
* is called for the first time. Two values imply starting and ending values. More than two
* values imply a starting value, values to animate through along the way, and an ending value
* (these values will be distributed evenly across the duration of the animation).
*
* @param target The object whose property is to be animated.
* @param property The property being animated.
* @param values A set of values that the animation will animate between over time.
* @return An ObjectAnimator object that is set up to animate between the given values.
*/
public static <T> ObjectAnimator ofInt(T target, Property<T, Integer> property, int... values) {
ObjectAnimator anim = new ObjectAnimator(target, property);
anim.setIntValues(values);
return anim;
}
/**
* Constructs and returns an ObjectAnimator that animates between color values. A single
* value implies that that value is the one being animated to, in which case the start value
* will be derived from the property being animated and the target object when {@link #start()}
* is called for the first time. Two values imply starting and ending values. More than two
* values imply a starting value, values to animate through along the way, and an ending value
* (these values will be distributed evenly across the duration of the animation).
*
* @param target The object whose property is to be animated. This object should
* have a public method on it called <code>setName()</code>, where <code>name</code> is
* the value of the <code>propertyName</code> parameter.
* @param propertyName The name of the property being animated.
* @param values A set of values that the animation will animate between over time.
* @return An ObjectAnimator object that is set up to animate between the given values.
*/
public static ObjectAnimator ofArgb(Object target, String propertyName, int... values) {
ObjectAnimator animator = ofInt(target, propertyName, values);
animator.setEvaluator(ArgbEvaluator.getInstance());
return animator;
}
/**
* Constructs and returns an ObjectAnimator that animates between color values. A single
* value implies that that value is the one being animated to, in which case the start value
* will be derived from the property being animated and the target object when {@link #start()}
* is called for the first time. Two values imply starting and ending values. More than two
* values imply a starting value, values to animate through along the way, and an ending value
* (these values will be distributed evenly across the duration of the animation).
*
* @param target The object whose property is to be animated.
* @param property The property being animated.
* @param values A set of values that the animation will animate between over time.
* @return An ObjectAnimator object that is set up to animate between the given values.
*/
public static <T> ObjectAnimator ofArgb(T target, Property<T, Integer> property,
int... values) {
ObjectAnimator animator = ofInt(target, property, values);
animator.setEvaluator(ArgbEvaluator.getInstance());
return animator;
}
/**
* Constructs and returns an ObjectAnimator that animates between float values. A single
* value implies that that value is the one being animated to, in which case the start value
* will be derived from the property being animated and the target object when {@link #start()}
* is called for the first time. Two values imply starting and ending values. More than two
* values imply a starting value, values to animate through along the way, and an ending value
* (these values will be distributed evenly across the duration of the animation).
*
* @param target The object whose property is to be animated. This object should
* have a public method on it called <code>setName()</code>, where <code>name</code> is
* the value of the <code>propertyName</code> parameter.
* @param propertyName The name of the property being animated.
* @param values A set of values that the animation will animate between over time.
* @return An ObjectAnimator object that is set up to animate between the given values.
*/
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
return new ObjectAnimator();
ObjectAnimator anim = new ObjectAnimator(target, propertyName);
anim.setFloatValues(values);
return anim;
}
public static <T> ObjectAnimator ofInt(T target, String propertyName, int... values) throws ReflectiveOperationException {
Method setter = target.getClass().getMethod("set" + Character.toUpperCase(propertyName.charAt(0)) + propertyName.substring(1), int.class);
setter.invoke(target, values[values.length - 1]);
return new ObjectAnimator();
/**
* Constructs and returns an ObjectAnimator that animates between float values. A single
* value implies that that value is the one being animated to, in which case the start value
* will be derived from the property being animated and the target object when {@link #start()}
* is called for the first time. Two values imply starting and ending values. More than two
* values imply a starting value, values to animate through along the way, and an ending value
* (these values will be distributed evenly across the duration of the animation).
*
* @param target The object whose property is to be animated.
* @param property The property being animated.
* @param values A set of values that the animation will animate between over time.
* @return An ObjectAnimator object that is set up to animate between the given values.
*/
public static <T> ObjectAnimator ofFloat(T target, Property<T, Float> property,
float... values) {
ObjectAnimator anim = new ObjectAnimator(target, property);
anim.setFloatValues(values);
return anim;
}
public ObjectAnimator setDuration(long duration) {return this;}
public void setAutoCancel(boolean autoCancel) {}
public void setPropertyName(String propertyName) {}
public static ObjectAnimator ofPropertyValuesHolder(Object target, PropertyValuesHolder... values) {
return new ObjectAnimator();
/**
* Constructs and returns an ObjectAnimator that animates between Object values. A single
* value implies that that value is the one being animated to, in which case the start value
* will be derived from the property being animated and the target object when {@link #start()}
* is called for the first time. Two values imply starting and ending values. More than two
* values imply a starting value, values to animate through along the way, and an ending value
* (these values will be distributed evenly across the duration of the animation).
*
* <p><strong>Note:</strong> The values are stored as references to the original
* objects, which means that changes to those objects after this method is called will
* affect the values on the animator. If the objects will be mutated externally after
* this method is called, callers should pass a copy of those objects instead.
*
* @param target The object whose property is to be animated. This object should
* have a public method on it called <code>setName()</code>, where <code>name</code> is
* the value of the <code>propertyName</code> parameter.
* @param propertyName The name of the property being animated.
* @param evaluator A TypeEvaluator that will be called on each animation frame to
* provide the necessary interpolation between the Object values to derive the animated
* value.
* @param values A set of values that the animation will animate between over time.
* @return An ObjectAnimator object that is set up to animate between the given values.
*/
public static ObjectAnimator ofObject(Object target, String propertyName,
TypeEvaluator evaluator, Object... values) {
ObjectAnimator anim = new ObjectAnimator(target, propertyName);
anim.setObjectValues(values);
anim.setEvaluator(evaluator);
return anim;
}
/**
* Constructs and returns an ObjectAnimator that animates between the sets of values specified
* in <code>PropertyValueHolder</code> objects. This variant should be used when animating
* several properties at once with the same ObjectAnimator, since PropertyValuesHolder allows
* you to associate a set of animation values with a property name.
*
* @param target The object whose property is to be animated. Depending on how the
* PropertyValuesObjects were constructed, the target object should either have the {@link
* android.util.Property} objects used to construct the PropertyValuesHolder objects or (if the
* PropertyValuesHOlder objects were created with property names) the target object should have
* public methods on it called <code>setName()</code>, where <code>name</code> is the name of
* the property passed in as the <code>propertyName</code> parameter for each of the
* PropertyValuesHolder objects.
* @param values A set of PropertyValuesHolder objects whose values will be animated between
* over time.
* @return An ObjectAnimator object that is set up to animate between the given values.
*/
@NonNull
public static ObjectAnimator ofPropertyValuesHolder(Object target,
PropertyValuesHolder... values) {
ObjectAnimator anim = new ObjectAnimator();
anim.setTarget(target);
anim.setValues(values);
return anim;
}
@Override
public void setIntValues(int... values) {
if (mValues == null || mValues.length == 0) {
// No values yet - this animator is being constructed piecemeal. Init the values with
// whatever the current propertyName is
if (mProperty != null) {
setValues(PropertyValuesHolder.ofInt(mProperty, values));
} else {
setValues(PropertyValuesHolder.ofInt(mPropertyName, values));
}
} else {
super.setIntValues(values);
}
}
@Override
public void setFloatValues(float... values) {
if (mValues == null || mValues.length == 0) {
// No values yet - this animator is being constructed piecemeal. Init the values with
// whatever the current propertyName is
if (mProperty != null) {
setValues(PropertyValuesHolder.ofFloat(mProperty, values));
} else {
setValues(PropertyValuesHolder.ofFloat(mPropertyName, values));
}
} else {
super.setFloatValues(values);
}
}
@Override
public void setObjectValues(Object... values) {
if (mValues == null || mValues.length == 0) {
// No values yet - this animator is being constructed piecemeal. Init the values with
// whatever the current propertyName is
if (mProperty != null) {
setValues(PropertyValuesHolder.ofObject(mProperty, (TypeEvaluator) null, values));
} else {
setValues(PropertyValuesHolder.ofObject(mPropertyName,
(TypeEvaluator) null, values));
}
} else {
super.setObjectValues(values);
}
}
/**
* autoCancel controls whether an ObjectAnimator will be canceled automatically
* when any other ObjectAnimator with the same target and properties is started.
* Setting this flag may make it easier to run different animators on the same target
* object without having to keep track of whether there are conflicting animators that
* need to be manually canceled. Canceling animators must have the same exact set of
* target properties, in the same order.
*
* @param cancel Whether future ObjectAnimators with the same target and properties
* as this ObjectAnimator will cause this ObjectAnimator to be canceled.
*/
public void setAutoCancel(boolean cancel) {
mAutoCancel = cancel;
}
private boolean hasSameTargetAndProperties(@Nullable Animator anim) {
if (anim instanceof ObjectAnimator) {
PropertyValuesHolder[] theirValues = ((ObjectAnimator) anim).getValues();
if (((ObjectAnimator) anim).getTarget() == getTarget() &&
mValues.length == theirValues.length) {
for (int i = 0; i < mValues.length; ++i) {
PropertyValuesHolder pvhMine = mValues[i];
PropertyValuesHolder pvhTheirs = theirValues[i];
if (pvhMine.getProperty_name() == null ||
!pvhMine.getProperty_name().equals(pvhTheirs.getProperty_name())) {
return false;
}
}
return true;
}
}
return false;
}
@Override
public void start() {
AnimationHandler.getInstance().autoCancelBasedOn(this);
if (DBG) {
Log.d(LOG_TAG, "Anim target, duration: " + getTarget() + ", " + getDuration());
for (int i = 0; i < mValues.length; ++i) {
PropertyValuesHolder pvh = mValues[i];
// Log.d(LOG_TAG, " Values[" + i + "]: " +
// pvh.getPropertyName() + ", " + pvh.mKeyframes.getValue(0) + ", " +
// pvh.mKeyframes.getValue(1));
}
}
super.start();
}
boolean shouldAutoCancel(AnimationHandler.AnimationFrameCallback anim) {
if (anim == null) {
return false;
}
if (anim instanceof ObjectAnimator) {
ObjectAnimator objAnim = (ObjectAnimator) anim;
if (objAnim.mAutoCancel && hasSameTargetAndProperties(objAnim)) {
return true;
}
}
return false;
}
/**
* This function is called immediately before processing the first animation
* frame of an animation. If there is a nonzero <code>startDelay</code>, the
* function is called after that delay ends.
* It takes care of the final initialization steps for the
* animation. This includes setting mEvaluator, if the user has not yet
* set it up, and the setter/getter methods, if the user did not supply
* them.
*
* <p>Overriders of this method should call the superclass method to cause
* internal mechanisms to be set up correctly.</p>
*/
@Override
void initAnimation() {
if (!mInitialized) {
// mValueType may change due to setter/getter setup; do this before calling super.init(),
// which uses mValueType to set up the default type evaluator.
final Object target = getTarget();
if (target != null) {
final int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
mValues[i].setupSetterAndGetter(target);
}
}
super.initAnimation();
}
}
/**
* Sets the length of the animation. The default duration is 300 milliseconds.
*
* @param duration The length of the animation, in milliseconds.
* @return ObjectAnimator The object called with setDuration(). This return
* value makes it easier to compose statements together that construct and then set the
* duration, as in
* <code>ObjectAnimator.ofInt(target, propertyName, 0, 10).setDuration(500).start()</code>.
*/
@Override
@NonNull
public ObjectAnimator setDuration(long duration) {
super.setDuration(duration);
return this;
}
/**
* The target object whose property will be animated by this animation
*
* @return The object being animated
*/
@Nullable
public Object getTarget() {
return mTarget;
}
@Override
public void setTarget(@Nullable Object target) {
final Object oldTarget = getTarget();
if (oldTarget != target) {
if (isStarted()) {
cancel();
}
mTarget = target;
// New target should cause re-initialization prior to starting
mInitialized = false;
}
}
@Override
public void setupStartValues() {
initAnimation();
final Object target = getTarget();
if (target != null) {
final int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
mValues[i].setupStartValue(target);
}
}
}
@Override
public void setupEndValues() {
initAnimation();
final Object target = getTarget();
if (target != null) {
final int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
mValues[i].setupEndValue(target);
}
}
}
/**
* This method is called with the elapsed fraction of the animation during every
* animation frame. This function turns the elapsed fraction into an interpolated fraction
* and then into an animated value (from the evaluator. The function is called mostly during
* animation updates, but it is also called when the <code>end()</code>
* function is called, to set the final value on the property.
*
* <p>Overrides of this method must call the superclass to perform the calculation
* of the animated value.</p>
*
* @param fraction The elapsed fraction of the animation.
*/
@Override
void animateValue(float fraction) {
final Object target = getTarget();
super.animateValue(fraction);
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
mValues[i].setAnimatedValue(target);
}
}
@Override
boolean isInitialized() {
return mInitialized;
}
@Override
public ObjectAnimator clone() {
final ObjectAnimator anim = (ObjectAnimator) super.clone();
return anim;
}
@Override
@NonNull
public String toString() {
String returnVal = "ObjectAnimator@" + Integer.toHexString(hashCode()) + ", target " +
getTarget();
if (mValues != null) {
for (int i = 0; i < mValues.length; ++i) {
returnVal += "\n " + mValues[i].toString();
}
}
return returnVal;
}
}

View file

@ -1,11 +1,141 @@
package android.animation;
public class PropertyValuesHolder{
import java.lang.reflect.Method;
import android.util.Log;
import android.util.Property;
public class PropertyValuesHolder {
private float values_float[];
private int values_int[];
private Object values_object[];
private Object value;
private String property_name;
private Method setter;
public static PropertyValuesHolder ofFloat(String propertyName, float... values) {
return null;
PropertyValuesHolder propertyValuesHolder = new PropertyValuesHolder();
propertyValuesHolder.values_float = values;
propertyValuesHolder.property_name = propertyName;
return propertyValuesHolder;
}
public static PropertyValuesHolder ofObject(String propertyName, TypeEvaluator evaluator, Object... values) {
PropertyValuesHolder propertyValuesHolder = new PropertyValuesHolder();
propertyValuesHolder.values_object = values;
propertyValuesHolder.property_name = propertyName;
return propertyValuesHolder;
}
public static PropertyValuesHolder ofInt(String propertyName, int... values) {
PropertyValuesHolder propertyValuesHolder = new PropertyValuesHolder();
propertyValuesHolder.values_int = values;
propertyValuesHolder.property_name = propertyName;
return propertyValuesHolder;
}
public static PropertyValuesHolder ofFloat(Property property, float... values) {
PropertyValuesHolder propertyValuesHolder = new PropertyValuesHolder();
propertyValuesHolder.values_float = values;
propertyValuesHolder.property_name = property.getName();
return propertyValuesHolder;
}
public static PropertyValuesHolder ofObject(Property property, TypeEvaluator evaluator, Object... values) {
PropertyValuesHolder propertyValuesHolder = new PropertyValuesHolder();
propertyValuesHolder.values_object = values;
propertyValuesHolder.property_name = property.getName();
return propertyValuesHolder;
}
public static PropertyValuesHolder ofInt(Property property, int... values) {
PropertyValuesHolder propertyValuesHolder = new PropertyValuesHolder();
propertyValuesHolder.values_int = values;
propertyValuesHolder.property_name = property.getName();
return propertyValuesHolder;
}
public void setIntValues(int... values) {
values_int = values;
}
public void setFloatValues(float... values) {
values_float = values;
}
public void setObjectValues(Object... values) {
values_object = values;
}
public String getProperty_name() {
return property_name;
}
public void setProperty_name(String propertyName) {
this.property_name = propertyName;
}
public void setProperty(Property property) {
property_name = property.getName();
}
public void init() {}
public Object getAnimatedValue() {
return value;
}
public void setEvaluator(TypeEvaluator value) {}
public void calculateValue(float fraction) {
if (values_object != null) {
value = values_object[(int) (fraction * (values_object.length - 1) + 0.5f)];
} else if (values_float != null) {
int i = (int) (fraction * (values_float.length - 1));
float f = fraction * (values_float.length - 1) - i;
value = values_float[i] * (1 - f) + ((f!=0.f) ? values_float[i + 1] * f : 0.f);
} else if (values_int != null) {
int i = (int) (fraction * (values_int.length - 1));
float f = fraction * (values_int.length - 1) - i;
value = (int)(values_int[i] * (1 - f) + ((f!=0.f) ? values_int[i + 1] * f : 0.f) + 0.5f);
} else {
Log.e("PropertyValuesHolder", "No values set");
}
}
public PropertyValuesHolder clone() {
return null;
}
public void setupSetterAndGetter(Object target) {
try {
Class<?> clazz;
if (values_float != null) {
clazz = float.class;
} else {
clazz = values_object[0].getClass();
}
setter = target.getClass().getMethod("set" + property_name.substring(0, 1).toUpperCase() + property_name.substring(1), clazz);
} catch (NoSuchMethodException e) {
Log.e("PropertyValuesHolder", "failed to find setter", e);
}
}
public void setupStartValue(Object target) {
}
public void setupEndValue(Object target) {
}
public void setAnimatedValue(Object target) {
if (setter != null && value != null) {
try {
setter.invoke(target, value);
} catch (ReflectiveOperationException e) {
Log.e("PropertyValuesHolder", "failed to invoke setter", e);
}
} else {
Log.e("PropertyValuesHolder", "no setter or value set");
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,229 @@
/*
* Copyright (C) 2013 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 android.util;
import java.util.Arrays;
import com.android.internal.util.ArrayUtils;
import android.annotation.Nullable;
import android.os.Build;
/**
* Implements a growing array of long primitives.
*
* @hide
*/
public class LongArray implements Cloneable {
private static final int MIN_CAPACITY_INCREMENT = 12;
private long[] mValues;
private int mSize;
private LongArray(long[] array, int size) {
mValues = array;
mSize = size; //Preconditions.checkArgumentInRange(size, 0, array.length, "size");
}
/**
* Creates an empty LongArray with the default initial capacity.
*/
public LongArray() {
this(0);
}
/**
* Creates an empty LongArray with the specified initial capacity.
*/
public LongArray(int initialCapacity) {
if (initialCapacity == 0) {
mValues = new long[0];
} else {
mValues = ArrayUtils.newUnpaddedLongArray(initialCapacity);
}
mSize = 0;
}
/**
* Creates an LongArray wrapping the given primitive long array.
*/
public static LongArray wrap(long[] array) {
return new LongArray(array, array.length);
}
/**
* Creates an LongArray from the given primitive long array, copying it.
*/
public static LongArray fromArray(long[] array, int size) {
return wrap(Arrays.copyOf(array, size));
}
/**
* Changes the size of this LongArray. If this LongArray is shrinked, the backing array capacity
* is unchanged. If the new size is larger than backing array capacity, a new backing array is
* created from the current content of this LongArray padded with 0s.
*/
public void resize(int newSize) {
// Preconditions.checkArgumentNonnegative(newSize);
if (newSize <= mValues.length) {
Arrays.fill(mValues, newSize, mValues.length, 0);
} else {
ensureCapacity(newSize - mSize);
}
mSize = newSize;
}
/**
* Appends the specified value to the end of this array.
*/
public void add(long value) {
add(mSize, value);
}
/**
* Inserts a value at the specified position in this array. If the specified index is equal to
* the length of the array, the value is added at the end.
*
* @throws IndexOutOfBoundsException when index &lt; 0 || index &gt; size()
*/
public void add(int index, long value) {
ensureCapacity(1);
int rightSegment = mSize - index;
mSize++;
// ArrayUtils.checkBounds(mSize, index);
if (rightSegment != 0) {
// Move by 1 all values from the right of 'index'
System.arraycopy(mValues, index, mValues, index + 1, rightSegment);
}
mValues[index] = value;
}
/**
* Adds the values in the specified array to this array.
*/
public void addAll(LongArray values) {
final int count = values.mSize;
ensureCapacity(count);
System.arraycopy(values.mValues, 0, mValues, mSize, count);
mSize += count;
}
/**
* Ensures capacity to append at least <code>count</code> values.
*/
private void ensureCapacity(int count) {
final int currentSize = mSize;
final int minCapacity = currentSize + count;
if (minCapacity >= mValues.length) {
final int targetCap = currentSize + (currentSize < (MIN_CAPACITY_INCREMENT / 2) ?
MIN_CAPACITY_INCREMENT : currentSize >> 1);
final int newCapacity = targetCap > minCapacity ? targetCap : minCapacity;
final long[] newValues = ArrayUtils.newUnpaddedLongArray(newCapacity);
System.arraycopy(mValues, 0, newValues, 0, currentSize);
mValues = newValues;
}
}
/**
* Removes all values from this array.
*/
public void clear() {
mSize = 0;
}
@Override
public LongArray clone() {
LongArray clone = null;
try {
clone = (LongArray) super.clone();
clone.mValues = mValues.clone();
} catch (CloneNotSupportedException cnse) {
/* ignore */
}
return clone;
}
/**
* Returns the value at the specified position in this array.
*/
public long get(int index) {
// ArrayUtils.checkBounds(mSize, index);
return mValues[index];
}
/**
* Sets the value at the specified position in this array.
*/
public void set(int index, long value) {
// ArrayUtils.checkBounds(mSize, index);
mValues[index] = value;
}
/**
* Returns the index of the first occurrence of the specified value in this
* array, or -1 if this array does not contain the value.
*/
public int indexOf(long value) {
final int n = mSize;
for (int i = 0; i < n; i++) {
if (mValues[i] == value) {
return i;
}
}
return -1;
}
/**
* Removes the value at the specified index from this array.
*/
public void remove(int index) {
// ArrayUtils.checkBounds(mSize, index);
System.arraycopy(mValues, index + 1, mValues, index, mSize - index - 1);
mSize--;
}
/**
* Returns the number of values in this array.
*/
public int size() {
return mSize;
}
/**
* Returns a new array with the contents of this LongArray.
*/
public long[] toArray() {
return Arrays.copyOf(mValues, mSize);
}
/**
* Test if each element of {@code a} equals corresponding element from {@code b}
*/
public static boolean elementsEqual(@Nullable LongArray a, @Nullable LongArray b) {
if (a == null || b == null) return a == b;
if (a.mSize != b.mSize) return false;
for (int i = 0; i < a.mSize; i++) {
if (a.get(i) != b.get(i)) {
return false;
}
}
return true;
}
}

View file

@ -1,10 +1,11 @@
package android.view;
import android.animation.Animator;
import android.animation.ValueAnimator;
public class ViewAnimationUtils {
public static Animator createCircularReveal(View view, int centerX, int centerY, float startRadius, float endRadius) {
return new Animator();
return new ValueAnimator();
}
}

View file

@ -2,6 +2,7 @@ package android.view;
import android.animation.Animator;
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.os.Handler;
public class ViewPropertyAnimator {
@ -71,7 +72,7 @@ public class ViewPropertyAnimator {
@Override
public void run() {
if (listener != null)
listener.onAnimationEnd(new Animator());
listener.onAnimationEnd(new ValueAnimator());
}
}, startDelay+duration);
}

View file

@ -3,6 +3,7 @@ srcs = [
'android/R.java',
'android/accounts/Account.java',
'android/accounts/AccountManager.java',
'android/animation/AnimationHandler.java',
'android/animation/Animator.java',
'android/animation/AnimatorInflater.java',
'android/animation/AnimatorListenerAdapter.java',
@ -436,6 +437,7 @@ srcs = [
'android/util/MathUtils.java',
'android/util/LayoutDirection.java',
'android/util/Log.java',
'android/util/LongArray.java',
'android/util/LongSparseArray.java',
'android/util/LruCache.java',
'android/util/MapCollections.java',