Merge "fix race condition when a new display is added" into klp-dev
diff --git a/api/current.txt b/api/current.txt
index 6026cc5..ff47557 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2338,17 +2338,23 @@
public abstract class Animator implements java.lang.Cloneable {
ctor public Animator();
method public void addListener(android.animation.Animator.AnimatorListener);
+ method public void addPauseListener(android.animation.Animator.AnimatorPauseListener);
method public void cancel();
method public android.animation.Animator clone();
method public void end();
method public abstract long getDuration();
method public android.animation.TimeInterpolator getInterpolator();
method public java.util.ArrayList<android.animation.Animator.AnimatorListener> getListeners();
+ method public java.util.ArrayList<android.animation.Animator.AnimatorPauseListener> getPauseListeners();
method public abstract long getStartDelay();
+ method public boolean isPaused();
method public abstract boolean isRunning();
method public boolean isStarted();
+ method public void pause();
method public void removeAllListeners();
method public void removeListener(android.animation.Animator.AnimatorListener);
+ method public void removePauseListener(android.animation.Animator.AnimatorPauseListener);
+ method public void resume();
method public abstract android.animation.Animator setDuration(long);
method public abstract void setInterpolator(android.animation.TimeInterpolator);
method public abstract void setStartDelay(long);
@@ -2365,16 +2371,23 @@
method public abstract void onAnimationStart(android.animation.Animator);
}
+ public static abstract interface Animator.AnimatorPauseListener {
+ method public abstract void onAnimationPause(android.animation.Animator);
+ method public abstract void onAnimationResume(android.animation.Animator);
+ }
+
public class AnimatorInflater {
ctor public AnimatorInflater();
method public static android.animation.Animator loadAnimator(android.content.Context, int) throws android.content.res.Resources.NotFoundException;
}
- public abstract class AnimatorListenerAdapter implements android.animation.Animator.AnimatorListener {
+ public abstract class AnimatorListenerAdapter implements android.animation.Animator.AnimatorListener android.animation.Animator.AnimatorPauseListener {
ctor public AnimatorListenerAdapter();
method public void onAnimationCancel(android.animation.Animator);
method public void onAnimationEnd(android.animation.Animator);
+ method public void onAnimationPause(android.animation.Animator);
method public void onAnimationRepeat(android.animation.Animator);
+ method public void onAnimationResume(android.animation.Animator);
method public void onAnimationStart(android.animation.Animator);
}
@@ -18741,7 +18754,7 @@
method public android.print.PrintDocumentInfo.Builder setPageCount(int);
}
- public final class PrintFileDocumentAdapter extends android.print.PrintDocumentAdapter {
+ public class PrintFileDocumentAdapter extends android.print.PrintDocumentAdapter {
ctor public PrintFileDocumentAdapter(android.content.Context, java.io.File, android.print.PrintDocumentInfo);
method public void onLayout(android.print.PrintAttributes, android.print.PrintAttributes, android.os.CancellationSignal, android.print.PrintDocumentAdapter.LayoutResultCallback, android.os.Bundle);
method public void onWrite(android.print.PageRange[], java.io.FileDescriptor, android.os.CancellationSignal, android.print.PrintDocumentAdapter.WriteResultCallback);
@@ -25819,7 +25832,9 @@
method public android.view.InputDevice.MotionRange getMotionRange(int, int);
method public java.util.List<android.view.InputDevice.MotionRange> getMotionRanges();
method public java.lang.String getName();
+ method public int getProductId();
method public int getSources();
+ method public int getVendorId();
method public android.os.Vibrator getVibrator();
method public boolean isVirtual();
method public void writeToParcel(android.os.Parcel, int);
diff --git a/core/java/android/animation/Animator.java b/core/java/android/animation/Animator.java
index 39eb8d6..89accbb 100644
--- a/core/java/android/animation/Animator.java
+++ b/core/java/android/animation/Animator.java
@@ -30,6 +30,17 @@
ArrayList<AnimatorListener> mListeners = null;
/**
+ * 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;
+
+ /**
* 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
@@ -69,6 +80,66 @@
}
/**
+ * 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() {
+ if (isStarted() && !mPaused) {
+ mPaused = true;
+ if (mPauseListeners != null) {
+ ArrayList<AnimatorPauseListener> tmpListeners =
+ (ArrayList<AnimatorPauseListener>) mPauseListeners.clone();
+ int numListeners = tmpListeners.size();
+ for (int i = 0; i < numListeners; ++i) {
+ tmpListeners.get(i).onAnimationPause(this);
+ }
+ }
+ }
+ }
+
+ /**
+ * 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;
+ if (mPauseListeners != null) {
+ ArrayList<AnimatorPauseListener> tmpListeners =
+ (ArrayList<AnimatorPauseListener>) mPauseListeners.clone();
+ int numListeners = tmpListeners.size();
+ for (int i = 0; i < numListeners; ++i) {
+ tmpListeners.get(i).onAnimationResume(this);
+ }
+ }
+ }
+ }
+
+ /**
+ * 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.
*
@@ -180,15 +251,58 @@
}
/**
+ * 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);
+ }
+
+ /**
+ * 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;
+ }
+ }
+
+ /**
+ * Gets the set of {@link AnimatorPauseListener} objects that are currently
+ * listening for pause/resume events on this animator.
+ *
+ * @return ArrayList<AnimatorListener> The set of pause listeners.
+ */
+ public ArrayList<AnimatorPauseListener> getPauseListeners() {
+ return mPauseListeners;
+ }
+
+ /**
* Removes all listeners from this object. This is equivalent to calling
- * <code>getListeners()</code> followed by calling <code>clear()</code> on the
- * returned list of listeners.
+ * {@link #getListeners()} and {@link #getPauseListeners()} followed by calling
+ * {@link ArrayList#clear()} on the returned lists of listeners.
*/
public void removeAllListeners() {
if (mListeners != null) {
mListeners.clear();
mListeners = null;
}
+ if (mPauseListeners != null) {
+ mPauseListeners.clear();
+ mPauseListeners = null;
+ }
}
@Override
@@ -203,6 +317,14 @@
anim.mListeners.add(oldListeners.get(i));
}
}
+ if (mPauseListeners != null) {
+ ArrayList<AnimatorPauseListener> oldListeners = mPauseListeners;
+ anim.mPauseListeners = new ArrayList<AnimatorPauseListener>();
+ int numListeners = oldListeners.size();
+ for (int i = 0; i < numListeners; ++i) {
+ anim.mPauseListeners.add(oldListeners.get(i));
+ }
+ }
return anim;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
@@ -280,4 +402,29 @@
*/
void onAnimationRepeat(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(Animator animation);
+
+ /**
+ * <p>Notifies that the animation was resumed, after being
+ * previously paused.</p>
+ *
+ * @param animation The animation being resumed.
+ * @see #resume()
+ */
+ void onAnimationResume(Animator animation);
+ }
}
diff --git a/core/java/android/animation/AnimatorListenerAdapter.java b/core/java/android/animation/AnimatorListenerAdapter.java
index e5d70a4..2ecb8c3 100644
--- a/core/java/android/animation/AnimatorListenerAdapter.java
+++ b/core/java/android/animation/AnimatorListenerAdapter.java
@@ -21,7 +21,8 @@
* Any custom listener that cares only about a subset of the methods of this listener can
* simply subclass this adapter class instead of implementing the interface directly.
*/
-public abstract class AnimatorListenerAdapter implements Animator.AnimatorListener {
+public abstract class AnimatorListenerAdapter implements Animator.AnimatorListener,
+ Animator.AnimatorPauseListener {
/**
* {@inheritDoc}
@@ -51,4 +52,17 @@
public void onAnimationStart(Animator animation) {
}
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onAnimationPause(Animator animation) {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onAnimationResume(Animator animation) {
+ }
}
diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java
index b48853b..018a2d6 100644
--- a/core/java/android/animation/AnimatorSet.java
+++ b/core/java/android/animation/AnimatorSet.java
@@ -455,6 +455,36 @@
}
}
+ @Override
+ public void pause() {
+ boolean previouslyPaused = mPaused;
+ super.pause();
+ if (!previouslyPaused && mPaused) {
+ if (mDelayAnim != null) {
+ mDelayAnim.pause();
+ } else {
+ for (Node node : mNodes) {
+ node.animation.pause();
+ }
+ }
+ }
+ }
+
+ @Override
+ public void resume() {
+ boolean previouslyPaused = mPaused;
+ super.resume();
+ if (previouslyPaused && !mPaused) {
+ if (mDelayAnim != null) {
+ mDelayAnim.resume();
+ } else {
+ for (Node node : mNodes) {
+ node.animation.resume();
+ }
+ }
+ }
+ }
+
/**
* {@inheritDoc}
*
@@ -467,6 +497,7 @@
public void start() {
mTerminated = false;
mStarted = true;
+ mPaused = false;
if (mDuration >= 0) {
// If the duration was set on this AnimatorSet, pass it along to all child animations
@@ -549,6 +580,7 @@
mPlayingSet.add(node.animation);
}
}
+ mDelayAnim = null;
}
});
mDelayAnim.start();
@@ -787,6 +819,7 @@
}
}
mAnimatorSet.mStarted = false;
+ mAnimatorSet.mPaused = false;
}
}
}
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index e370e4a..6394299 100644
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -80,6 +80,20 @@
*/
long mSeekTime = -1;
+ /**
+ * Set on the next frame after pause() is called, used to calculate a new startTime
+ * or delayStartTime which allows the animator to continue from the point at which
+ * it was paused. If negative, has not yet been set.
+ */
+ private long mPauseTime;
+
+ /**
+ * Set when an animator is resumed. This triggers logic in the next frame which
+ * actually resumes the animator.
+ */
+ private boolean mResumed = false;
+
+
// The static sAnimationHandler processes the internal timing loop on which all animations
// are based
/**
@@ -147,7 +161,7 @@
private boolean mStarted = false;
/**
- * Tracks whether we've notified listeners of the onAnimationSTart() event. This can be
+ * 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().
*/
@@ -914,6 +928,7 @@
mPlayingState = STOPPED;
mStarted = true;
mStartedDelay = false;
+ mPaused = false;
AnimationHandler animationHandler = getOrCreateAnimationHandler();
animationHandler.mPendingAnimations.add(this);
if (mStartDelay == 0) {
@@ -971,6 +986,24 @@
}
@Override
+ public void resume() {
+ if (mPaused) {
+ mResumed = true;
+ }
+ super.resume();
+ }
+
+ @Override
+ public void pause() {
+ boolean previouslyPaused = mPaused;
+ super.pause();
+ if (!previouslyPaused && mPaused) {
+ mPauseTime = -1;
+ mResumed = false;
+ }
+ }
+
+ @Override
public boolean isRunning() {
return (mPlayingState == RUNNING || mRunning);
}
@@ -1008,6 +1041,7 @@
handler.mPendingAnimations.remove(this);
handler.mDelayedAnims.remove(this);
mPlayingState = STOPPED;
+ mPaused = false;
if ((mStarted || mRunning) && mListeners != null) {
if (!mRunning) {
// If it's not yet running, then start listeners weren't called. Call them now.
@@ -1071,6 +1105,18 @@
mStartedDelay = true;
mDelayStartTime = currentTime;
} else {
+ if (mPaused) {
+ if (mPauseTime < 0) {
+ mPauseTime = currentTime;
+ }
+ return false;
+ } else if (mResumed) {
+ mResumed = false;
+ if (mPauseTime > 0) {
+ // Offset by the duration that the animation was paused
+ mDelayStartTime += (currentTime - mPauseTime);
+ }
+ }
long deltaTime = currentTime - mDelayStartTime;
if (deltaTime > mStartDelay) {
// startDelay ended - start the anim and record the
@@ -1093,7 +1139,7 @@
*
* @param currentTime The current time, as tracked by the static timing handler
* @return true if the animation's duration, including any repetitions due to
- * <code>repeatCount</code> has been exceeded and the animation should be ended.
+ * <code>repeatCount</code>, has been exceeded and the animation should be ended.
*/
boolean animationFrame(long currentTime) {
boolean done = false;
@@ -1148,6 +1194,18 @@
mSeekTime = -1;
}
}
+ if (mPaused) {
+ if (mPauseTime < 0) {
+ mPauseTime = frameTime;
+ }
+ return false;
+ } else if (mResumed) {
+ mResumed = false;
+ if (mPauseTime > 0) {
+ // Offset by the duration that the animation was paused
+ mStartTime += (frameTime - mPauseTime);
+ }
+ }
// The frame time might be before the start time during the first frame of
// an animation. The "current time" must always be on or after the start
// time to avoid animating frames at negative time intervals. In practice, this
diff --git a/core/java/android/print/PrintFileDocumentAdapter.java b/core/java/android/print/PrintFileDocumentAdapter.java
index 4503eda..dbc8b6f 100644
--- a/core/java/android/print/PrintFileDocumentAdapter.java
+++ b/core/java/android/print/PrintFileDocumentAdapter.java
@@ -41,7 +41,7 @@
* spooling the data, so you can deleted the file if it is a
* temporary one.
*/
-public final class PrintFileDocumentAdapter extends PrintDocumentAdapter {
+public class PrintFileDocumentAdapter extends PrintDocumentAdapter {
private static final String LOG_TAG = "PrintedFileDocumentAdapter";
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index debf4ee..f43e4ab3 100644
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -46,6 +46,8 @@
private final int mGeneration;
private final int mControllerNumber;
private final String mName;
+ private final int mVendorId;
+ private final int mProductId;
private final String mDescriptor;
private final boolean mIsExternal;
private final int mSources;
@@ -343,13 +345,15 @@
};
// Called by native code.
- private InputDevice(int id, int generation, int controllerNumber, String name,
- String descriptor, boolean isExternal, int sources, int keyboardType,
+ private InputDevice(int id, int generation, int controllerNumber, String name, int vendorId,
+ int productId, String descriptor, boolean isExternal, int sources, int keyboardType,
KeyCharacterMap keyCharacterMap, boolean hasVibrator, boolean hasButtonUnderPad) {
mId = id;
mGeneration = generation;
mControllerNumber = controllerNumber;
mName = name;
+ mVendorId = vendorId;
+ mProductId = productId;
mDescriptor = descriptor;
mIsExternal = isExternal;
mSources = sources;
@@ -364,6 +368,8 @@
mGeneration = in.readInt();
mControllerNumber = in.readInt();
mName = in.readString();
+ mVendorId = in.readInt();
+ mProductId = in.readInt();
mDescriptor = in.readString();
mIsExternal = in.readInt() != 0;
mSources = in.readInt();
@@ -443,6 +449,33 @@
}
/**
+ * Gets the vendor id for the given device, if available.
+ * <p>
+ * A vendor id uniquely identifies the company who manufactured the device. A value of 0 will
+ * be assigned where a vendor id is not available.
+ * </p>
+ *
+ * @return The vendor id of a given device
+ */
+ public int getVendorId() {
+ return mVendorId;
+ }
+
+ /**
+ * Gets the product id for the given device, if available.
+ * <p>
+ * A product id uniquely identifies which product within the address space of a given vendor,
+ * identified by the device's vendor id. A value of 0 will be assigned where a product id is
+ * not available.
+ * </p>
+ *
+ * @return The product id of a given device
+ */
+ public int getProductId() {
+ return mProductId;
+ }
+
+ /**
* Gets the input device descriptor, which is a stable identifier for an input device.
* <p>
* An input device descriptor uniquely identifies an input device. Its value
@@ -757,6 +790,8 @@
out.writeInt(mGeneration);
out.writeInt(mControllerNumber);
out.writeString(mName);
+ out.writeInt(mVendorId);
+ out.writeInt(mProductId);
out.writeString(mDescriptor);
out.writeInt(mIsExternal ? 1 : 0);
out.writeInt(mSources);
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 0ed846b..07198c75 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -3485,7 +3485,8 @@
mLastY = Integer.MIN_VALUE;
}
- if (performButtonActionOnTouchDown(ev) && (mTouchMode == TOUCH_MODE_DOWN)) {
+ if (mTouchMode == TOUCH_MODE_DOWN && mMotionPosition != INVALID_POSITION
+ && performButtonActionOnTouchDown(ev)) {
removeCallbacks(mPendingCheckForTap);
}
}
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index 70a4daa..6d46cf9 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -610,6 +610,7 @@
const jchar* glyphs = value->getGlyphs();
size_t glyphsCount = value->getGlyphsCount();
jfloat totalAdvance = value->getTotalAdvance();
+ x += xOffsetForTextAlign(paint, totalAdvance);
const float* positions = value->getPos();
int bytesCount = glyphsCount * sizeof(jchar);
const SkRect& r = value->getBounds();
@@ -617,8 +618,7 @@
bounds.translate(x, y);
renderer->drawText((const char*) glyphs, bytesCount, glyphsCount,
- x + xOffsetForTextAlign(paint, totalAdvance), y, positions,
- paint, totalAdvance, bounds);
+ x, y, positions, paint, totalAdvance, bounds);
}
static void renderTextOnPath(OpenGLRenderer* renderer, const jchar* text, int count,
@@ -646,6 +646,7 @@
const jchar* glyphs = value->getGlyphs();
size_t glyphsCount = value->getGlyphsCount();
jfloat totalAdvance = value->getTotalAdvance();
+ x += xOffsetForTextAlign(paint, totalAdvance);
const float* positions = value->getPos();
int bytesCount = glyphsCount * sizeof(jchar);
const SkRect& r = value->getBounds();
@@ -653,8 +654,7 @@
bounds.translate(x, y);
renderer->drawText((const char*) glyphs, bytesCount, glyphsCount,
- x + xOffsetForTextAlign(paint, totalAdvance), y, positions,
- paint, totalAdvance, bounds);
+ x, y, positions, paint, totalAdvance, bounds);
}
static void android_view_GLES20Canvas_drawTextArray(JNIEnv* env, jobject clazz,
diff --git a/core/jni/android_view_InputDevice.cpp b/core/jni/android_view_InputDevice.cpp
index 5c8e010..bef0f84 100644
--- a/core/jni/android_view_InputDevice.cpp
+++ b/core/jni/android_view_InputDevice.cpp
@@ -53,11 +53,15 @@
return NULL;
}
+ const InputDeviceIdentifier& ident = deviceInfo.getIdentifier();
+
ScopedLocalRef<jobject> inputDeviceObj(env, env->NewObject(gInputDeviceClassInfo.clazz,
- gInputDeviceClassInfo.ctor, deviceInfo.getId(), deviceInfo.getGeneration(),
- deviceInfo.getControllerNumber(), nameObj.get(), descriptorObj.get(),
- deviceInfo.isExternal(), deviceInfo.getSources(), deviceInfo.getKeyboardType(),
- kcmObj.get(), deviceInfo.hasVibrator(), deviceInfo.hasButtonUnderPad()));
+ gInputDeviceClassInfo.ctor, deviceInfo.getId(), deviceInfo.getGeneration(),
+ deviceInfo.getControllerNumber(), nameObj.get(),
+ static_cast<int32_t>(ident.vendor), static_cast<int32_t>(ident.product),
+ descriptorObj.get(), deviceInfo.isExternal(), deviceInfo.getSources(),
+ deviceInfo.getKeyboardType(), kcmObj.get(), deviceInfo.hasVibrator(),
+ deviceInfo.hasButtonUnderPad()));
const Vector<InputDeviceInfo::MotionRange>& ranges = deviceInfo.getMotionRanges();
for (size_t i = 0; i < ranges.size(); i++) {
@@ -88,7 +92,7 @@
GET_METHOD_ID(gInputDeviceClassInfo.ctor, gInputDeviceClassInfo.clazz,
"<init>",
- "(IIILjava/lang/String;Ljava/lang/String;ZIILandroid/view/KeyCharacterMap;ZZ)V");
+ "(IIILjava/lang/String;IILjava/lang/String;ZIILandroid/view/KeyCharacterMap;ZZ)V");
GET_METHOD_ID(gInputDeviceClassInfo.addMotionRange, gInputDeviceClassInfo.clazz,
"addMotionRange", "(IIFFFFF)V");
diff --git a/core/tests/coretests/src/android/animation/AnimatorSetEventsTest.java b/core/tests/coretests/src/android/animation/AnimatorSetEventsTest.java
index d415e4e..7eb32ee 100644
--- a/core/tests/coretests/src/android/animation/AnimatorSetEventsTest.java
+++ b/core/tests/coretests/src/android/animation/AnimatorSetEventsTest.java
@@ -37,14 +37,12 @@
button = (Button) getActivity().findViewById(R.id.animatingButton);
mAnimator = new AnimatorSet();
((AnimatorSet)mAnimator).playSequentially(xAnim, yAnim);
-
super.setUp();
}
@Override
protected long getTimeout() {
- return (xAnim.getDuration() + yAnim.getDuration()) +
- (xAnim.getStartDelay() + yAnim.getStartDelay()) +
+ return (2 * mAnimator.getDuration()) + (2 * mAnimator.getStartDelay()) +
ANIM_DELAY + FUTURE_RELEASE_DELAY;
}
diff --git a/core/tests/coretests/src/android/animation/EventsTest.java b/core/tests/coretests/src/android/animation/EventsTest.java
index 8df711b..28cfe3d 100644
--- a/core/tests/coretests/src/android/animation/EventsTest.java
+++ b/core/tests/coretests/src/android/animation/EventsTest.java
@@ -22,6 +22,7 @@
import android.test.suitebuilder.annotation.SmallTest;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
/**
* Tests for the various lifecycle events of Animators. This abstract class is subclassed by
@@ -42,12 +43,15 @@
protected static final int ANIM_DELAY = 100;
protected static final int ANIM_MID_DURATION = ANIM_DURATION / 2;
protected static final int ANIM_MID_DELAY = ANIM_DELAY / 2;
+ protected static final int ANIM_PAUSE_DURATION = ANIM_DELAY;
+ protected static final int ANIM_PAUSE_DELAY = ANIM_DELAY / 2;
protected static final int FUTURE_RELEASE_DELAY = 50;
+ protected static final int ANIM_FULL_DURATION_SLOP = 100;
private boolean mStarted; // tracks whether we've received the onAnimationStart() callback
protected boolean mRunning; // tracks whether we've started the animator
- private boolean mCanceled; // trackes whether we've canceled the animator
- protected Animator.AnimatorListener mFutureListener; // mechanism for delaying the end of the test
+ private boolean mCanceled; // tracks whether we've canceled the animator
+ protected Animator.AnimatorListener mFutureListener; // mechanism for delaying end of the test
protected FutureWaiter mFuture; // Mechanism for waiting for the UI test to complete
private Animator.AnimatorListener mListener; // Listener that handles/tests the events
@@ -104,6 +108,48 @@
};
/**
+ * Pauses the given animator. Used to delay pausing until some later time (after the
+ * animator has started playing).
+ */
+ static class Pauser implements Runnable {
+ Animator mAnim;
+ FutureWaiter mFuture;
+ public Pauser(Animator anim, FutureWaiter future) {
+ mAnim = anim;
+ mFuture = future;
+ }
+ @Override
+ public void run() {
+ try {
+ mAnim.pause();
+ } catch (junit.framework.AssertionFailedError e) {
+ mFuture.setException(new RuntimeException(e));
+ }
+ }
+ };
+
+ /**
+ * Resumes the given animator. Used to delay resuming until some later time (after the
+ * animator has paused for some duration).
+ */
+ static class Resumer implements Runnable {
+ Animator mAnim;
+ FutureWaiter mFuture;
+ public Resumer(Animator anim, FutureWaiter future) {
+ mAnim = anim;
+ mFuture = future;
+ }
+ @Override
+ public void run() {
+ try {
+ mAnim.resume();
+ } catch (junit.framework.AssertionFailedError e) {
+ mFuture.setException(new RuntimeException(e));
+ }
+ }
+ };
+
+ /**
* Releases the given Future object when the listener's end() event is called. Specifically,
* it releases it after some further delay, to give the test time to do other things right
* after an animation ends.
@@ -555,4 +601,114 @@
mFuture.get(getTimeout(), TimeUnit.MILLISECONDS);
}
+ /**
+ * Verify that pausing and resuming an animator ends within
+ * the appropriate timeout duration.
+ */
+ @MediumTest
+ public void testPauseResume() throws Exception {
+ mFutureListener = new FutureReleaseListener(mFuture);
+ getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ Handler handler = new Handler();
+ mAnimator.addListener(mFutureListener);
+ mRunning = true;
+ mAnimator.start();
+ handler.postDelayed(new Pauser(mAnimator, mFuture), ANIM_PAUSE_DELAY);
+ handler.postDelayed(new Resumer(mAnimator, mFuture),
+ ANIM_PAUSE_DELAY + ANIM_PAUSE_DURATION);
+ } catch (junit.framework.AssertionFailedError e) {
+ mFuture.setException(new RuntimeException(e));
+ }
+ }
+ });
+ mFuture.get(getTimeout() + ANIM_PAUSE_DURATION, TimeUnit.MILLISECONDS);
+ }
+
+ /**
+ * Verify that pausing and resuming a startDelayed animator ends within
+ * the appropriate timeout duration.
+ */
+ @MediumTest
+ public void testPauseResumeDelayed() throws Exception {
+ mAnimator.setStartDelay(ANIM_DELAY);
+ mFutureListener = new FutureReleaseListener(mFuture);
+ getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ Handler handler = new Handler();
+ mAnimator.addListener(mFutureListener);
+ mRunning = true;
+ mAnimator.start();
+ handler.postDelayed(new Pauser(mAnimator, mFuture), ANIM_PAUSE_DELAY);
+ handler.postDelayed(new Resumer(mAnimator, mFuture),
+ ANIM_PAUSE_DELAY + ANIM_PAUSE_DURATION);
+ } catch (junit.framework.AssertionFailedError e) {
+ mFuture.setException(new RuntimeException(e));
+ }
+ }
+ });
+ mFuture.get(getTimeout() + ANIM_PAUSE_DURATION + ANIM_FULL_DURATION_SLOP,
+ TimeUnit.MILLISECONDS);
+ }
+
+ /**
+ * Verify that pausing an animator without resuming it causes a timeout.
+ */
+ @MediumTest
+ public void testPauseTimeout() throws Exception {
+ mFutureListener = new FutureReleaseListener(mFuture);
+ getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ Handler handler = new Handler();
+ mAnimator.addListener(mFutureListener);
+ mRunning = true;
+ mAnimator.start();
+ handler.postDelayed(new Pauser(mAnimator, mFuture), ANIM_PAUSE_DELAY);
+ } catch (junit.framework.AssertionFailedError e) {
+ mFuture.setException(new RuntimeException(e));
+ }
+ }
+ });
+ try {
+ mFuture.get(getTimeout() + ANIM_PAUSE_DURATION + ANIM_FULL_DURATION_SLOP,
+ TimeUnit.MILLISECONDS);
+ } catch (TimeoutException e) {
+ // Expected behavior, swallow the exception
+ }
+ }
+
+ /**
+ * Verify that pausing a startDelayed animator without resuming it causes a timeout.
+ */
+ @MediumTest
+ public void testPauseTimeoutDelayed() throws Exception {
+ mAnimator.setStartDelay(ANIM_DELAY);
+ mFutureListener = new FutureReleaseListener(mFuture);
+ getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ Handler handler = new Handler();
+ mAnimator.addListener(mFutureListener);
+ mRunning = true;
+ mAnimator.start();
+ handler.postDelayed(new Pauser(mAnimator, mFuture), ANIM_PAUSE_DELAY);
+ } catch (junit.framework.AssertionFailedError e) {
+ mFuture.setException(new RuntimeException(e));
+ }
+ }
+ });
+ try {
+ mFuture.get(getTimeout() + ANIM_PAUSE_DURATION + ANIM_FULL_DURATION_SLOP,
+ TimeUnit.MILLISECONDS);
+ } catch (TimeoutException e) {
+ // Expected behavior, swallow the exception
+ }
+ }
}
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 1700473..cb6bb2e 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -234,6 +234,7 @@
Vector<CacheTexture*>* cacheTextures = NULL;
switch (format) {
case SkMask::kA8_Format:
+ case SkMask::kBW_Format:
cacheTextures = &mACacheTextures;
break;
case SkMask::kARGB32_Format:
diff --git a/libs/hwui/font/CacheTexture.cpp b/libs/hwui/font/CacheTexture.cpp
index 55503ce..cbed3e4 100644
--- a/libs/hwui/font/CacheTexture.cpp
+++ b/libs/hwui/font/CacheTexture.cpp
@@ -233,9 +233,11 @@
bool CacheTexture::fitBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY) {
switch (glyph.fMaskFormat) {
case SkMask::kA8_Format:
+ case SkMask::kBW_Format:
if (mFormat != GL_ALPHA) {
#if DEBUG_FONT_RENDERER
- ALOGD("fitBitmap: kA8_Format glyph cannot fit into texture format %x", mFormat);
+ ALOGD("fitBitmap: texture format %x is inappropriate for monochromatic glyphs",
+ mFormat);
#endif
return false;
}
@@ -243,7 +245,7 @@
case SkMask::kARGB32_Format:
if (mFormat != GL_RGBA) {
#if DEBUG_FONT_RENDERER
- ALOGD("fitBitmap: kARGB32_Format glyph cannot fit into texture format %x", mFormat);
+ ALOGD("fitBitmap: texture format %x is inappropriate for colour glyphs", mFormat);
#endif
return false;
}
diff --git a/services/java/com/android/server/am/ActivityStackSupervisor.java b/services/java/com/android/server/am/ActivityStackSupervisor.java
index 8634929..0808861 100644
--- a/services/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1684,9 +1684,6 @@
setLaunchHomeTaskNextFlag(sourceRecord, null, targetStack);
targetStack.resumeTopActivityLocked(null);
}
- if (r.task == null) Slog.v(TAG,
- "startActivityUncheckedLocked: task left null",
- new RuntimeException("here").fillInStackTrace());
return ActivityManager.START_DELIVERED_TO_TOP;
}
}