summaryrefslogtreecommitdiffstats
path: root/core/java
diff options
context:
space:
mode:
Diffstat (limited to 'core/java')
-rw-r--r--core/java/android/app/Notification.java11
-rw-r--r--core/java/android/content/pm/ContainerEncryptionParams.java28
-rw-r--r--core/java/android/content/pm/LimitedLengthInputStream.java26
-rwxr-xr-xcore/java/android/hardware/input/InputManager.java4
-rw-r--r--core/java/android/os/Build.java4
-rw-r--r--core/java/android/text/style/TextAppearanceSpan.java41
-rw-r--r--core/java/android/util/TimeUtils.java19
-rw-r--r--core/java/android/view/GLES20DisplayList.java3
-rw-r--r--core/java/android/view/TextureView.java12
-rw-r--r--core/java/android/view/VelocityTracker.java18
-rw-r--r--core/java/android/view/View.java140
-rw-r--r--core/java/android/view/ViewGroup.java7
-rw-r--r--core/java/android/view/ViewRootImpl.java12
-rw-r--r--core/java/android/webkit/HTML5VideoFullScreen.java18
-rw-r--r--core/java/android/webkit/HTML5VideoInline.java14
-rw-r--r--core/java/android/webkit/HTML5VideoView.java86
-rw-r--r--core/java/android/webkit/HTML5VideoViewProxy.java23
-rw-r--r--core/java/android/webkit/WebViewInputDispatcher.java2
-rw-r--r--core/java/android/widget/SpellChecker.java44
-rw-r--r--core/java/android/widget/TextView.java34
-rw-r--r--core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java384
-rw-r--r--core/java/com/android/internal/widget/multiwaveview/TargetDrawable.java33
22 files changed, 666 insertions, 297 deletions
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index ed9babf..0c47069 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1503,7 +1503,16 @@ public class Notification implements Parcelable
RemoteViews button = new RemoteViews(mContext.getPackageName(), R.layout.notification_action);
button.setTextViewCompoundDrawables(R.id.action0, action.icon, 0, 0, 0);
button.setTextViewText(R.id.action0, action.title);
- button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
+ if (action.actionIntent != null) {
+ button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
+ //button.setBoolean(R.id.action0, "setEnabled", true);
+ button.setFloat(R.id.button0, "setAlpha", 1.0f);
+ button.setBoolean(R.id.button0, "setClickable", true);
+ } else {
+ //button.setBoolean(R.id.action0, "setEnabled", false);
+ button.setFloat(R.id.button0, "setAlpha", 0.5f);
+ button.setBoolean(R.id.button0, "setClickable", false);
+ }
button.setContentDescription(R.id.action0, action.title);
return button;
}
diff --git a/core/java/android/content/pm/ContainerEncryptionParams.java b/core/java/android/content/pm/ContainerEncryptionParams.java
index 5b1440d..88112a7 100644
--- a/core/java/android/content/pm/ContainerEncryptionParams.java
+++ b/core/java/android/content/pm/ContainerEncryptionParams.java
@@ -70,16 +70,16 @@ public class ContainerEncryptionParams implements Parcelable {
private final byte[] mMacTag;
/** Offset into file where authenticated (e.g., MAC protected) data begins. */
- private final int mAuthenticatedDataStart;
+ private final long mAuthenticatedDataStart;
/** Offset into file where encrypted data begins. */
- private final int mEncryptedDataStart;
+ private final long mEncryptedDataStart;
/**
* Offset into file for the end of encrypted data (and, by extension,
* authenticated data) in file.
*/
- private final int mDataEnd;
+ private final long mDataEnd;
public ContainerEncryptionParams(String encryptionAlgorithm,
AlgorithmParameterSpec encryptionSpec, SecretKey encryptionKey)
@@ -99,6 +99,8 @@ public class ContainerEncryptionParams implements Parcelable {
* @param macAlgorithm MAC algorithm to use; format matches JCE
* @param macSpec algorithm parameters specification, may be {@code null}
* @param macKey key used for authentication (i.e., for the MAC tag)
+ * @param macTag message authentication code (MAC) tag for the authenticated
+ * data
* @param authenticatedDataStart offset of start of authenticated data in
* stream
* @param encryptedDataStart offset of start of encrypted data in stream
@@ -109,7 +111,7 @@ public class ContainerEncryptionParams implements Parcelable {
public ContainerEncryptionParams(String encryptionAlgorithm,
AlgorithmParameterSpec encryptionSpec, SecretKey encryptionKey, String macAlgorithm,
AlgorithmParameterSpec macSpec, SecretKey macKey, byte[] macTag,
- int authenticatedDataStart, int encryptedDataStart, int dataEnd)
+ long authenticatedDataStart, long encryptedDataStart, long dataEnd)
throws InvalidAlgorithmParameterException {
if (TextUtils.isEmpty(encryptionAlgorithm)) {
throw new NullPointerException("algorithm == null");
@@ -172,15 +174,15 @@ public class ContainerEncryptionParams implements Parcelable {
return mMacTag;
}
- public int getAuthenticatedDataStart() {
+ public long getAuthenticatedDataStart() {
return mAuthenticatedDataStart;
}
- public int getEncryptedDataStart() {
+ public long getEncryptedDataStart() {
return mEncryptedDataStart;
}
- public int getDataEnd() {
+ public long getDataEnd() {
return mDataEnd;
}
@@ -315,9 +317,9 @@ public class ContainerEncryptionParams implements Parcelable {
dest.writeByteArray(mMacTag);
- dest.writeInt(mAuthenticatedDataStart);
- dest.writeInt(mEncryptedDataStart);
- dest.writeInt(mDataEnd);
+ dest.writeLong(mAuthenticatedDataStart);
+ dest.writeLong(mEncryptedDataStart);
+ dest.writeLong(mDataEnd);
}
private ContainerEncryptionParams(Parcel source) throws InvalidAlgorithmParameterException {
@@ -333,9 +335,9 @@ public class ContainerEncryptionParams implements Parcelable {
mMacTag = source.createByteArray();
- mAuthenticatedDataStart = source.readInt();
- mEncryptedDataStart = source.readInt();
- mDataEnd = source.readInt();
+ mAuthenticatedDataStart = source.readLong();
+ mEncryptedDataStart = source.readLong();
+ mDataEnd = source.readLong();
switch (encParamType) {
case ENC_PARAMS_IV_PARAMETERS:
diff --git a/core/java/android/content/pm/LimitedLengthInputStream.java b/core/java/android/content/pm/LimitedLengthInputStream.java
index 25a490f..e787277 100644
--- a/core/java/android/content/pm/LimitedLengthInputStream.java
+++ b/core/java/android/content/pm/LimitedLengthInputStream.java
@@ -3,6 +3,7 @@ package android.content.pm;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.util.Arrays;
/**
* A class that limits the amount of data that is read from an InputStream. When
@@ -15,20 +16,20 @@ public class LimitedLengthInputStream extends FilterInputStream {
/**
* The end of the stream where we don't want to allow more data to be read.
*/
- private final int mEnd;
+ private final long mEnd;
/**
* Current offset in the stream.
*/
- private int mOffset;
+ private long mOffset;
/**
* @param in underlying stream to wrap
* @param offset offset into stream where data starts
* @param length length of data at offset
- * @throws IOException if an error occured with the underlying stream
+ * @throws IOException if an error occurred with the underlying stream
*/
- public LimitedLengthInputStream(InputStream in, int offset, int length) throws IOException {
+ public LimitedLengthInputStream(InputStream in, long offset, long length) throws IOException {
super(in);
if (in == null) {
@@ -36,11 +37,15 @@ public class LimitedLengthInputStream extends FilterInputStream {
}
if (offset < 0) {
- throw new IOException("offset == " + offset);
+ throw new IOException("offset < 0");
}
if (length < 0) {
- throw new IOException("length must be non-negative; is " + length);
+ throw new IOException("length < 0");
+ }
+
+ if (length > Long.MAX_VALUE - offset) {
+ throw new IOException("offset + length > Long.MAX_VALUE");
}
mEnd = offset + length;
@@ -65,8 +70,15 @@ public class LimitedLengthInputStream extends FilterInputStream {
return -1;
}
+ final int arrayLength = buffer.length;
+ Arrays.checkOffsetAndCount(arrayLength, offset, byteCount);
+
+ if (mOffset > Long.MAX_VALUE - byteCount) {
+ throw new IOException("offset out of bounds: " + mOffset + " + " + byteCount);
+ }
+
if (mOffset + byteCount > mEnd) {
- byteCount = mEnd - mOffset;
+ byteCount = (int) (mEnd - mOffset);
}
final int numRead = super.read(buffer, offset, byteCount);
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 5ba1850..6448b55 100755
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -96,14 +96,14 @@ public final class InputManager {
* &lt;keyboard-layouts xmlns:android="http://schemas.android.com/apk/res/android">
* &lt;keyboard-layout android:name="keyboard_layout_english_us"
* android:label="@string/keyboard_layout_english_us_label"
- * android:kcm="@raw/keyboard_layout_english_us" />
+ * android:keyboardLayout="@raw/keyboard_layout_english_us" />
* &lt;/keyboard-layouts>
* </p><p>
* The <code>android:name</code> attribute specifies an identifier by which
* the keyboard layout will be known in the package.
* The <code>android:label</code> attributes specifies a human-readable descriptive
* label to describe the keyboard layout in the user interface, such as "English (US)".
- * The <code>android:kcm</code> attribute refers to a
+ * The <code>android:keyboardLayout</code> attribute refers to a
* <a href="http://source.android.com/tech/input/key-character-map-files.html">
* key character map</a> resource that defines the keyboard layout.
* </p>
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 63275cf..2d5b625 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -370,7 +370,7 @@ public class Build {
public static final int ICE_CREAM_SANDWICH_MR1 = 15;
/**
- * Next up on Android!
+ * Android 4.1.
*
* <p>Applications targeting this or a later release will get these
* new changes in behavior:</p>
@@ -381,7 +381,7 @@ public class Build {
* exist in the application's manifest.
* </ul>
*/
- public static final int JELLY_BEAN = CUR_DEVELOPMENT;
+ public static final int JELLY_BEAN = 16;
}
/** The type of build, like "user" or "eng". */
diff --git a/core/java/android/text/style/TextAppearanceSpan.java b/core/java/android/text/style/TextAppearanceSpan.java
index 5fd7c57..ecbf4bc 100644
--- a/core/java/android/text/style/TextAppearanceSpan.java
+++ b/core/java/android/text/style/TextAppearanceSpan.java
@@ -68,24 +68,29 @@ public class TextAppearanceSpan extends MetricAffectingSpan implements Parcelabl
TextAppearance_textSize, -1);
mStyle = a.getInt(com.android.internal.R.styleable.TextAppearance_textStyle, 0);
- int tf = a.getInt(com.android.internal.R.styleable.TextAppearance_typeface, 0);
-
- switch (tf) {
- case 1:
- mTypeface = "sans";
- break;
-
- case 2:
- mTypeface = "serif";
- break;
-
- case 3:
- mTypeface = "monospace";
- break;
-
- default:
- mTypeface = null;
- break;
+ String family = a.getString(com.android.internal.R.styleable.TextAppearance_fontFamily);
+ if (family != null) {
+ mTypeface = family;
+ } else {
+ int tf = a.getInt(com.android.internal.R.styleable.TextAppearance_typeface, 0);
+
+ switch (tf) {
+ case 1:
+ mTypeface = "sans";
+ break;
+
+ case 2:
+ mTypeface = "serif";
+ break;
+
+ case 3:
+ mTypeface = "monospace";
+ break;
+
+ default:
+ mTypeface = null;
+ break;
+ }
}
a.recycle();
diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java
index 2883eca..c4ebec4 100644
--- a/core/java/android/util/TimeUtils.java
+++ b/core/java/android/util/TimeUtils.java
@@ -26,6 +26,7 @@ import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Calendar;
import java.util.Collection;
import java.util.TimeZone;
import java.util.Date;
@@ -381,4 +382,22 @@ public class TimeUtils {
}
formatDuration(time-now, pw, 0);
}
+
+ /**
+ * Convert a System.currentTimeMillis() value to a time of day value like
+ * that printed in logs. MM-DD HH:MM:SS.MMM
+ *
+ * @param millis since the epoch (1/1/1970)
+ * @return String representation of the time.
+ * @hide
+ */
+ public static String logTimeOfDay(long millis) {
+ Calendar c = Calendar.getInstance();
+ if (millis >= 0) {
+ c.setTimeInMillis(millis);
+ return String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c);
+ } else {
+ return Long.toString(millis);
+ }
+ }
}
diff --git a/core/java/android/view/GLES20DisplayList.java b/core/java/android/view/GLES20DisplayList.java
index f3618eb..0154556 100644
--- a/core/java/android/view/GLES20DisplayList.java
+++ b/core/java/android/view/GLES20DisplayList.java
@@ -131,7 +131,8 @@ class GLES20DisplayList extends DisplayList {
@Override
public void setAnimationMatrix(Matrix matrix) {
try {
- nSetAnimationMatrix(getNativeDisplayList(), matrix.native_instance);
+ nSetAnimationMatrix(getNativeDisplayList(),
+ (matrix != null) ? matrix.native_instance : 0);
} catch (IllegalStateException e) {
// invalid DisplayList okay: we'll set current values the next time we render to it
}
diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java
index 651be2e..2048de2 100644
--- a/core/java/android/view/TextureView.java
+++ b/core/java/android/view/TextureView.java
@@ -561,7 +561,17 @@ public class TextureView extends View {
applyUpdate();
applyTransformMatrix();
- mLayer.copyInto(bitmap);
+ // This case can happen if the app invokes setSurfaceTexture() before
+ // we are able to create the hardware layer. We can safely initialize
+ // the layer here thanks to the validate() call at the beginning of
+ // this method
+ if (mLayer == null && mUpdateSurface) {
+ getHardwareLayer();
+ }
+
+ if (mLayer != null) {
+ mLayer.copyInto(bitmap);
+ }
}
return bitmap;
}
diff --git a/core/java/android/view/VelocityTracker.java b/core/java/android/view/VelocityTracker.java
index 1c35e31..f703e34 100644
--- a/core/java/android/view/VelocityTracker.java
+++ b/core/java/android/view/VelocityTracker.java
@@ -298,6 +298,24 @@ public final class VelocityTracker implements Poolable<VelocityTracker> {
return estimate(time, yCoeff);
}
+ /**
+ * Gets the X coefficient with the specified index.
+ * @param index The index of the coefficient to return.
+ * @return The X coefficient, or 0 if the index is greater than the degree.
+ */
+ public float getXCoeff(int index) {
+ return index <= degree ? xCoeff[index] : 0;
+ }
+
+ /**
+ * Gets the Y coefficient with the specified index.
+ * @param index The index of the coefficient to return.
+ * @return The Y coefficient, or 0 if the index is greater than the degree.
+ */
+ public float getYCoeff(int index) {
+ return index <= degree ? yCoeff[index] : 0;
+ }
+
private float estimate(float time, float[] c) {
float a = 0;
float scale = 1;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index aad6756..55ea938 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -2128,6 +2128,20 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
*/
static final int ACCESSIBILITY_STATE_CHANGED = 0x00000080 << IMPORTANT_FOR_ACCESSIBILITY_SHIFT;
+ /**
+ * Flag indicating that view has an animation set on it. This is used to track whether an
+ * animation is cleared between successive frames, in order to tell the associated
+ * DisplayList to clear its animation matrix.
+ */
+ static final int VIEW_IS_ANIMATING_TRANSFORM = 0x10000000;
+
+ /**
+ * Flag indicating whether a view failed the quickReject() check in draw(). This condition
+ * is used to check whether later changes to the view's transform should invalidate the
+ * view to force the quickReject test to run again.
+ */
+ static final int VIEW_QUICK_REJECTED = 0x20000000;
+
/* End of masks for mPrivateFlags2 */
static final int DRAG_MASK = DRAG_CAN_ACCEPT | DRAG_HOVERED;
@@ -5209,6 +5223,13 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
* call to continue to your children, you must be sure to call the super
* implementation.
*
+ * <p>Here is a sample layout that makes use of fitting system windows
+ * to have controls for a video view placed inside of the window decorations
+ * that it hides and shows. This can be used with code like the second
+ * sample (video player) shown in {@link #setSystemUiVisibility(int)}.
+ *
+ * {@sample development/samples/ApiDemos/res/layout/video_player.xml complete}
+ *
* @param insets Current content insets of the window. Prior to
* {@link android.os.Build.VERSION_CODES#JELLY_BEAN} you must not modify
* the insets or else you and Android will be unhappy.
@@ -5251,7 +5272,8 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
}
/**
- * Check for the FITS_SYSTEM_WINDOWS flag. If this method returns true, this view
+ * Check for state of {@link #setFitsSystemWindows(boolean). If this method
+ * returns true, this view
* will account for system screen decorations such as the status bar and inset its
* content. This allows the view to be positioned in absolute screen coordinates
* and remain visible to the user.
@@ -5260,10 +5282,15 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
*
* @attr ref android.R.styleable#View_fitsSystemWindows
*/
- public boolean fitsSystemWindows() {
+ public boolean getFitsSystemWindows() {
return (mViewFlags & FITS_SYSTEM_WINDOWS) == FITS_SYSTEM_WINDOWS;
}
+ /** @hide */
+ public boolean fitsSystemWindows() {
+ return getFitsSystemWindows();
+ }
+
/**
* Ask that a new dispatch of {@link #fitSystemWindows(Rect)} be performed.
*/
@@ -8547,6 +8574,10 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
if (mDisplayList != null) {
mDisplayList.setCameraDistance(-Math.abs(distance) / dpi);
}
+ if ((mPrivateFlags2 & VIEW_QUICK_REJECTED) == VIEW_QUICK_REJECTED) {
+ // View was rejected last time it was drawn by its parent; this may have changed
+ invalidateParentIfNeeded();
+ }
}
/**
@@ -8589,6 +8620,10 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
if (mDisplayList != null) {
mDisplayList.setRotation(rotation);
}
+ if ((mPrivateFlags2 & VIEW_QUICK_REJECTED) == VIEW_QUICK_REJECTED) {
+ // View was rejected last time it was drawn by its parent; this may have changed
+ invalidateParentIfNeeded();
+ }
}
}
@@ -8636,6 +8671,10 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
if (mDisplayList != null) {
mDisplayList.setRotationY(rotationY);
}
+ if ((mPrivateFlags2 & VIEW_QUICK_REJECTED) == VIEW_QUICK_REJECTED) {
+ // View was rejected last time it was drawn by its parent; this may have changed
+ invalidateParentIfNeeded();
+ }
}
}
@@ -8683,6 +8722,10 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
if (mDisplayList != null) {
mDisplayList.setRotationX(rotationX);
}
+ if ((mPrivateFlags2 & VIEW_QUICK_REJECTED) == VIEW_QUICK_REJECTED) {
+ // View was rejected last time it was drawn by its parent; this may have changed
+ invalidateParentIfNeeded();
+ }
}
}
@@ -8722,6 +8765,10 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
if (mDisplayList != null) {
mDisplayList.setScaleX(scaleX);
}
+ if ((mPrivateFlags2 & VIEW_QUICK_REJECTED) == VIEW_QUICK_REJECTED) {
+ // View was rejected last time it was drawn by its parent; this may have changed
+ invalidateParentIfNeeded();
+ }
}
}
@@ -8761,6 +8808,10 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
if (mDisplayList != null) {
mDisplayList.setScaleY(scaleY);
}
+ if ((mPrivateFlags2 & VIEW_QUICK_REJECTED) == VIEW_QUICK_REJECTED) {
+ // View was rejected last time it was drawn by its parent; this may have changed
+ invalidateParentIfNeeded();
+ }
}
}
@@ -8808,6 +8859,10 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
if (mDisplayList != null) {
mDisplayList.setPivotX(pivotX);
}
+ if ((mPrivateFlags2 & VIEW_QUICK_REJECTED) == VIEW_QUICK_REJECTED) {
+ // View was rejected last time it was drawn by its parent; this may have changed
+ invalidateParentIfNeeded();
+ }
}
}
@@ -8854,6 +8909,10 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
if (mDisplayList != null) {
mDisplayList.setPivotY(pivotY);
}
+ if ((mPrivateFlags2 & VIEW_QUICK_REJECTED) == VIEW_QUICK_REJECTED) {
+ // View was rejected last time it was drawn by its parent; this may have changed
+ invalidateParentIfNeeded();
+ }
}
}
@@ -9012,6 +9071,10 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
}
mBackgroundSizeChanged = true;
invalidateParentIfNeeded();
+ if ((mPrivateFlags2 & VIEW_QUICK_REJECTED) == VIEW_QUICK_REJECTED) {
+ // View was rejected last time it was drawn by its parent; this may have changed
+ invalidateParentIfNeeded();
+ }
}
}
@@ -9081,6 +9144,10 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
}
mBackgroundSizeChanged = true;
invalidateParentIfNeeded();
+ if ((mPrivateFlags2 & VIEW_QUICK_REJECTED) == VIEW_QUICK_REJECTED) {
+ // View was rejected last time it was drawn by its parent; this may have changed
+ invalidateParentIfNeeded();
+ }
}
}
@@ -9144,6 +9211,10 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
}
mBackgroundSizeChanged = true;
invalidateParentIfNeeded();
+ if ((mPrivateFlags2 & VIEW_QUICK_REJECTED) == VIEW_QUICK_REJECTED) {
+ // View was rejected last time it was drawn by its parent; this may have changed
+ invalidateParentIfNeeded();
+ }
}
}
@@ -9204,6 +9275,10 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
}
mBackgroundSizeChanged = true;
invalidateParentIfNeeded();
+ if ((mPrivateFlags2 & VIEW_QUICK_REJECTED) == VIEW_QUICK_REJECTED) {
+ // View was rejected last time it was drawn by its parent; this may have changed
+ invalidateParentIfNeeded();
+ }
}
}
@@ -9288,6 +9363,10 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
if (mDisplayList != null) {
mDisplayList.setTranslationX(translationX);
}
+ if ((mPrivateFlags2 & VIEW_QUICK_REJECTED) == VIEW_QUICK_REJECTED) {
+ // View was rejected last time it was drawn by its parent; this may have changed
+ invalidateParentIfNeeded();
+ }
}
}
@@ -9325,6 +9404,10 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
if (mDisplayList != null) {
mDisplayList.setTranslationY(translationY);
}
+ if ((mPrivateFlags2 & VIEW_QUICK_REJECTED) == VIEW_QUICK_REJECTED) {
+ // View was rejected last time it was drawn by its parent; this may have changed
+ invalidateParentIfNeeded();
+ }
}
}
@@ -12764,16 +12847,27 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
if (a != null) {
more = drawAnimation(parent, drawingTime, a, scalingRequired);
concatMatrix = a.willChangeTransformationMatrix();
+ if (concatMatrix) {
+ mPrivateFlags2 |= VIEW_IS_ANIMATING_TRANSFORM;
+ }
transformToApply = parent.mChildTransformation;
- } else if (!useDisplayListProperties &&
- (flags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) {
- final boolean hasTransform =
- parent.getChildStaticTransformation(this, parent.mChildTransformation);
- if (hasTransform) {
- final int transformType = parent.mChildTransformation.getTransformationType();
- transformToApply = transformType != Transformation.TYPE_IDENTITY ?
- parent.mChildTransformation : null;
- concatMatrix = (transformType & Transformation.TYPE_MATRIX) != 0;
+ } else {
+ if ((mPrivateFlags2 & VIEW_IS_ANIMATING_TRANSFORM) == VIEW_IS_ANIMATING_TRANSFORM &&
+ mDisplayList != null) {
+ // No longer animating: clear out old animation matrix
+ mDisplayList.setAnimationMatrix(null);
+ mPrivateFlags2 &= ~VIEW_IS_ANIMATING_TRANSFORM;
+ }
+ if (!useDisplayListProperties &&
+ (flags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) {
+ final boolean hasTransform =
+ parent.getChildStaticTransformation(this, parent.mChildTransformation);
+ if (hasTransform) {
+ final int transformType = parent.mChildTransformation.getTransformationType();
+ transformToApply = transformType != Transformation.TYPE_IDENTITY ?
+ parent.mChildTransformation : null;
+ concatMatrix = (transformType & Transformation.TYPE_MATRIX) != 0;
+ }
}
}
@@ -12785,8 +12879,10 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
if (!concatMatrix && canvas.quickReject(mLeft, mTop, mRight, mBottom, Canvas.EdgeType.BW) &&
(mPrivateFlags & DRAW_ANIMATION) == 0) {
+ mPrivateFlags2 |= VIEW_QUICK_REJECTED;
return more;
}
+ mPrivateFlags2 &= ~VIEW_QUICK_REJECTED;
if (hardwareAccelerated) {
// Clear INVALIDATED flag to allow invalidation to occur during rendering, but
@@ -15376,7 +15472,8 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
* playing the application would like to go into a complete full-screen mode,
* to use as much of the display as possible for the video. When in this state
* the user can not interact with the application; the system intercepts
- * touching on the screen to pop the UI out of full screen mode.
+ * touching on the screen to pop the UI out of full screen mode. See
+ * {@link #fitSystemWindows(Rect)} for a sample layout that goes with this code.
*
* {@sample development/samples/ApiDemos/src/com/example/android/apis/view/VideoPlayerActivity.java
* content}
@@ -15458,11 +15555,13 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
}
}
- void updateLocalSystemUiVisibility(int localValue, int localChanges) {
+ boolean updateLocalSystemUiVisibility(int localValue, int localChanges) {
int val = (mSystemUiVisibility&~localChanges) | (localValue&localChanges);
if (val != mSystemUiVisibility) {
setSystemUiVisibility(val);
+ return true;
}
+ return false;
}
/** @hide */
@@ -16861,7 +16960,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
/**
* Interface definition for a callback to be invoked when the status bar changes
* visibility. This reports <strong>global</strong> changes to the system UI
- * state, not just what the application is requesting.
+ * state, not what the application is requesting.
*
* @see View#setOnSystemUiVisibilityChangeListener(android.view.View.OnSystemUiVisibilityChangeListener)
*/
@@ -16870,10 +16969,10 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
* Called when the status bar changes visibility because of a call to
* {@link View#setSystemUiVisibility(int)}.
*
- * @param visibility Bitwise-or of flags {@link #SYSTEM_UI_FLAG_LOW_PROFILE} or
- * {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION}. This tells you the
- * <strong>global</strong> state of the UI visibility flags, not what your
- * app is currently applying.
+ * @param visibility Bitwise-or of flags {@link #SYSTEM_UI_FLAG_LOW_PROFILE},
+ * {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION}, and {@link #SYSTEM_UI_FLAG_FULLSCREEN}.
+ * This tells you the <strong>global</strong> state of these UI visibility
+ * flags, not what your app is currently applying.
*/
public void onSystemUiVisibilityChange(int visibility);
}
@@ -17159,6 +17258,11 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
int mDisabledSystemUiVisibility;
/**
+ * Last global system UI visibility reported by the window manager.
+ */
+ int mGlobalSystemUiVisibility;
+
+ /**
* True if a view in this hierarchy has an OnSystemUiVisibilityChangeListener
* attached.
*/
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index acfca26..b3c8895 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -1317,15 +1317,16 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
}
@Override
- void updateLocalSystemUiVisibility(int localValue, int localChanges) {
- super.updateLocalSystemUiVisibility(localValue, localChanges);
+ boolean updateLocalSystemUiVisibility(int localValue, int localChanges) {
+ boolean changed = super.updateLocalSystemUiVisibility(localValue, localChanges);
final int count = mChildrenCount;
final View[] children = mChildren;
for (int i=0; i <count; i++) {
final View child = children[i];
- child.updateLocalSystemUiVisibility(localValue, localChanges);
+ changed |= child.updateLocalSystemUiVisibility(localValue, localChanges);
}
+ return changed;
}
/**
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index b43db14..1fcb2c3 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -3795,13 +3795,15 @@ public final class ViewRootImpl implements ViewParent,
}
if (mView == null) return;
if (args.localChanges != 0) {
- if (mAttachInfo != null) {
- mAttachInfo.mRecomputeGlobalAttributes = true;
- }
mView.updateLocalSystemUiVisibility(args.localValue, args.localChanges);
- scheduleTraversals();
}
- mView.dispatchSystemUiVisibilityChanged(args.globalVisibility);
+ if (mAttachInfo != null) {
+ int visibility = args.globalVisibility&View.SYSTEM_UI_CLEARABLE_FLAGS;
+ if (visibility != mAttachInfo.mGlobalSystemUiVisibility) {
+ mAttachInfo.mGlobalSystemUiVisibility = visibility;
+ mView.dispatchSystemUiVisibilityChanged(visibility);
+ }
+ }
}
public void handleDispatchDoneAnimating() {
diff --git a/core/java/android/webkit/HTML5VideoFullScreen.java b/core/java/android/webkit/HTML5VideoFullScreen.java
index 730ad08..62bc502 100644
--- a/core/java/android/webkit/HTML5VideoFullScreen.java
+++ b/core/java/android/webkit/HTML5VideoFullScreen.java
@@ -104,7 +104,9 @@ public class HTML5VideoFullScreen extends HTML5VideoView
// After we return from this we can't use the surface any more.
// The current Video View will be destroy when we play a new video.
pauseAndDispatch(mProxy);
+ // TODO: handle full screen->inline mode transition without a reload.
mPlayer.release();
+ mPlayer = null;
mSurfaceHolder = null;
if (mMediaController != null) {
mMediaController.hide();
@@ -128,12 +130,12 @@ public class HTML5VideoFullScreen extends HTML5VideoView
return mVideoSurfaceView;
}
- HTML5VideoFullScreen(Context context, int videoLayerId, int position) {
+ HTML5VideoFullScreen(Context context, int videoLayerId, int position, boolean skipPrepare) {
mVideoSurfaceView = new VideoSurfaceView(context);
mFullScreenMode = FULLSCREEN_OFF;
mVideoWidth = 0;
mVideoHeight = 0;
- init(videoLayerId, position);
+ init(videoLayerId, position, skipPrepare);
}
private void setMediaController(MediaController m) {
@@ -156,8 +158,6 @@ public class HTML5VideoFullScreen extends HTML5VideoView
}
private void prepareForFullScreen() {
- // So in full screen, we reset the MediaPlayer
- mPlayer.reset();
MediaController mc = new FullScreenMediaController(mProxy.getContext(), mLayout);
mc.setSystemUiVisibility(mLayout.getSystemUiVisibility());
setMediaController(mc);
@@ -198,6 +198,7 @@ public class HTML5VideoFullScreen extends HTML5VideoView
// after reading the MetaData
if (mMediaController != null) {
mMediaController.setEnabled(true);
+ mMediaController.show();
}
if (mProgressView != null) {
@@ -243,7 +244,7 @@ public class HTML5VideoFullScreen extends HTML5VideoView
// Don't show the controller after exiting the full screen.
mMediaController = null;
- mCurrentState = STATE_RELEASED;
+ mCurrentState = STATE_RESETTED;
}
};
@@ -320,6 +321,13 @@ public class HTML5VideoFullScreen extends HTML5VideoView
return 0;
}
+ @Override
+ public void showControllerInFullScreen() {
+ if (mMediaController != null) {
+ mMediaController.show(0);
+ }
+ }
+
// Other listeners functions:
private MediaPlayer.OnBufferingUpdateListener mBufferingUpdateListener =
new MediaPlayer.OnBufferingUpdateListener() {
diff --git a/core/java/android/webkit/HTML5VideoInline.java b/core/java/android/webkit/HTML5VideoInline.java
index 62e812e..2c7ea5d 100644
--- a/core/java/android/webkit/HTML5VideoInline.java
+++ b/core/java/android/webkit/HTML5VideoInline.java
@@ -21,7 +21,7 @@ public class HTML5VideoInline extends HTML5VideoView{
// associated with the surface texture can be used for showing the screen
// shot when paused, so they are not singleton.
private static SurfaceTexture mSurfaceTexture = null;
- private int[] mTextureNames;
+ private static int[] mTextureNames = null;
// Every time when the VideoLayer Id change, we need to recreate the
// SurfaceTexture in order to delete the old video's decoder memory.
private static int mVideoLayerUsingSurfaceTexture = -1;
@@ -35,8 +35,7 @@ public class HTML5VideoInline extends HTML5VideoView{
}
HTML5VideoInline(int videoLayerId, int position) {
- init(videoLayerId, position);
- mTextureNames = null;
+ init(videoLayerId, position, false);
}
@Override
@@ -69,15 +68,14 @@ public class HTML5VideoInline extends HTML5VideoView{
// Inline Video specific FUNCTIONS:
- @Override
- public SurfaceTexture getSurfaceTexture(int videoLayerId) {
+ public static SurfaceTexture getSurfaceTexture(int videoLayerId) {
// Create the surface texture.
if (videoLayerId != mVideoLayerUsingSurfaceTexture
|| mSurfaceTexture == null
|| mTextureNames == null) {
- if (mTextureNames != null) {
- GLES20.glDeleteTextures(1, mTextureNames, 0);
- }
+ // The GL texture will store in the VideoLayerManager at native side.
+ // They will be clean up when requested.
+ // The reason we recreated GL texture name is for screen shot support.
mTextureNames = new int[1];
GLES20.glGenTextures(1, mTextureNames, 0);
mSurfaceTexture = new SurfaceTexture(mTextureNames[0]);
diff --git a/core/java/android/webkit/HTML5VideoView.java b/core/java/android/webkit/HTML5VideoView.java
index 0d3b755..371feea 100644
--- a/core/java/android/webkit/HTML5VideoView.java
+++ b/core/java/android/webkit/HTML5VideoView.java
@@ -31,11 +31,10 @@ public class HTML5VideoView implements MediaPlayer.OnPreparedListener {
// NOTE: these values are in sync with VideoLayerAndroid.h in webkit side.
// Please keep them in sync when changed.
static final int STATE_INITIALIZED = 0;
- static final int STATE_NOTPREPARED = 1;
+ static final int STATE_PREPARING = 1;
static final int STATE_PREPARED = 2;
static final int STATE_PLAYING = 3;
- static final int STATE_RELEASED = 4;
- protected int mCurrentState;
+ static final int STATE_RESETTED = 4;
protected HTML5VideoViewProxy mProxy;
@@ -46,11 +45,11 @@ public class HTML5VideoView implements MediaPlayer.OnPreparedListener {
// This is used to find the VideoLayer on the native side.
protected int mVideoLayerId;
- // Every video will have one MediaPlayer. Given the fact we only have one
- // SurfaceTexture, there is only one MediaPlayer in action. Every time we
- // switch videos, a new instance of MediaPlayer will be created in reset().
- // Switching between inline and full screen will also create a new instance.
- protected MediaPlayer mPlayer;
+ // Given the fact we only have one SurfaceTexture, we cannot support multiple
+ // player at the same time. We may recreate a new one and abandon the old
+ // one at transition time.
+ protected static MediaPlayer mPlayer = null;
+ protected static int mCurrentState = -1;
// We need to save such info.
protected Uri mUri;
@@ -60,10 +59,12 @@ public class HTML5VideoView implements MediaPlayer.OnPreparedListener {
// See http://www.whatwg.org/specs/web-apps/current-work/#event-media-timeupdate
protected static Timer mTimer;
+ protected boolean mPauseDuringPreparing;
+
// The spec says the timer should fire every 250 ms or less.
private static final int TIMEUPDATE_PERIOD = 250; // ms
+ private boolean mSkipPrepare = false;
- protected boolean mPauseDuringPreparing;
// common Video control FUNCTIONS:
public void start() {
if (mCurrentState == STATE_PREPARED) {
@@ -83,7 +84,7 @@ public class HTML5VideoView implements MediaPlayer.OnPreparedListener {
public void pause() {
if (isPlaying()) {
mPlayer.pause();
- } else if (mCurrentState == STATE_NOTPREPARED) {
+ } else if (mCurrentState == STATE_PREPARING) {
mPauseDuringPreparing = true;
}
// Delete the Timer to stop it since there is no stop call.
@@ -124,11 +125,11 @@ public class HTML5VideoView implements MediaPlayer.OnPreparedListener {
}
}
- public void release() {
- if (mCurrentState != STATE_RELEASED) {
- mPlayer.release();
+ public void reset() {
+ if (mCurrentState != STATE_RESETTED) {
+ mPlayer.reset();
}
- mCurrentState = STATE_RELEASED;
+ mCurrentState = STATE_RESETTED;
}
public void stopPlayback() {
@@ -142,9 +143,16 @@ public class HTML5VideoView implements MediaPlayer.OnPreparedListener {
}
// Every time we start a new Video, we create a VideoView and a MediaPlayer
- public void init(int videoLayerId, int position) {
- mPlayer = new MediaPlayer();
- mCurrentState = STATE_INITIALIZED;
+ public void init(int videoLayerId, int position, boolean skipPrepare) {
+ if (mPlayer == null) {
+ mPlayer = new MediaPlayer();
+ mCurrentState = STATE_INITIALIZED;
+ }
+ mSkipPrepare = skipPrepare;
+ // If we want to skip the prepare, then we keep the state.
+ if (!mSkipPrepare) {
+ mCurrentState = STATE_INITIALIZED;
+ }
mProxy = null;
mVideoLayerId = videoLayerId;
mSaveSeekTime = position;
@@ -195,17 +203,28 @@ public class HTML5VideoView implements MediaPlayer.OnPreparedListener {
}
public void prepareDataCommon(HTML5VideoViewProxy proxy) {
- try {
- mPlayer.setDataSource(proxy.getContext(), mUri, mHeaders);
- mPlayer.prepareAsync();
- } catch (IllegalArgumentException e) {
- e.printStackTrace();
- } catch (IllegalStateException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
+ if (!mSkipPrepare) {
+ try {
+ mPlayer.reset();
+ mPlayer.setDataSource(proxy.getContext(), mUri, mHeaders);
+ mPlayer.prepareAsync();
+ } catch (IllegalArgumentException e) {
+ e.printStackTrace();
+ } catch (IllegalStateException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ mCurrentState = STATE_PREPARING;
+ } else {
+ // If we skip prepare and the onPrepared happened in inline mode, we
+ // don't need to call prepare again, we just need to call onPrepared
+ // to refresh the state here.
+ if (mCurrentState >= STATE_PREPARED) {
+ onPrepared(mPlayer);
+ }
+ mSkipPrepare = false;
}
- mCurrentState = STATE_NOTPREPARED;
}
public void reprepareData(HTML5VideoViewProxy proxy) {
@@ -294,10 +313,6 @@ public class HTML5VideoView implements MediaPlayer.OnPreparedListener {
return false;
}
- public SurfaceTexture getSurfaceTexture(int videoLayerId) {
- return null;
- }
-
public void deleteSurfaceTexture() {
}
@@ -332,14 +347,17 @@ public class HTML5VideoView implements MediaPlayer.OnPreparedListener {
return false;
}
- private boolean m_startWhenPrepared = false;
+ private boolean mStartWhenPrepared = false;
public void setStartWhenPrepared(boolean willPlay) {
- m_startWhenPrepared = willPlay;
+ mStartWhenPrepared = willPlay;
}
public boolean getStartWhenPrepared() {
- return m_startWhenPrepared;
+ return mStartWhenPrepared;
+ }
+
+ public void showControllerInFullScreen() {
}
}
diff --git a/core/java/android/webkit/HTML5VideoViewProxy.java b/core/java/android/webkit/HTML5VideoViewProxy.java
index 5fa4bad..90db308 100644
--- a/core/java/android/webkit/HTML5VideoViewProxy.java
+++ b/core/java/android/webkit/HTML5VideoViewProxy.java
@@ -112,13 +112,14 @@ class HTML5VideoViewProxy extends Handler
mBaseLayer = layer;
int currentVideoLayerId = mHTML5VideoView.getVideoLayerId();
- SurfaceTexture surfTexture = mHTML5VideoView.getSurfaceTexture(currentVideoLayerId);
+ SurfaceTexture surfTexture =
+ HTML5VideoInline.getSurfaceTexture(currentVideoLayerId);
int textureName = mHTML5VideoView.getTextureName();
if (layer != 0 && surfTexture != null && currentVideoLayerId != -1) {
int playerState = mHTML5VideoView.getCurrentState();
if (mHTML5VideoView.getPlayerBuffering())
- playerState = HTML5VideoView.STATE_NOTPREPARED;
+ playerState = HTML5VideoView.STATE_PREPARING;
boolean foundInTree = nativeSendSurfaceTexture(surfTexture,
layer, currentVideoLayerId, textureName,
playerState);
@@ -145,6 +146,7 @@ class HTML5VideoViewProxy extends Handler
HTML5VideoViewProxy proxy, WebViewClassic webView) {
// Save the inline video info and inherit it in the full screen
int savePosition = 0;
+ boolean canSkipPrepare = false;
if (mHTML5VideoView != null) {
// We don't allow enter full screen mode while the previous
// full screen video hasn't finished yet.
@@ -156,15 +158,20 @@ class HTML5VideoViewProxy extends Handler
// save the current position.
if (layerId == mHTML5VideoView.getVideoLayerId()) {
savePosition = mHTML5VideoView.getCurrentPosition();
+ int playerState = mHTML5VideoView.getCurrentState();
+ canSkipPrepare = (playerState == HTML5VideoView.STATE_PREPARING
+ || playerState == HTML5VideoView.STATE_PREPARED
+ || playerState == HTML5VideoView.STATE_PLAYING)
+ && !mHTML5VideoView.isFullScreenMode();
+ }
+ if (!canSkipPrepare) {
+ mHTML5VideoView.reset();
}
- mHTML5VideoView.release();
}
mHTML5VideoView = new HTML5VideoFullScreen(proxy.getContext(),
- layerId, savePosition);
+ layerId, savePosition, canSkipPrepare);
mCurrentProxy = proxy;
-
mHTML5VideoView.setVideoURI(url, mCurrentProxy);
-
mHTML5VideoView.enterFullScreenVideoState(layerId, proxy, webView);
}
@@ -217,8 +224,7 @@ class HTML5VideoViewProxy extends Handler
if (!backFromFullScreenMode) {
mHTML5VideoView.pauseAndDispatch(mCurrentProxy);
}
- // release the media player to avoid finalize error
- mHTML5VideoView.release();
+ mHTML5VideoView.reset();
}
mCurrentProxy = proxy;
mHTML5VideoView = new HTML5VideoInline(videoLayerId, time);
@@ -273,6 +279,7 @@ class HTML5VideoViewProxy extends Handler
}
public static void end() {
+ mHTML5VideoView.showControllerInFullScreen();
if (mCurrentProxy != null) {
if (isVideoSelfEnded)
mCurrentProxy.dispatchOnEnded();
diff --git a/core/java/android/webkit/WebViewInputDispatcher.java b/core/java/android/webkit/WebViewInputDispatcher.java
index 9541435..9328d8c 100644
--- a/core/java/android/webkit/WebViewInputDispatcher.java
+++ b/core/java/android/webkit/WebViewInputDispatcher.java
@@ -334,6 +334,7 @@ final class WebViewInputDispatcher {
DispatchEvent d = obtainDispatchEventLocked(eventToEnqueue, eventType, 0,
webKitXOffset, webKitYOffset, webKitScale);
+ updateStateTrackersLocked(d, event);
enqueueEventLocked(d);
}
return true;
@@ -787,7 +788,6 @@ final class WebViewInputDispatcher {
flags = d.mFlags;
- updateStateTrackersLocked(d, event);
if (event == d.mEvent) {
d.mEvent = null; // retain ownership of event, don't recycle it yet
}
diff --git a/core/java/android/widget/SpellChecker.java b/core/java/android/widget/SpellChecker.java
index ebf8a4a..7ca02e1 100644
--- a/core/java/android/widget/SpellChecker.java
+++ b/core/java/android/widget/SpellChecker.java
@@ -343,6 +343,36 @@ public class SpellChecker implements SpellCheckerSessionListener {
if (!isInDictionary && looksLikeTypo) {
createMisspelledSuggestionSpan(
editable, suggestionsInfo, spellCheckSpan, offset, length);
+ } else {
+ // Valid word -- isInDictionary || !looksLikeTypo
+ if (mIsSentenceSpellCheckSupported) {
+ // Allow the spell checker to remove existing misspelled span by
+ // overwriting the span over the same place
+ final int spellCheckSpanStart = editable.getSpanStart(spellCheckSpan);
+ final int spellCheckSpanEnd = editable.getSpanEnd(spellCheckSpan);
+ final int start;
+ final int end;
+ if (offset != USE_SPAN_RANGE && length != USE_SPAN_RANGE) {
+ start = spellCheckSpanStart + offset;
+ end = start + length;
+ } else {
+ start = spellCheckSpanStart;
+ end = spellCheckSpanEnd;
+ }
+ if (spellCheckSpanStart >= 0 && spellCheckSpanEnd > spellCheckSpanStart
+ && end > start) {
+ final Long key = Long.valueOf(TextUtils.packRangeInLong(start, end));
+ final SuggestionSpan tempSuggestionSpan = mSuggestionSpanCache.get(key);
+ if (tempSuggestionSpan != null) {
+ if (DBG) {
+ Log.i(TAG, "Remove existing misspelled span. "
+ + editable.subSequence(start, end));
+ }
+ editable.removeSpan(tempSuggestionSpan);
+ mSuggestionSpanCache.remove(key);
+ }
+ }
+ }
}
return spellCheckSpan;
}
@@ -473,8 +503,16 @@ public class SpellChecker implements SpellCheckerSessionListener {
private Object mRange = new Object();
public void parse(int start, int end) {
- if (end > start) {
- setRangeSpan((Editable) mTextView.getText(), start, end);
+ final int max = mTextView.length();
+ final int parseEnd;
+ if (end > max) {
+ Log.w(TAG, "Parse invalid region, from " + start + " to " + end);
+ parseEnd = max;
+ } else {
+ parseEnd = end;
+ }
+ if (parseEnd > start) {
+ setRangeSpan((Editable) mTextView.getText(), start, parseEnd);
parse();
}
}
@@ -612,6 +650,8 @@ public class SpellChecker implements SpellCheckerSessionListener {
break;
}
if (spellCheckEnd <= spellCheckStart) {
+ Log.w(TAG, "Trying to spellcheck invalid region, from "
+ + start + " to " + end);
break;
}
if (createSpellCheckSpan) {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 555c974..098a034 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -154,6 +154,7 @@ import java.util.Locale;
* @attr ref android.R.styleable#TextView_textColorLink
* @attr ref android.R.styleable#TextView_textSize
* @attr ref android.R.styleable#TextView_textScaleX
+ * @attr ref android.R.styleable#TextView_fontFamily
* @attr ref android.R.styleable#TextView_typeface
* @attr ref android.R.styleable#TextView_textStyle
* @attr ref android.R.styleable#TextView_cursorVisible
@@ -464,6 +465,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
ColorStateList textColorHint = null;
ColorStateList textColorLink = null;
int textSize = 15;
+ String fontFamily = null;
int typefaceIndex = -1;
int styleIndex = -1;
boolean allCaps = false;
@@ -516,6 +518,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
typefaceIndex = appearance.getInt(attr, -1);
break;
+ case com.android.internal.R.styleable.TextAppearance_fontFamily:
+ fontFamily = appearance.getString(attr);
+ break;
+
case com.android.internal.R.styleable.TextAppearance_textStyle:
styleIndex = appearance.getInt(attr, -1);
break;
@@ -781,6 +787,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
styleIndex = a.getInt(attr, styleIndex);
break;
+ case com.android.internal.R.styleable.TextView_fontFamily:
+ fontFamily = a.getString(attr);
+ break;
+
case com.android.internal.R.styleable.TextView_password:
password = a.getBoolean(attr, password);
break;
@@ -1051,7 +1061,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
typefaceIndex = MONOSPACE;
}
- setTypefaceByIndex(typefaceIndex, styleIndex);
+ setTypefaceFromAttrs(fontFamily, typefaceIndex, styleIndex);
if (shadowcolor != 0) {
setShadowLayer(r, dx, dy, shadowcolor);
@@ -1111,8 +1121,15 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
}
- private void setTypefaceByIndex(int typefaceIndex, int styleIndex) {
+ private void setTypefaceFromAttrs(String familyName, int typefaceIndex, int styleIndex) {
Typeface tf = null;
+ if (familyName != null) {
+ tf = Typeface.create(familyName, styleIndex);
+ if (tf != null) {
+ setTypeface(tf);
+ return;
+ }
+ }
switch (typefaceIndex) {
case SANS:
tf = Typeface.SANS_SERIF;
@@ -2160,14 +2177,17 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
setLinkTextColor(colors);
}
+ String familyName;
int typefaceIndex, styleIndex;
+ familyName = appearance.getString(com.android.internal.R.styleable.
+ TextAppearance_fontFamily);
typefaceIndex = appearance.getInt(com.android.internal.R.styleable.
TextAppearance_typeface, -1);
styleIndex = appearance.getInt(com.android.internal.R.styleable.
TextAppearance_textStyle, -1);
- setTypefaceByIndex(typefaceIndex, styleIndex);
+ setTypefaceFromAttrs(familyName, typefaceIndex, styleIndex);
if (appearance.getBoolean(com.android.internal.R.styleable.TextAppearance_textAllCaps,
false)) {
@@ -2269,6 +2289,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
*
* @see #getTypeface()
*
+ * @attr ref android.R.styleable#TextView_fontFamily
* @attr ref android.R.styleable#TextView_typeface
* @attr ref android.R.styleable#TextView_textStyle
*/
@@ -2290,6 +2311,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
*
* @see #setTypeface(Typeface)
*
+ * @attr ref android.R.styleable#TextView_fontFamily
* @attr ref android.R.styleable#TextView_typeface
* @attr ref android.R.styleable#TextView_textStyle
*/
@@ -3690,15 +3712,15 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
boolean forceUpdate = false;
if (isPassword) {
setTransformationMethod(PasswordTransformationMethod.getInstance());
- setTypefaceByIndex(MONOSPACE, 0);
+ setTypefaceFromAttrs(null /* fontFamily */, MONOSPACE, 0);
} else if (isVisiblePassword) {
if (mTransformation == PasswordTransformationMethod.getInstance()) {
forceUpdate = true;
}
- setTypefaceByIndex(MONOSPACE, 0);
+ setTypefaceFromAttrs(null /* fontFamily */, MONOSPACE, 0);
} else if (wasPassword || wasVisiblePassword) {
// not in password mode, clean up typeface and transformation
- setTypefaceByIndex(-1, -1);
+ setTypefaceFromAttrs(null /* fontFamily */, -1, -1);
if (mTransformation == PasswordTransformationMethod.getInstance()) {
forceUpdate = true;
}
diff --git a/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java b/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java
index a74ecd3..d6ffba2 100644
--- a/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java
+++ b/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java
@@ -69,16 +69,19 @@ public class MultiWaveView extends View {
public void onGrabbedStateChange(View v, int handle);
}
- // Tune-able parameters
+ // Tuneable parameters for animation
private static final int CHEVRON_INCREMENTAL_DELAY = 160;
private static final int CHEVRON_ANIMATION_DURATION = 850;
private static final int RETURN_TO_HOME_DELAY = 1200;
private static final int RETURN_TO_HOME_DURATION = 300;
private static final int HIDE_ANIMATION_DELAY = 200;
- private static final int HIDE_ANIMATION_DURATION = RETURN_TO_HOME_DELAY;
- private static final int SHOW_ANIMATION_DURATION = 0;
+ private static final int HIDE_ANIMATION_DURATION = 200;
+ private static final int SHOW_ANIMATION_DURATION = 200;
private static final int SHOW_ANIMATION_DELAY = 0;
private static final float TAP_RADIUS_SCALE_ACCESSIBILITY_ENABLED = 1.3f;
+ private static final long RING_EXPAND_DURATION = 200;
+ private static final float TARGET_INITIAL_POSITION_SCALE = 0.8f;
+
private TimeInterpolator mChevronAnimationInterpolator = Ease.Quad.easeOut;
private ArrayList<TargetDrawable> mTargetDrawables = new ArrayList<TargetDrawable>();
@@ -149,6 +152,7 @@ public class MultiWaveView extends View {
private int mHorizontalInset;
private int mVerticalInset;
private int mGravity = Gravity.TOP;
+ private boolean mInitialLayout = true;
public MultiWaveView(Context context) {
this(context, null);
@@ -177,23 +181,37 @@ public class MultiWaveView extends View {
mAlwaysTrackFinger = a.getBoolean(R.styleable.MultiWaveView_alwaysTrackFinger, false);
mGravity = a.getInt(R.styleable.MultiWaveView_gravity, Gravity.TOP);
- // Read chevron animation drawables
- final int chevrons[] = { R.styleable.MultiWaveView_leftChevronDrawable,
- R.styleable.MultiWaveView_rightChevronDrawable,
- R.styleable.MultiWaveView_topChevronDrawable,
- R.styleable.MultiWaveView_bottomChevronDrawable
- };
-
- for (int chevron : chevrons) {
- TypedValue typedValue = a.peekValue(chevron);
- for (int i = 0; i < mFeedbackCount; i++) {
- mChevronDrawables.add(
- typedValue != null ? new TargetDrawable(res, typedValue.resourceId) : null);
+ // Read array of chevron drawables
+ TypedValue outValue = new TypedValue();
+ if (a.getValue(R.styleable.MultiWaveView_chevronDrawables, outValue)) {
+ ArrayList<TargetDrawable> chevrons = loadDrawableArray(outValue.resourceId);
+ for (int i = 0; i < chevrons.size(); i++) {
+ final TargetDrawable chevron = chevrons.get(i);
+ for (int k = 0; k < mFeedbackCount; k++) {
+ mChevronDrawables.add(chevron == null ? null : new TargetDrawable(chevron));
+ }
+ }
+ }
+
+ // Support old-style chevron specification if new specification not found
+ if (mChevronDrawables.size() == 0) {
+ final int chevronResIds[] = {
+ R.styleable.MultiWaveView_rightChevronDrawable,
+ R.styleable.MultiWaveView_topChevronDrawable,
+ R.styleable.MultiWaveView_leftChevronDrawable,
+ R.styleable.MultiWaveView_bottomChevronDrawable
+ };
+
+ for (int i = 0; i < chevronResIds.length; i++) {
+ TypedValue typedValue = a.peekValue(chevronResIds[i]);
+ for (int k = 0; k < mFeedbackCount; k++) {
+ mChevronDrawables.add(
+ typedValue != null ? new TargetDrawable(res, typedValue.resourceId) : null);
+ }
}
}
// Read array of target drawables
- TypedValue outValue = new TypedValue();
if (a.getValue(R.styleable.MultiWaveView_targetDrawables, outValue)) {
internalSetTargetResources(outValue.resourceId);
}
@@ -274,7 +292,7 @@ public class MultiWaveView extends View {
final int minimumHeight = getSuggestedMinimumHeight();
int computedWidth = resolveMeasured(widthMeasureSpec, minimumWidth);
int computedHeight = resolveMeasured(heightMeasureSpec, minimumHeight);
- setupGravity((computedWidth - minimumWidth), (computedHeight - minimumHeight));
+ computeInsets((computedWidth - minimumWidth), (computedHeight - minimumHeight));
setMeasuredDimension(computedWidth, computedHeight);
}
@@ -314,23 +332,24 @@ public class MultiWaveView extends View {
* mFeedbackCount items in the order: left, right, top, bottom.
*/
private void startChevronAnimation() {
- final float r = mHandleDrawable.getWidth() * 0.4f;
- final float chevronAnimationDistance = mOuterRadius * 0.9f;
- final float from[][] = {
- {mWaveCenterX - r, mWaveCenterY}, // left
- {mWaveCenterX + r, mWaveCenterY}, // right
- {mWaveCenterX, mWaveCenterY - r}, // top
- {mWaveCenterX, mWaveCenterY + r} }; // bottom
- final float to[][] = {
- {mWaveCenterX - chevronAnimationDistance, mWaveCenterY}, // left
- {mWaveCenterX + chevronAnimationDistance, mWaveCenterY}, // right
- {mWaveCenterX, mWaveCenterY - chevronAnimationDistance}, // top
- {mWaveCenterX, mWaveCenterY + chevronAnimationDistance} }; // bottom
-
+ final float chevronStartDistance = mHandleDrawable.getWidth() * 0.8f;
+ final float chevronStopDistance = mOuterRadius * 0.9f / 2.0f;
mChevronAnimations.clear();
final float startScale = 0.5f;
final float endScale = 2.0f;
- for (int direction = 0; direction < 4; direction++) {
+
+ final int directionCount = mFeedbackCount > 0 ? mChevronDrawables.size()/mFeedbackCount : 0;
+
+ // Add an animation for all chevron drawables. There are mFeedbackCount drawables
+ // in each direction and directionCount directions.
+ for (int direction = 0; direction < directionCount; direction++) {
+ double angle = 2.0 * Math.PI * direction / directionCount;
+ final float sx = (float) Math.cos(angle);
+ final float sy = 0.0f - (float) Math.sin(angle);
+ final float[] xrange = new float[]
+ {sx * chevronStartDistance, sx * chevronStopDistance};
+ final float[] yrange = new float[]
+ {sy * chevronStartDistance, sy * chevronStopDistance};
for (int count = 0; count < mFeedbackCount; count++) {
int delay = count * CHEVRON_INCREMENTAL_DELAY;
final TargetDrawable icon = mChevronDrawables.get(direction*mFeedbackCount + count);
@@ -340,8 +359,8 @@ public class MultiWaveView extends View {
mChevronAnimations.add(Tweener.to(icon, CHEVRON_ANIMATION_DURATION,
"ease", mChevronAnimationInterpolator,
"delay", delay,
- "x", new float[] { from[direction][0], to[direction][0] },
- "y", new float[] { from[direction][1], to[direction][1] },
+ "x", xrange,
+ "y", yrange,
"alpha", new float[] {1.0f, 0.0f},
"scaleX", new float[] {startScale, endScale},
"scaleY", new float[] {startScale, endScale},
@@ -416,32 +435,25 @@ public class MultiWaveView extends View {
mHandleDrawable.setAlpha(targetHit ? 0.0f : 1.0f);
if (targetHit) {
mTargetDrawables.get(activeTarget).setState(TargetDrawable.STATE_ACTIVE);
-
hideUnselected(activeTarget);
// Inform listener of any active targets. Typically only one will be active.
if (DEBUG) Log.v(TAG, "Finish with target hit = " + targetHit);
dispatchTriggerEvent(mActiveTarget);
- mHandleAnimation = Tweener.to(mHandleDrawable, 0,
- "ease", Ease.Quart.easeOut,
- "delay", RETURN_TO_HOME_DELAY,
- "alpha", 1.0f,
- "x", mWaveCenterX,
- "y", mWaveCenterY,
- "onUpdate", mUpdateListener,
- "onComplete", mResetListener);
- } else {
- // Animate finger outline back to home position
- mHandleAnimation = Tweener.to(mHandleDrawable, RETURN_TO_HOME_DURATION,
- "ease", Ease.Quart.easeOut,
- "delay", 0,
- "alpha", 1.0f,
- "x", mWaveCenterX,
- "y", mWaveCenterY,
- "onUpdate", mUpdateListener,
- "onComplete", mDragging ? mResetListenerWithPing : mResetListener);
}
+ // Animate handle back to the center based on current state.
+ int delay = targetHit ? RETURN_TO_HOME_DELAY : 0;
+ int duration = targetHit ? 0 : RETURN_TO_HOME_DURATION;
+ mHandleAnimation = Tweener.to(mHandleDrawable, duration,
+ "ease", Ease.Quart.easeOut,
+ "delay", delay,
+ "alpha", 1.0f,
+ "x", 0,
+ "y", 0,
+ "onUpdate", mUpdateListener,
+ "onComplete", (mDragging && !targetHit) ? mResetListenerWithPing : mResetListener);
+
setGrabbedState(OnTriggerListener.NO_HANDLE);
}
@@ -461,27 +473,30 @@ public class MultiWaveView extends View {
// Note: these animations should complete at the same time so that we can swap out
// the target assets asynchronously from the setTargetResources() call.
mAnimatingTargets = animate;
- if (animate) {
- final int duration = animate ? HIDE_ANIMATION_DURATION : 0;
- for (TargetDrawable target : mTargetDrawables) {
- target.setState(TargetDrawable.STATE_INACTIVE);
- mTargetAnimations.add(Tweener.to(target, duration,
- "alpha", 0.0f,
- "delay", HIDE_ANIMATION_DELAY,
- "onUpdate", mUpdateListener));
- }
- mTargetAnimations.add(Tweener.to(mOuterRing, duration,
+ final int duration = animate ? HIDE_ANIMATION_DURATION : 0;
+ final int delay = animate ? HIDE_ANIMATION_DELAY : 0;
+ final int length = mTargetDrawables.size();
+ for (int i = 0; i < length; i++) {
+ TargetDrawable target = mTargetDrawables.get(i);
+ target.setState(TargetDrawable.STATE_INACTIVE);
+ mTargetAnimations.add(Tweener.to(target, duration,
+ "ease", Ease.Cubic.easeOut,
"alpha", 0.0f,
- "delay", HIDE_ANIMATION_DELAY,
- "onUpdate", mUpdateListener,
- "onComplete", mTargetUpdateListener));
- } else {
- for (TargetDrawable target : mTargetDrawables) {
- target.setState(TargetDrawable.STATE_INACTIVE);
- target.setAlpha(0.0f);
- }
- mOuterRing.setAlpha(0.0f);
+ "scaleX", TARGET_INITIAL_POSITION_SCALE,
+ "scaleY", TARGET_INITIAL_POSITION_SCALE,
+ "delay", delay,
+ "onUpdate", mUpdateListener));
}
+
+ float ringScaleTarget = mActiveTarget != -1 ? 1.5f : 0.5f;
+ mTargetAnimations.add(Tweener.to(mOuterRing, duration,
+ "ease", Ease.Cubic.easeOut,
+ "alpha", 0.0f,
+ "scaleX", ringScaleTarget,
+ "scaleY", ringScaleTarget,
+ "delay", delay,
+ "onUpdate", mUpdateListener,
+ "onComplete", mTargetUpdateListener));
}
private void showTargets(boolean animate) {
@@ -489,26 +504,31 @@ public class MultiWaveView extends View {
stopTargetAnimation();
}
mAnimatingTargets = animate;
- if (animate) {
- for (TargetDrawable target : mTargetDrawables) {
- target.setState(TargetDrawable.STATE_INACTIVE);
- mTargetAnimations.add(Tweener.to(target, SHOW_ANIMATION_DURATION,
- "alpha", 1.0f,
- "delay", SHOW_ANIMATION_DELAY,
- "onUpdate", mUpdateListener));
- }
- mTargetAnimations.add(Tweener.to(mOuterRing, SHOW_ANIMATION_DURATION,
+ final int delay = animate ? SHOW_ANIMATION_DELAY : 0;
+ final int length = mTargetDrawables.size();
+ for (int i = 0; i < length; i++) {
+ TargetDrawable target = mTargetDrawables.get(i);
+ target.setState(TargetDrawable.STATE_INACTIVE);
+ target.setScaleX(TARGET_INITIAL_POSITION_SCALE);
+ target.setScaleY(TARGET_INITIAL_POSITION_SCALE);
+ mTargetAnimations.add(Tweener.to(target, animate ? SHOW_ANIMATION_DURATION : 0,
+ "ease", Ease.Cubic.easeOut,
"alpha", 1.0f,
- "delay", SHOW_ANIMATION_DELAY,
- "onUpdate", mUpdateListener,
- "onComplete", mTargetUpdateListener));
- } else {
- for (TargetDrawable target : mTargetDrawables) {
- target.setState(TargetDrawable.STATE_INACTIVE);
- target.setAlpha(1.0f);
- }
- mOuterRing.setAlpha(1.0f);
+ "scaleX", 1.0f,
+ "scaleY", 1.0f,
+ "delay", delay,
+ "onUpdate", mUpdateListener));
}
+ mOuterRing.setScaleX(0.5f);
+ mOuterRing.setScaleY(0.5f);
+ mTargetAnimations.add(Tweener.to(mOuterRing, animate ? RING_EXPAND_DURATION : 0,
+ "ease", Ease.Cubic.easeOut,
+ "alpha", 1.0f,
+ "scaleX", 1.0f,
+ "scaleY", 1.0f,
+ "delay", delay,
+ "onUpdate", mUpdateListener,
+ "onComplete", mTargetUpdateListener));
}
private void stopTargetAnimation() {
@@ -524,30 +544,39 @@ public class MultiWaveView extends View {
}
}
- private void internalSetTargetResources(int resourceId) {
+ private ArrayList<TargetDrawable> loadDrawableArray(int resourceId) {
Resources res = getContext().getResources();
TypedArray array = res.obtainTypedArray(resourceId);
- int count = array.length();
- ArrayList<TargetDrawable> targetDrawables = new ArrayList<TargetDrawable>(count);
+ final int count = array.length();
+ ArrayList<TargetDrawable> drawables = new ArrayList<TargetDrawable>(count);
+ for (int i = 0; i < count; i++) {
+ TypedValue value = array.peekValue(i);
+ TargetDrawable target = new TargetDrawable(res, value != null ? value.resourceId : 0);
+ drawables.add(target);
+ }
+ array.recycle();
+ return drawables;
+ }
+
+ private void internalSetTargetResources(int resourceId) {
+ mTargetDrawables = loadDrawableArray(resourceId);
+ mTargetResourceId = resourceId;
+ final int count = mTargetDrawables.size();
int maxWidth = mHandleDrawable.getWidth();
int maxHeight = mHandleDrawable.getHeight();
for (int i = 0; i < count; i++) {
- TypedValue value = array.peekValue(i);
- TargetDrawable target= new TargetDrawable(res, value != null ? value.resourceId : 0);
- targetDrawables.add(target);
+ TargetDrawable target = mTargetDrawables.get(i);
maxWidth = Math.max(maxWidth, target.getWidth());
maxHeight = Math.max(maxHeight, target.getHeight());
}
- mTargetResourceId = resourceId;
- mTargetDrawables = targetDrawables;
if (mMaxTargetWidth != maxWidth || mMaxTargetHeight != maxHeight) {
mMaxTargetWidth = maxWidth;
mMaxTargetHeight = maxHeight;
requestLayout(); // required to resize layout and call updateTargetPositions()
} else {
- updateTargetPositions();
+ updateTargetPositions(mWaveCenterX, mWaveCenterY);
+ updateChevronPositions(mWaveCenterX, mWaveCenterY);
}
- array.recycle();
}
/**
@@ -645,8 +674,8 @@ public class MultiWaveView extends View {
stopTargetAnimation();
hideChevrons();
hideTargets(animate);
- mHandleDrawable.setX(mWaveCenterX);
- mHandleDrawable.setY(mWaveCenterY);
+ mHandleDrawable.setX(0);
+ mHandleDrawable.setY(0);
mHandleDrawable.setState(TargetDrawable.STATE_INACTIVE);
Tweener.reset();
}
@@ -677,7 +706,7 @@ public class MultiWaveView extends View {
case MotionEvent.ACTION_CANCEL:
if (DEBUG) Log.v(TAG, "*** CANCEL ***");
- // handleMove(event);
+ handleMove(event);
handleCancel(event);
handled = true;
break;
@@ -687,7 +716,6 @@ public class MultiWaveView extends View {
}
private void moveHandleTo(float x, float y, boolean animate) {
- // TODO: animate the handle based on the current state/position
mHandleDrawable.setX(x);
mHandleDrawable.setY(y);
}
@@ -707,7 +735,14 @@ public class MultiWaveView extends View {
private void handleCancel(MotionEvent event) {
if (DEBUG && mDragging) Log.v(TAG, "** Handle CANCEL");
- mActiveTarget = -1; // Drop the active target if canceled.
+
+ // We should drop the active target here but it interferes with
+ // moving off the screen in the direction of the navigation bar. At some point we may
+ // want to revisit how we handle this. For now we'll allow a canceled event to
+ // activate the current target.
+
+ // mActiveTarget = -1; // Drop the active target if canceled.
+
switchToState(STATE_FINISH, event.getX(), event.getY());
}
@@ -719,24 +754,25 @@ public class MultiWaveView extends View {
int activeTarget = -1;
final int historySize = event.getHistorySize();
+ final boolean singleTarget = mTargetDrawables.size() == 1;
+ float x = 0.0f;
+ float y = 0.0f;
for (int k = 0; k < historySize + 1; k++) {
- float x = k < historySize ? event.getHistoricalX(k) : event.getX();
- float y = k < historySize ? event.getHistoricalY(k) : event.getY();
- float tx = x - mWaveCenterX;
- float ty = y - mWaveCenterY;
+ float eventX = k < historySize ? event.getHistoricalX(k) : event.getX();
+ float eventY = k < historySize ? event.getHistoricalY(k) : event.getY();
+ // tx and ty are relative to wave center
+ float tx = eventX - mWaveCenterX;
+ float ty = eventY - mWaveCenterY;
float touchRadius = (float) Math.sqrt(dist2(tx, ty));
final float scale = touchRadius > mOuterRadius ? mOuterRadius / touchRadius : 1.0f;
- float limitX = mWaveCenterX + tx * scale;
- float limitY = mWaveCenterY + ty * scale;
+ float limitX = tx * scale;
+ float limitY = ty * scale;
- boolean singleTarget = mTargetDrawables.size() == 1;
if (singleTarget) {
// Snap to outer ring if there's only one target
float snapRadius = mOuterRadius - mSnapMargin;
if (touchRadius > snapRadius) {
activeTarget = 0;
- x = limitX;
- y = limitY;
}
} else {
// If there's more than one target, snap to the closest one less than hitRadius away.
@@ -753,34 +789,47 @@ public class MultiWaveView extends View {
best = dist2;
}
}
- x = limitX;
- y = limitY;
- }
- if (activeTarget != -1) {
- switchToState(STATE_SNAP, x,y);
- float newX = singleTarget ? limitX : mTargetDrawables.get(activeTarget).getX();
- float newY = singleTarget ? limitY : mTargetDrawables.get(activeTarget).getY();
- moveHandleTo(newX, newY, false);
- TargetDrawable currentTarget = mTargetDrawables.get(activeTarget);
- if (currentTarget.hasState(TargetDrawable.STATE_FOCUSED)) {
- currentTarget.setState(TargetDrawable.STATE_FOCUSED);
- mHandleDrawable.setAlpha(0.0f);
- }
- } else {
- switchToState(STATE_TRACKING, x, y);
- moveHandleTo(x, y, false);
- mHandleDrawable.setAlpha(1.0f);
}
+ x = limitX;
+ y = limitY;
+ }
+
+ if (activeTarget != -1) {
+ switchToState(STATE_SNAP, x,y);
+ TargetDrawable target = mTargetDrawables.get(activeTarget);
+ float newX = singleTarget ? x : target.getX();
+ float newY = singleTarget ? y : target.getY();
+ moveHandleTo(newX, newY, false);
+ } else {
+ switchToState(STATE_TRACKING, x, y);
+ moveHandleTo(x, y, false);
+ mHandleDrawable.setAlpha(1.0f);
}
// Draw handle outside parent's bounds
invalidateGlobalRegion(mHandleDrawable);
- if (mActiveTarget != activeTarget && activeTarget != -1) {
- dispatchGrabbedEvent(activeTarget);
- if (AccessibilityManager.getInstance(mContext).isEnabled()) {
- String targetContentDescription = getTargetDescription(activeTarget);
- announceText(targetContentDescription);
+ if (mActiveTarget != activeTarget) {
+ // Defocus the old target
+ if (mActiveTarget != -1) {
+ TargetDrawable target = mTargetDrawables.get(mActiveTarget);
+ if (target.hasState(TargetDrawable.STATE_FOCUSED)) {
+ target.setState(TargetDrawable.STATE_INACTIVE);
+ mHandleDrawable.setAlpha(1.0f);
+ }
+ }
+ // Focus the new target
+ if (activeTarget != -1) {
+ TargetDrawable target = mTargetDrawables.get(activeTarget);
+ if (target.hasState(TargetDrawable.STATE_FOCUSED)) {
+ target.setState(TargetDrawable.STATE_FOCUSED);
+ mHandleDrawable.setAlpha(0.0f);
+ }
+ dispatchGrabbedEvent(activeTarget);
+ if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+ String targetContentDescription = getTargetDescription(activeTarget);
+ announceText(targetContentDescription);
+ }
}
}
mActiveTarget = activeTarget;
@@ -831,21 +880,21 @@ public class MultiWaveView extends View {
private boolean trySwitchToFirstTouchState(MotionEvent event) {
final float x = event.getX();
final float y = event.getY();
- final float dx = x - mWaveCenterX;
- final float dy = y - mWaveCenterY;
- if (mAlwaysTrackFinger || dist2(dx,dy) <= getScaledTapRadiusSquared()) {
+ final float tx = x - mWaveCenterX;
+ final float ty = y - mWaveCenterY;
+ if (mAlwaysTrackFinger || dist2(tx,ty) <= getScaledTapRadiusSquared()) {
if (DEBUG) Log.v(TAG, "** Handle HIT");
switchToState(STATE_FIRST_TOUCH, x, y);
- moveHandleTo(x, y, false);
+ moveHandleTo(tx, ty, false);
mDragging = true;
return true;
}
return false;
}
- private void performInitialLayout(float centerX, float centerY) {
+ private void assignDefaultsIfNeeded(float centerX, float centerY) {
if (mOuterRadius == 0.0f) {
- mOuterRadius = 0.5f*(float) Math.sqrt(dist2(centerX, centerY));
+ mOuterRadius = 0.5f*(float) Math.hypot(centerX, centerY);
}
if (mHitRadius == 0.0f) {
// Use the radius of inscribed circle of the first target.
@@ -855,12 +904,9 @@ public class MultiWaveView extends View {
mSnapMargin = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
SNAP_MARGIN_DEFAULT, getContext().getResources().getDisplayMetrics());
}
- hideChevrons();
- hideTargets(false);
- moveHandleTo(centerX, centerY, false);
}
- private void setupGravity(int dx, int dy) {
+ private void computeInsets(int dx, int dy) {
final int layoutDirection = getResolvedLayoutDirection();
final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity, layoutDirection);
@@ -899,29 +945,49 @@ public class MultiWaveView extends View {
+ Math.max(width, mMaxTargetWidth + mOuterRing.getWidth()) / 2;
float newWaveCenterY = mVerticalOffset + mVerticalInset
+ Math.max(height, + mMaxTargetHeight + mOuterRing.getHeight()) / 2;
- if (newWaveCenterX != mWaveCenterX || newWaveCenterY != mWaveCenterY) {
- if (mWaveCenterX == 0 && mWaveCenterY == 0) {
- performInitialLayout(newWaveCenterX, newWaveCenterY);
- }
- mWaveCenterX = newWaveCenterX;
- mWaveCenterY = newWaveCenterY;
- mOuterRing.setX(mWaveCenterX);
- mOuterRing.setY(Math.max(mWaveCenterY, mWaveCenterY));
+ assignDefaultsIfNeeded(newWaveCenterX, newWaveCenterY);
+
+ if (mInitialLayout) {
+ hideChevrons();
+ hideTargets(false);
+ moveHandleTo(0, 0, false);
+ mInitialLayout = false;
}
- updateTargetPositions();
+
+ mOuterRing.setPositionX(newWaveCenterX);
+ mOuterRing.setPositionY(newWaveCenterY);
+
+ mHandleDrawable.setPositionX(newWaveCenterX);
+ mHandleDrawable.setPositionY(newWaveCenterY);
+
+ updateTargetPositions(newWaveCenterX, newWaveCenterY);
+ updateChevronPositions(newWaveCenterX, newWaveCenterY);
+
+ mWaveCenterX = newWaveCenterX;
+ mWaveCenterY = newWaveCenterY;
+
if (DEBUG) dump();
}
- private void updateTargetPositions() {
+ private void updateTargetPositions(float centerX, float centerY) {
// Reposition the target drawables if the view changed.
for (int i = 0; i < mTargetDrawables.size(); i++) {
final TargetDrawable targetIcon = mTargetDrawables.get(i);
double angle = -2.0f * Math.PI * i / mTargetDrawables.size();
- float xPosition = mWaveCenterX + mOuterRadius * (float) Math.cos(angle);
- float yPosition = mWaveCenterY + mOuterRadius * (float) Math.sin(angle);
- targetIcon.setX(xPosition);
- targetIcon.setY(yPosition);
+ targetIcon.setPositionX(centerX);
+ targetIcon.setPositionY(centerY);
+ targetIcon.setX(mOuterRadius * (float) Math.cos(angle));
+ targetIcon.setY(mOuterRadius * (float) Math.sin(angle));
+ }
+ }
+
+ private void updateChevronPositions(float centerX, float centerY) {
+ for (TargetDrawable target : mChevronDrawables) {
+ if (target != null) {
+ target.setPositionX(centerX);
+ target.setPositionY(centerY);
+ }
}
}
diff --git a/core/java/com/android/internal/widget/multiwaveview/TargetDrawable.java b/core/java/com/android/internal/widget/multiwaveview/TargetDrawable.java
index ec2c945..6392093 100644
--- a/core/java/com/android/internal/widget/multiwaveview/TargetDrawable.java
+++ b/core/java/com/android/internal/widget/multiwaveview/TargetDrawable.java
@@ -32,10 +32,13 @@ public class TargetDrawable {
public static final int[] STATE_INACTIVE =
{ android.R.attr.state_enabled, -android.R.attr.state_active };
public static final int[] STATE_FOCUSED =
- { android.R.attr.state_enabled, android.R.attr.state_focused };
+ { android.R.attr.state_enabled, -android.R.attr.state_active,
+ android.R.attr.state_focused };
private float mTranslationX = 0.0f;
private float mTranslationY = 0.0f;
+ private float mPositionX = 0.0f;
+ private float mPositionY = 0.0f;
private float mScaleX = 1.0f;
private float mScaleY = 1.0f;
private float mAlpha = 1.0f;
@@ -82,6 +85,14 @@ public class TargetDrawable {
setState(STATE_INACTIVE);
}
+ public TargetDrawable(TargetDrawable other) {
+ mResourceId = other.mResourceId;
+ // Mutate the drawable so we can animate shared drawable properties.
+ mDrawable = other.mDrawable != null ? other.mDrawable.mutate() : null;
+ resizeDrawables();
+ setState(STATE_INACTIVE);
+ }
+
public void setState(int [] state) {
if (mDrawable instanceof StateListDrawable) {
StateListDrawable d = (StateListDrawable) mDrawable;
@@ -196,6 +207,22 @@ public class TargetDrawable {
return mAlpha;
}
+ public void setPositionX(float x) {
+ mPositionX = x;
+ }
+
+ public void setPositionY(float y) {
+ mPositionY = y;
+ }
+
+ public float getPositionX() {
+ return mPositionX;
+ }
+
+ public float getPositionY() {
+ return mPositionY;
+ }
+
public int getWidth() {
return mDrawable != null ? mDrawable.getIntrinsicWidth() : 0;
}
@@ -209,8 +236,8 @@ public class TargetDrawable {
return;
}
canvas.save(Canvas.MATRIX_SAVE_FLAG);
- canvas.translate(mTranslationX, mTranslationY);
- canvas.scale(mScaleX, mScaleY);
+ canvas.scale(mScaleX, mScaleY, mPositionX, mPositionY);
+ canvas.translate(mTranslationX + mPositionX, mTranslationY + mPositionY);
canvas.translate(-0.5f * getWidth(), -0.5f * getHeight());
mDrawable.setAlpha((int) Math.round(mAlpha * 255f));
mDrawable.draw(canvas);