Merge "Update WakeLock-related docs to mention permissions"
diff --git a/api/current.xml b/api/current.xml
index 41daf5c..b5cb881 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -154794,6 +154794,21 @@
<parameter name="target" type="java.util.List<android.preference.PreferenceActivity.Header>">
</parameter>
</method>
+<method name="onBuildStartFragmentIntent"
+ return="android.content.Intent"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fragmentName" type="java.lang.String">
+</parameter>
+<parameter name="args" type="android.os.Bundle">
+</parameter>
+</method>
<method name="onGetInitialHeader"
return="android.preference.PreferenceActivity.Header"
abstract="false"
@@ -201843,6 +201858,186 @@
</parameter>
</method>
</class>
+<class name="CorrectionSpan"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.text.ParcelableSpan">
+</implements>
+<constructor name="CorrectionSpan"
+ type="android.text.style.CorrectionSpan"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="suggests" type="java.util.List<java.lang.CharSequence>">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</constructor>
+<constructor name="CorrectionSpan"
+ type="android.text.style.CorrectionSpan"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="locale" type="java.util.Locale">
+</parameter>
+<parameter name="suggests" type="java.util.List<java.lang.CharSequence>">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</constructor>
+<constructor name="CorrectionSpan"
+ type="android.text.style.CorrectionSpan"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="locale" type="java.util.Locale">
+</parameter>
+<parameter name="suggests" type="java.util.List<java.lang.CharSequence>">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+<parameter name="originalString" type="java.lang.String">
+</parameter>
+</constructor>
+<constructor name="CorrectionSpan"
+ type="android.text.style.CorrectionSpan"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="src" type="android.os.Parcel">
+</parameter>
+</constructor>
+<method name="describeContents"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getFlags"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getLocale"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getOriginalString"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getSpanTypeId"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getSuggests"
+ return="java.util.List<java.lang.CharSequence>"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="writeToParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="dest" type="android.os.Parcel">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<field name="CREATOR"
+ type="android.os.Parcelable.Creator"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="FLAG_DEFAULT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="FLAG_VERBATIM"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
<class name="DrawableMarginSpan"
extends="java.lang.Object"
abstract="false"
@@ -236544,6 +236739,27 @@
<parameter name="newCursorPosition" type="int">
</parameter>
</method>
+<method name="setCorrectionSpan"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="token" type="android.os.IBinder">
+</parameter>
+<parameter name="correctionSpan" type="android.text.style.CorrectionSpan">
+</parameter>
+<parameter name="start" type="int">
+</parameter>
+<parameter name="end" type="int">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
<method name="setSelection"
return="boolean"
abstract="false"
@@ -237814,6 +238030,27 @@
<parameter name="newCursorPosition" type="int">
</parameter>
</method>
+<method name="setCorrectionSpan"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="token" type="android.os.IBinder">
+</parameter>
+<parameter name="correctionSpan" type="android.text.style.CorrectionSpan">
+</parameter>
+<parameter name="start" type="int">
+</parameter>
+<parameter name="end" type="int">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
<method name="setSelection"
return="boolean"
abstract="true"
@@ -238144,6 +238381,27 @@
<parameter name="newCursorPosition" type="int">
</parameter>
</method>
+<method name="setCorrectionSpan"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="token" type="android.os.IBinder">
+</parameter>
+<parameter name="correctionSpan" type="android.text.style.CorrectionSpan">
+</parameter>
+<parameter name="start" type="int">
+</parameter>
+<parameter name="end" type="int">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
<method name="setSelection"
return="boolean"
abstract="false"
@@ -264470,6 +264728,27 @@
<parameter name="bottom" type="android.graphics.drawable.Drawable">
</parameter>
</method>
+<method name="setCorrectionSpan"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="token" type="android.os.IBinder">
+</parameter>
+<parameter name="span" type="android.text.style.CorrectionSpan">
+</parameter>
+<parameter name="start" type="int">
+</parameter>
+<parameter name="end" type="int">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
<method name="setCursorVisible"
return="void"
abstract="false"
@@ -267130,7 +267409,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="t" type="T">
+<parameter name="arg0" type="T">
</parameter>
</method>
</interface>
diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java
index 8204e3c..ccf642c 100644
--- a/core/java/android/os/Looper.java
+++ b/core/java/android/os/Looper.java
@@ -141,7 +141,8 @@
Log.wtf("Looper", "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
- + msg.target + " " + msg.callback + " what=" + msg.what);
+ + msg.target.getClass().getName() + " "
+ + msg.callback + " what=" + msg.what);
}
msg.recycle();
diff --git a/core/java/android/preference/PreferenceActivity.java b/core/java/android/preference/PreferenceActivity.java
index ad0bc84..db50bfc 100644
--- a/core/java/android/preference/PreferenceActivity.java
+++ b/core/java/android/preference/PreferenceActivity.java
@@ -961,6 +961,27 @@
}
/**
+ * Called by {@link #startWithFragment(String, Bundle, Fragment, int)} when
+ * in single-pane mode, to build an Intent to launch a new activity showing
+ * the selected fragment. The default implementation constructs an Intent
+ * that re-launches the current activity with the appropriate arguments to
+ * display the fragment.
+ *
+ * @param fragmentName The name of the fragment to display.
+ * @param args Optional arguments to supply to the fragment.
+ * @return Returns an Intent that can be launched to display the given
+ * fragment.
+ */
+ public Intent onBuildStartFragmentIntent(String fragmentName, Bundle args) {
+ Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.setClass(this, getClass());
+ intent.putExtra(EXTRA_SHOW_FRAGMENT, fragmentName);
+ intent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
+ intent.putExtra(EXTRA_NO_HEADERS, true);
+ return intent;
+ }
+
+ /**
* Start a new instance of this activity, showing only the given
* preference fragment. When launched in this mode, the header list
* will be hidden and the given preference fragment will be instantiated
@@ -968,14 +989,14 @@
*
* @param fragmentName The name of the fragment to display.
* @param args Optional arguments to supply to the fragment.
+ * @param resultTo Option fragment that should receive the result of
+ * the activity launch.
+ * @param resultRequestCode If resultTo is non-null, this is the request
+ * code in which to report the result.
*/
public void startWithFragment(String fragmentName, Bundle args,
Fragment resultTo, int resultRequestCode) {
- Intent intent = new Intent(Intent.ACTION_MAIN);
- intent.setClass(this, getClass());
- intent.putExtra(EXTRA_SHOW_FRAGMENT, fragmentName);
- intent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
- intent.putExtra(EXTRA_NO_HEADERS, true);
+ Intent intent = onBuildStartFragmentIntent(fragmentName, args);
if (resultTo == null) {
startActivity(intent);
} else {
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index b59421e..bb8d874 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -166,7 +166,6 @@
* If the EXTRA_OUTPUT is present, then the full-sized image will be written to the Uri
* value of EXTRA_OUTPUT.
* @see #EXTRA_OUTPUT
- * @see #EXTRA_VIDEO_QUALITY
*/
public final static String ACTION_IMAGE_CAPTURE = "android.media.action.IMAGE_CAPTURE";
@@ -181,6 +180,9 @@
* written to the standard location for videos, and the Uri of that location will be
* returned in the data field of the Uri.
* @see #EXTRA_OUTPUT
+ * @see #EXTRA_VIDEO_QUALITY
+ * @see #EXTRA_SIZE_LIMIT
+ * @see #EXTRA_DURATION_LIMIT
*/
public final static String ACTION_VIDEO_CAPTURE = "android.media.action.VIDEO_CAPTURE";
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index d2d2557..6ff9f0e 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -90,12 +90,18 @@
public static final String PERSON_ID = "person";
/**
- * The date the message was sent
+ * The date the message was received
* <P>Type: INTEGER (long)</P>
*/
public static final String DATE = "date";
/**
+ * The date the message was sent
+ * <P>Type: INTEGER (long)</P>
+ */
+ public static final String DATE_SENT = "date_sent";
+
+ /**
* Has the message been read
* <P>Type: INTEGER (boolean)</P>
*/
@@ -650,12 +656,18 @@
public static final int MESSAGE_BOX_OUTBOX = 4;
/**
- * The date the message was sent.
+ * The date the message was received.
* <P>Type: INTEGER (long)</P>
*/
public static final String DATE = "date";
/**
+ * The date the message was sent.
+ * <P>Type: INTEGER (long)</P>
+ */
+ public static final String DATE_SENT = "date_sent";
+
+ /**
* The box which the message belong to, for example, MESSAGE_BOX_INBOX.
* <P>Type: INTEGER</P>
*/
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index 30a1f48..ee6342a 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -565,6 +565,8 @@
public static final int TEXT_APPEARANCE_SPAN = 17;
/** @hide */
public static final int ANNOTATION = 18;
+ /** @hide */
+ public static final int CORRECTION_SPAN = 19;
/**
* Flatten a CharSequence and whatever styles can be copied across processes
diff --git a/core/java/android/text/style/CorrectionSpan.aidl b/core/java/android/text/style/CorrectionSpan.aidl
new file mode 100644
index 0000000..82e3d04
--- /dev/null
+++ b/core/java/android/text/style/CorrectionSpan.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text.style;
+
+parcelable CorrectionSpan;
diff --git a/core/java/android/text/style/CorrectionSpan.java b/core/java/android/text/style/CorrectionSpan.java
new file mode 100644
index 0000000..6142e6f
--- /dev/null
+++ b/core/java/android/text/style/CorrectionSpan.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text.style;
+
+import android.content.Context;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.ParcelableSpan;
+import android.text.TextUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+public class CorrectionSpan implements ParcelableSpan {
+
+ /**
+ * Flag for the default value.
+ */
+ public static final int FLAG_DEFAULT = 0x0000;
+ /**
+ * Flag for indicating that the input is verbatim. TextView refers to this flag to determine
+ * how it displays a word with CorrectionSpan.
+ */
+ public static final int FLAG_VERBATIM = 0x0001;
+
+ private static final int SUGGESTS_MAX_SIZE = 5;
+
+ /*
+ * TODO: Needs to check the validity and add a feature that TextView will change
+ * the current IME to the other IME which is specified in CorrectionSpan.
+ * An IME needs to set the span by specifying the target IME and Subtype of CorrectionSpan.
+ * And the current IME might want to specify any IME as the target IME including other IMEs.
+ */
+
+ private final int mFlags;
+ private final List<CharSequence> mSuggests = new ArrayList<CharSequence>();
+ private final String mLocaleString;
+ private final String mOriginalString;
+ /*
+ * TODO: If switching IME is required, needs to add parameters for ids of InputMethodInfo
+ * and InputMethodSubtype.
+ */
+
+ /**
+ * @param context Context for the application
+ * @param suggests Suggests for the string under the span
+ * @param flags Additional flags indicating how this span is handled in TextView
+ */
+ public CorrectionSpan(Context context, List<CharSequence> suggests, int flags) {
+ this(context, null, suggests, flags, null);
+ }
+
+ /**
+ * @param locale Locale of the suggestions
+ * @param suggests Suggests for the string under the span
+ * @param flags Additional flags indicating how this span is handled in TextView
+ */
+ public CorrectionSpan(Locale locale, List<CharSequence> suggests, int flags) {
+ this(null, locale, suggests, flags, null);
+ }
+
+ /**
+ * @param context Context for the application
+ * @param locale locale Locale of the suggestions
+ * @param suggests suggests Suggests for the string under the span
+ * @param flags Additional flags indicating how this span is handled in TextView
+ * @param originalString originalString for suggests
+ */
+ public CorrectionSpan(Context context, Locale locale, List<CharSequence> suggests, int flags,
+ String originalString) {
+ final int N = Math.min(SUGGESTS_MAX_SIZE, suggests.size());
+ for (int i = 0; i < N; ++i) {
+ mSuggests.add(suggests.get(i));
+ }
+ mFlags = flags;
+ if (context != null && locale == null) {
+ mLocaleString = context.getResources().getConfiguration().locale.toString();
+ } else {
+ mLocaleString = locale.toString();
+ }
+ mOriginalString = originalString;
+ }
+
+ public CorrectionSpan(Parcel src) {
+ src.readList(mSuggests, null);
+ mFlags = src.readInt();
+ mLocaleString = src.readString();
+ mOriginalString = src.readString();
+ }
+
+ /**
+ * @return suggestions
+ */
+ public List<CharSequence> getSuggests() {
+ return new ArrayList<CharSequence>(mSuggests);
+ }
+
+ /**
+ * @return locale of suggestions
+ */
+ public String getLocale() {
+ return mLocaleString;
+ }
+
+ /**
+ * @return original string of suggestions
+ */
+ public String getOriginalString() {
+ return mOriginalString;
+ }
+
+ public int getFlags() {
+ return mFlags;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeList(mSuggests);
+ dest.writeInt(mFlags);
+ dest.writeString(mLocaleString);
+ dest.writeString(mOriginalString);
+ }
+
+ @Override
+ public int getSpanTypeId() {
+ return TextUtils.CORRECTION_SPAN;
+ }
+
+ public static final Parcelable.Creator<CorrectionSpan> CREATOR =
+ new Parcelable.Creator<CorrectionSpan>() {
+ @Override
+ public CorrectionSpan createFromParcel(Parcel source) {
+ return new CorrectionSpan(source);
+ }
+
+ @Override
+ public CorrectionSpan[] newArray(int size) {
+ return new CorrectionSpan[size];
+ }
+ };
+}
diff --git a/core/java/android/view/VelocityTracker.java b/core/java/android/view/VelocityTracker.java
index 4ab2881..fccef2b 100644
--- a/core/java/android/view/VelocityTracker.java
+++ b/core/java/android/view/VelocityTracker.java
@@ -16,8 +16,6 @@
package android.view;
-import android.util.Config;
-import android.util.Log;
import android.util.Poolable;
import android.util.Pool;
import android.util.Pools;
@@ -25,24 +23,15 @@
/**
* Helper for tracking the velocity of touch events, for implementing
- * flinging and other such gestures. Use {@link #obtain} to retrieve a
- * new instance of the class when you are going to begin tracking, put
- * the motion events you receive into it with {@link #addMovement(MotionEvent)},
- * and when you want to determine the velocity call
- * {@link #computeCurrentVelocity(int)} and then {@link #getXVelocity()}
- * and {@link #getXVelocity()}.
+ * flinging and other such gestures.
+ *
+ * Use {@link #obtain} to retrieve a new instance of the class when you are going
+ * to begin tracking. Put the motion events you receive into it with
+ * {@link #addMovement(MotionEvent)}. When you want to determine the velocity call
+ * {@link #computeCurrentVelocity(int)} and then call {@link #getXVelocity(int)}
+ * and {@link #getXVelocity(int)} to retrieve the velocity for each pointer id.
*/
public final class VelocityTracker implements Poolable<VelocityTracker> {
- private static final String TAG = "VelocityTracker";
- private static final boolean DEBUG = false;
- private static final boolean localLOGV = DEBUG || Config.LOGV;
-
- private static final int NUM_PAST = 10;
- private static final int MAX_AGE_MILLISECONDS = 200;
-
- private static final int POINTER_POOL_CAPACITY = 20;
- private static final int INVALID_POINTER = -1;
-
private static final Pool<VelocityTracker> sPool = Pools.synchronizedPool(
Pools.finitePool(new PoolableManager<VelocityTracker>() {
public VelocityTracker newInstance() {
@@ -56,31 +45,20 @@
element.clear();
}
}, 2));
-
- private static Pointer sRecycledPointerListHead;
- private static int sRecycledPointerCount;
-
- private static final class Pointer {
- public Pointer next;
-
- public int id;
- public float xVelocity;
- public float yVelocity;
-
- public final float[] pastX = new float[NUM_PAST];
- public final float[] pastY = new float[NUM_PAST];
- public final long[] pastTime = new long[NUM_PAST]; // uses Long.MIN_VALUE as a sentinel
-
- public int generation;
- }
-
- private Pointer mPointerListHead; // sorted by id in increasing order
- private int mLastTouchIndex;
- private int mGeneration;
- private int mActivePointerId;
+ private static final int ACTIVE_POINTER_ID = -1;
+
+ private int mPtr;
private VelocityTracker mNext;
+ private static native int nativeInitialize();
+ private static native void nativeDispose(int ptr);
+ private static native void nativeClear(int ptr);
+ private static native void nativeAddMovement(int ptr, MotionEvent event);
+ private static native void nativeComputeCurrentVelocity(int ptr, int units, float maxVelocity);
+ private static native float nativeGetXVelocity(int ptr, int id);
+ private static native float nativeGetYVelocity(int ptr, int id);
+
/**
* Retrieve a new VelocityTracker object to watch the velocity of a
* motion. Be sure to call {@link #recycle} when done. You should
@@ -116,18 +94,26 @@
}
private VelocityTracker() {
- clear();
+ mPtr = nativeInitialize();
}
-
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ if (mPtr != 0) {
+ nativeDispose(mPtr);
+ mPtr = 0;
+ }
+ } finally {
+ super.finalize();
+ }
+ }
+
/**
* Reset the velocity tracker back to its initial state.
*/
public void clear() {
- releasePointerList(mPointerListHead);
-
- mPointerListHead = null;
- mLastTouchIndex = 0;
- mActivePointerId = INVALID_POINTER;
+ nativeClear(mPtr);
}
/**
@@ -137,110 +123,13 @@
* final {@link MotionEvent#ACTION_UP}. You can, however, call this
* for whichever events you desire.
*
- * @param ev The MotionEvent you received and would like to track.
+ * @param event The MotionEvent you received and would like to track.
*/
- public void addMovement(MotionEvent ev) {
- final int historySize = ev.getHistorySize();
- final int pointerCount = ev.getPointerCount();
- final int lastTouchIndex = mLastTouchIndex;
- final int nextTouchIndex = (lastTouchIndex + 1) % NUM_PAST;
- final int finalTouchIndex = (nextTouchIndex + historySize) % NUM_PAST;
- final int generation = mGeneration++;
-
- mLastTouchIndex = finalTouchIndex;
-
- // Update pointer data.
- Pointer previousPointer = null;
- for (int i = 0; i < pointerCount; i++){
- final int pointerId = ev.getPointerId(i);
-
- // Find the pointer data for this pointer id.
- // This loop is optimized for the common case where pointer ids in the event
- // are in sorted order. However, we check for this case explicitly and
- // perform a full linear scan from the start if needed.
- Pointer nextPointer;
- if (previousPointer == null || pointerId < previousPointer.id) {
- previousPointer = null;
- nextPointer = mPointerListHead;
- } else {
- nextPointer = previousPointer.next;
- }
-
- final Pointer pointer;
- for (;;) {
- if (nextPointer != null) {
- final int nextPointerId = nextPointer.id;
- if (nextPointerId == pointerId) {
- pointer = nextPointer;
- break;
- }
- if (nextPointerId < pointerId) {
- nextPointer = nextPointer.next;
- continue;
- }
- }
-
- // Pointer went down. Add it to the list.
- // Write a sentinel at the end of the pastTime trace so we will be able to
- // tell when the trace started.
- if (mActivePointerId == INVALID_POINTER) {
- // Congratulations! You're the new active pointer!
- mActivePointerId = pointerId;
- }
- pointer = obtainPointer();
- pointer.id = pointerId;
- pointer.pastTime[lastTouchIndex] = Long.MIN_VALUE;
- pointer.next = nextPointer;
- if (previousPointer == null) {
- mPointerListHead = pointer;
- } else {
- previousPointer.next = pointer;
- }
- break;
- }
-
- pointer.generation = generation;
- previousPointer = pointer;
-
- final float[] pastX = pointer.pastX;
- final float[] pastY = pointer.pastY;
- final long[] pastTime = pointer.pastTime;
-
- for (int j = 0; j < historySize; j++) {
- final int touchIndex = (nextTouchIndex + j) % NUM_PAST;
- pastX[touchIndex] = ev.getHistoricalX(i, j);
- pastY[touchIndex] = ev.getHistoricalY(i, j);
- pastTime[touchIndex] = ev.getHistoricalEventTime(j);
- }
- pastX[finalTouchIndex] = ev.getX(i);
- pastY[finalTouchIndex] = ev.getY(i);
- pastTime[finalTouchIndex] = ev.getEventTime();
+ public void addMovement(MotionEvent event) {
+ if (event == null) {
+ throw new IllegalArgumentException("event must not be null");
}
-
- // Find removed pointers.
- previousPointer = null;
- for (Pointer pointer = mPointerListHead; pointer != null; ) {
- final Pointer nextPointer = pointer.next;
- final int pointerId = pointer.id;
- if (pointer.generation != generation) {
- // Pointer went up. Remove it from the list.
- if (previousPointer == null) {
- mPointerListHead = nextPointer;
- } else {
- previousPointer.next = nextPointer;
- }
- releasePointer(pointer);
-
- if (pointerId == mActivePointerId) {
- // Pick a new active pointer. How is arbitrary.
- mActivePointerId = mPointerListHead != null ?
- mPointerListHead.id : INVALID_POINTER;
- }
- } else {
- previousPointer = pointer;
- }
- pointer = nextPointer;
- }
+ nativeAddMovement(mPtr, event);
}
/**
@@ -250,7 +139,7 @@
* @see #computeCurrentVelocity(int, float)
*/
public void computeCurrentVelocity(int units) {
- computeCurrentVelocity(units, Float.MAX_VALUE);
+ nativeComputeCurrentVelocity(mPtr, units, Float.MAX_VALUE);
}
/**
@@ -267,78 +156,7 @@
* must be positive.
*/
public void computeCurrentVelocity(int units, float maxVelocity) {
- final int lastTouchIndex = mLastTouchIndex;
-
- for (Pointer pointer = mPointerListHead; pointer != null; pointer = pointer.next) {
- final long[] pastTime = pointer.pastTime;
-
- // Search backwards in time for oldest acceptable time.
- // Stop at the beginning of the trace as indicated by the sentinel time Long.MIN_VALUE.
- int oldestTouchIndex = lastTouchIndex;
- int numTouches = 1;
- final long minTime = pastTime[lastTouchIndex] - MAX_AGE_MILLISECONDS;
- while (numTouches < NUM_PAST) {
- final int nextOldestTouchIndex = (oldestTouchIndex + NUM_PAST - 1) % NUM_PAST;
- final long nextOldestTime = pastTime[nextOldestTouchIndex];
- if (nextOldestTime < minTime) { // also handles end of trace sentinel
- break;
- }
- oldestTouchIndex = nextOldestTouchIndex;
- numTouches += 1;
- }
-
- // If we have a lot of samples, skip the last received sample since it is
- // probably pretty noisy compared to the sum of all of the traces already acquired.
- if (numTouches > 3) {
- numTouches -= 1;
- }
-
- // Kind-of stupid.
- final float[] pastX = pointer.pastX;
- final float[] pastY = pointer.pastY;
-
- final float oldestX = pastX[oldestTouchIndex];
- final float oldestY = pastY[oldestTouchIndex];
- final long oldestTime = pastTime[oldestTouchIndex];
-
- float accumX = 0;
- float accumY = 0;
-
- for (int i = 1; i < numTouches; i++) {
- final int touchIndex = (oldestTouchIndex + i) % NUM_PAST;
- final int duration = (int)(pastTime[touchIndex] - oldestTime);
-
- if (duration == 0) continue;
-
- float delta = pastX[touchIndex] - oldestX;
- float velocity = (delta / duration) * units; // pixels/frame.
- accumX = (accumX == 0) ? velocity : (accumX + velocity) * .5f;
-
- delta = pastY[touchIndex] - oldestY;
- velocity = (delta / duration) * units; // pixels/frame.
- accumY = (accumY == 0) ? velocity : (accumY + velocity) * .5f;
- }
-
- if (accumX < -maxVelocity) {
- accumX = - maxVelocity;
- } else if (accumX > maxVelocity) {
- accumX = maxVelocity;
- }
-
- if (accumY < -maxVelocity) {
- accumY = - maxVelocity;
- } else if (accumY > maxVelocity) {
- accumY = maxVelocity;
- }
-
- pointer.xVelocity = accumX;
- pointer.yVelocity = accumY;
-
- if (localLOGV) {
- Log.v(TAG, "Pointer " + pointer.id
- + ": Y velocity=" + accumX +" X velocity=" + accumY + " N=" + numTouches);
- }
- }
+ nativeComputeCurrentVelocity(mPtr, units, maxVelocity);
}
/**
@@ -348,8 +166,7 @@
* @return The previously computed X velocity.
*/
public float getXVelocity() {
- Pointer pointer = getPointer(mActivePointerId);
- return pointer != null ? pointer.xVelocity : 0;
+ return nativeGetXVelocity(mPtr, ACTIVE_POINTER_ID);
}
/**
@@ -359,8 +176,7 @@
* @return The previously computed Y velocity.
*/
public float getYVelocity() {
- Pointer pointer = getPointer(mActivePointerId);
- return pointer != null ? pointer.yVelocity : 0;
+ return nativeGetYVelocity(mPtr, ACTIVE_POINTER_ID);
}
/**
@@ -371,8 +187,7 @@
* @return The previously computed X velocity.
*/
public float getXVelocity(int id) {
- Pointer pointer = getPointer(id);
- return pointer != null ? pointer.xVelocity : 0;
+ return nativeGetXVelocity(mPtr, id);
}
/**
@@ -383,68 +198,6 @@
* @return The previously computed Y velocity.
*/
public float getYVelocity(int id) {
- Pointer pointer = getPointer(id);
- return pointer != null ? pointer.yVelocity : 0;
- }
-
- private Pointer getPointer(int id) {
- for (Pointer pointer = mPointerListHead; pointer != null; pointer = pointer.next) {
- if (pointer.id == id) {
- return pointer;
- }
- }
- return null;
- }
-
- private static Pointer obtainPointer() {
- synchronized (sPool) {
- if (sRecycledPointerCount != 0) {
- Pointer element = sRecycledPointerListHead;
- sRecycledPointerCount -= 1;
- sRecycledPointerListHead = element.next;
- element.next = null;
- return element;
- }
- }
- return new Pointer();
- }
-
- private static void releasePointer(Pointer pointer) {
- synchronized (sPool) {
- if (sRecycledPointerCount < POINTER_POOL_CAPACITY) {
- pointer.next = sRecycledPointerListHead;
- sRecycledPointerCount += 1;
- sRecycledPointerListHead = pointer;
- }
- }
- }
-
- private static void releasePointerList(Pointer pointer) {
- if (pointer != null) {
- synchronized (sPool) {
- int count = sRecycledPointerCount;
- if (count >= POINTER_POOL_CAPACITY) {
- return;
- }
-
- Pointer tail = pointer;
- for (;;) {
- count += 1;
- if (count >= POINTER_POOL_CAPACITY) {
- break;
- }
-
- Pointer next = tail.next;
- if (next == null) {
- break;
- }
- tail = next;
- }
-
- tail.next = sRecycledPointerListHead;
- sRecycledPointerCount = count;
- sRecycledPointerListHead = pointer;
- }
- }
+ return nativeGetYVelocity(mPtr, id);
}
}
diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java
index e644045..b95e7c9 100644
--- a/core/java/android/view/inputmethod/BaseInputConnection.java
+++ b/core/java/android/view/inputmethod/BaseInputConnection.java
@@ -20,6 +20,7 @@
import android.content.res.TypedArray;
import android.os.Bundle;
import android.os.Handler;
+import android.os.IBinder;
import android.os.SystemClock;
import android.text.Editable;
import android.text.NoCopySpan;
@@ -29,6 +30,7 @@
import android.text.Spanned;
import android.text.TextUtils;
import android.text.method.MetaKeyKeyListener;
+import android.text.style.CorrectionSpan;
import android.util.Log;
import android.util.LogPrinter;
import android.view.KeyCharacterMap;
@@ -190,6 +192,15 @@
}
/**
+ * Default implementation does nothing and returns false.
+ */
+ @Override
+ public boolean setCorrectionSpan(IBinder token, CorrectionSpan correctionSpan, int start,
+ int end, int flags) {
+ return false;
+ }
+
+ /**
* The default implementation performs the deletion around the current
* selection position of the editable text.
*/
diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java
index ea9e402..a8a5346 100644
--- a/core/java/android/view/inputmethod/InputConnection.java
+++ b/core/java/android/view/inputmethod/InputConnection.java
@@ -17,6 +17,8 @@
package android.view.inputmethod;
import android.os.Bundle;
+import android.os.IBinder;
+import android.text.style.CorrectionSpan;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
@@ -353,4 +355,10 @@
* valid.
*/
public boolean performPrivateCommand(String action, Bundle data);
+
+ /**
+ * Add a correction span.
+ */
+ public boolean setCorrectionSpan(IBinder token, CorrectionSpan correctionSpan, int start,
+ int end, int flags);
}
diff --git a/core/java/android/view/inputmethod/InputConnectionWrapper.java b/core/java/android/view/inputmethod/InputConnectionWrapper.java
index 4d9d51e..fee88d97 100644
--- a/core/java/android/view/inputmethod/InputConnectionWrapper.java
+++ b/core/java/android/view/inputmethod/InputConnectionWrapper.java
@@ -17,6 +17,8 @@
package android.view.inputmethod;
import android.os.Bundle;
+import android.os.IBinder;
+import android.text.style.CorrectionSpan;
import android.view.KeyEvent;
/**
@@ -126,4 +128,9 @@
public boolean performPrivateCommand(String action, Bundle data) {
return mTarget.performPrivateCommand(action, data);
}
+
+ public boolean setCorrectionSpan(IBinder token, CorrectionSpan correctionSpan, int start,
+ int end, int flags) {
+ return mTarget.setCorrectionSpan(token, correctionSpan, start, end, flags);
+ }
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index a3ea6a9..82022fb 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -43,6 +43,7 @@
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Message;
import android.os.Parcel;
import android.os.Parcelable;
@@ -81,6 +82,7 @@
import android.text.method.TimeKeyListener;
import android.text.method.TransformationMethod;
import android.text.style.ClickableSpan;
+import android.text.style.CorrectionSpan;
import android.text.style.ParagraphStyle;
import android.text.style.URLSpan;
import android.text.style.UpdateAppearance;
@@ -126,6 +128,7 @@
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.HashSet;
/**
* Displays text to the user and optionally allows them to edit it. A TextView
@@ -8339,6 +8342,19 @@
sLastCutOrCopyTime = SystemClock.uptimeMillis();
}
+ public boolean setCorrectionSpan(IBinder token, CorrectionSpan span, int start, int end,
+ int flags) {
+ if (getWindowToken() != token || !(mText instanceof Spannable)) return false;
+ Spannable spannable = (Spannable)mText;
+ CorrectionSpan[] spans = spannable.getSpans(start, end, CorrectionSpan.class);
+ final int N = spans.length;
+ for (int i = 0; i < N; ++i) {
+ spannable.removeSpan(spans[i]);
+ }
+ spannable.setSpan(span, start, end, flags);
+ return true;
+ }
+
/**
* An ActionMode Callback class that is used to provide actions while in text selection mode.
*
diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java
index b5df812..8719fde 100644
--- a/core/java/com/android/internal/view/IInputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java
@@ -18,9 +18,11 @@
import android.os.Bundle;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
+import android.text.style.CorrectionSpan;
import android.util.Log;
import android.view.KeyEvent;
import android.view.inputmethod.CompletionInfo;
@@ -45,6 +47,7 @@
private static final int DO_PERFORM_EDITOR_ACTION = 58;
private static final int DO_PERFORM_CONTEXT_MENU_ACTION = 59;
private static final int DO_SET_COMPOSING_TEXT = 60;
+ private static final int DO_SET_SECURE_CORRECTION_SPAN = 61;
private static final int DO_SET_COMPOSING_REGION = 63;
private static final int DO_FINISH_COMPOSING_TEXT = 65;
private static final int DO_SEND_KEY_EVENT = 70;
@@ -174,7 +177,14 @@
public void performPrivateCommand(String action, Bundle data) {
dispatchMessage(obtainMessageOO(DO_PERFORM_PRIVATE_COMMAND, action, data));
}
-
+
+ @Override
+ public void setCorrectionSpan(IBinder token, CorrectionSpan correctionSpan, int start,
+ int end, int flags) {
+ dispatchMessage(obtainMessageOOIII(DO_SET_SECURE_CORRECTION_SPAN, token, correctionSpan,
+ start, end, flags));
+ }
+
void dispatchMessage(Message msg) {
// If we are calling this from the main thread, then we can call
// right through. Otherwise, we need to send the message to the
@@ -420,6 +430,17 @@
(Bundle)args.arg2);
return;
}
+ case DO_SET_SECURE_CORRECTION_SPAN: {
+ InputConnection ic = mInputConnection.get();
+ if (ic == null || !isActive()) {
+ Log.w(TAG, "setCorrectionSpan on inactive InputConnection");
+ return;
+ }
+ SomeArgs args = (SomeArgs)msg.obj;
+ ic.setCorrectionSpan((IBinder)args.arg1, (CorrectionSpan)args.arg2, msg.arg1,
+ msg.arg2, args.seq);
+ return;
+ }
}
Log.w(TAG, "Unhandled message code: " + msg.what);
}
@@ -469,4 +490,12 @@
args.arg2 = arg2;
return mH.obtainMessage(what, 0, 0, args);
}
+
+ Message obtainMessageOOIII(int what, Object arg1, Object arg2, int arg3, int arg4, int arg5) {
+ SomeArgs args = new SomeArgs();
+ args.arg1 = arg1;
+ args.arg2 = arg2;
+ args.seq = arg5;
+ return mH.obtainMessage(what, arg3, arg4, args);
+ }
}
diff --git a/core/java/com/android/internal/view/IInputContext.aidl b/core/java/com/android/internal/view/IInputContext.aidl
index e00dd4e..eb20d61 100644
--- a/core/java/com/android/internal/view/IInputContext.aidl
+++ b/core/java/com/android/internal/view/IInputContext.aidl
@@ -17,6 +17,7 @@
package com.android.internal.view;
import android.os.Bundle;
+import android.text.style.CorrectionSpan;
import android.view.KeyEvent;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.CorrectionInfo;
@@ -72,4 +73,7 @@
void setComposingRegion(int start, int end);
void getSelectedText(int flags, int seq, IInputContextCallback callback);
+
+ void setCorrectionSpan(in IBinder token, in CorrectionSpan correctionSpan, int start,
+ int end, int flags);
}
diff --git a/core/java/com/android/internal/view/InputConnectionWrapper.java b/core/java/com/android/internal/view/InputConnectionWrapper.java
index b13118a..efe315f 100644
--- a/core/java/com/android/internal/view/InputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/InputConnectionWrapper.java
@@ -17,8 +17,10 @@
package com.android.internal.view;
import android.os.Bundle;
+import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.text.style.CorrectionSpan;
import android.util.Log;
import android.view.KeyEvent;
import android.view.inputmethod.CompletionInfo;
@@ -413,4 +415,14 @@
return false;
}
}
+ @Override
+ public boolean setCorrectionSpan(IBinder token, CorrectionSpan correctionSpan, int start,
+ int end, int flags) {
+ try {
+ mIInputContext.setCorrectionSpan(token, correctionSpan, start, end, flags);
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
}
diff --git a/core/java/com/android/internal/widget/EditableInputConnection.java b/core/java/com/android/internal/widget/EditableInputConnection.java
index 9f9f020..ea82bc7 100644
--- a/core/java/com/android/internal/widget/EditableInputConnection.java
+++ b/core/java/com/android/internal/widget/EditableInputConnection.java
@@ -17,8 +17,10 @@
package com.android.internal.widget;
import android.os.Bundle;
+import android.os.IBinder;
import android.text.Editable;
import android.text.method.KeyListener;
+import android.text.style.CorrectionSpan;
import android.util.Log;
import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.CompletionInfo;
@@ -144,4 +146,13 @@
return success;
}
+
+ @Override
+ public boolean setCorrectionSpan(IBinder token, CorrectionSpan correctionSpan, int start,
+ int end, int flags) {
+ mTextView.beginBatchEdit();
+ boolean retval = mTextView.setCorrectionSpan(token, correctionSpan, start, end, flags);
+ mTextView.endBatchEdit();
+ return retval;
+ }
}
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 52fc930..b4a0e4f 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -54,6 +54,7 @@
android_view_KeyCharacterMap.cpp \
android_view_GLES20Canvas.cpp \
android_view_MotionEvent.cpp \
+ android_view_VelocityTracker.cpp \
android_text_AndroidCharacter.cpp \
android_text_AndroidBidi.cpp \
android_os_Debug.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 0e071a4..e4eb692 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -170,6 +170,7 @@
extern int register_android_view_InputQueue(JNIEnv* env);
extern int register_android_view_KeyEvent(JNIEnv* env);
extern int register_android_view_MotionEvent(JNIEnv* env);
+extern int register_android_view_VelocityTracker(JNIEnv* env);
extern int register_android_content_res_ObbScanner(JNIEnv* env);
extern int register_android_content_res_Configuration(JNIEnv* env);
extern int register_android_animation_PropertyValuesHolder(JNIEnv *env);
@@ -1302,6 +1303,7 @@
REG_JNI(register_android_view_InputQueue),
REG_JNI(register_android_view_KeyEvent),
REG_JNI(register_android_view_MotionEvent),
+ REG_JNI(register_android_view_VelocityTracker),
REG_JNI(register_android_content_res_ObbScanner),
REG_JNI(register_android_content_res_Configuration),
diff --git a/core/jni/android_view_InputQueue.cpp b/core/jni/android_view_InputQueue.cpp
index b5a5d2e..80c4871 100644
--- a/core/jni/android_view_InputQueue.cpp
+++ b/core/jni/android_view_InputQueue.cpp
@@ -380,7 +380,7 @@
#if DEBUG_DISPATCH_CYCLE
LOGD("channel '%s' ~ Received motion event.", connection->getInputChannelName());
#endif
- inputEventObj = android_view_MotionEvent_fromNative(env,
+ inputEventObj = android_view_MotionEvent_obtainAsCopy(env,
static_cast<MotionEvent*>(inputEvent));
dispatchMethodId = gInputQueueClassInfo.dispatchMotionEvent;
break;
diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp
index 68ebfe7..4ce471e 100644
--- a/core/jni/android_view_MotionEvent.cpp
+++ b/core/jni/android_view_MotionEvent.cpp
@@ -57,7 +57,10 @@
// ----------------------------------------------------------------------------
-static MotionEvent* android_view_MotionEvent_getNativePtr(JNIEnv* env, jobject eventObj) {
+MotionEvent* android_view_MotionEvent_getNativePtr(JNIEnv* env, jobject eventObj) {
+ if (!eventObj) {
+ return NULL;
+ }
return reinterpret_cast<MotionEvent*>(
env->GetIntField(eventObj, gMotionEventClassInfo.mNativePtr));
}
@@ -68,10 +71,10 @@
reinterpret_cast<int>(event));
}
-jobject android_view_MotionEvent_fromNative(JNIEnv* env, const MotionEvent* event) {
+jobject android_view_MotionEvent_obtainAsCopy(JNIEnv* env, const MotionEvent* event) {
jobject eventObj = env->CallStaticObjectMethod(gMotionEventClassInfo.clazz,
gMotionEventClassInfo.obtain);
- if (env->ExceptionCheck()) {
+ if (env->ExceptionCheck() || !eventObj) {
LOGE("An exception occurred while obtaining a motion event.");
LOGE_EX(env);
env->ExceptionClear();
@@ -88,18 +91,6 @@
return eventObj;
}
-status_t android_view_MotionEvent_toNative(JNIEnv* env, jobject eventObj,
- MotionEvent* event) {
- MotionEvent* srcEvent = android_view_MotionEvent_getNativePtr(env, eventObj);
- if (!srcEvent) {
- LOGE("MotionEvent was finalized");
- return BAD_VALUE;
- }
-
- event->copyFrom(srcEvent, true);
- return OK;
-}
-
status_t android_view_MotionEvent_recycle(JNIEnv* env, jobject eventObj) {
env->CallVoidMethod(eventObj, gMotionEventClassInfo.recycle);
if (env->ExceptionCheck()) {
@@ -500,13 +491,7 @@
static jint android_view_MotionEvent_nativeFindPointerIndex(JNIEnv* env, jclass clazz,
jint nativePtr, jint pointerId) {
MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- size_t pointerCount = event->getPointerCount();
- for (size_t i = 0; i < pointerCount; i++) {
- if (event->getPointerId(i) == pointerId) {
- return i;
- }
- }
- return -1;
+ return jint(event->findPointerIndex(pointerId));
}
static jint android_view_MotionEvent_nativeGetHistorySize(JNIEnv* env, jclass clazz,
diff --git a/core/jni/android_view_MotionEvent.h b/core/jni/android_view_MotionEvent.h
index 80dc861..0cf1fb2 100644
--- a/core/jni/android_view_MotionEvent.h
+++ b/core/jni/android_view_MotionEvent.h
@@ -26,12 +26,11 @@
/* Obtains an instance of a DVM MotionEvent object as a copy of a native MotionEvent instance.
* Returns NULL on error. */
-extern jobject android_view_MotionEvent_fromNative(JNIEnv* env, const MotionEvent* event);
+extern jobject android_view_MotionEvent_obtainAsCopy(JNIEnv* env, const MotionEvent* event);
-/* Copies the contents of a DVM MotionEvent object to a native MotionEvent instance.
- * Returns non-zero on error. */
-extern status_t android_view_MotionEvent_toNative(JNIEnv* env, jobject eventObj,
- MotionEvent* event);
+/* Gets the underlying native MotionEvent instance within a DVM MotionEvent object.
+ * Returns NULL if the event is NULL or if it is uninitialized. */
+extern MotionEvent* android_view_MotionEvent_getNativePtr(JNIEnv* env, jobject eventObj);
/* Recycles a DVM MotionEvent object.
* Returns non-zero on error. */
diff --git a/core/jni/android_view_VelocityTracker.cpp b/core/jni/android_view_VelocityTracker.cpp
new file mode 100644
index 0000000..daa0adc
--- /dev/null
+++ b/core/jni/android_view_VelocityTracker.cpp
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "VelocityTracker-JNI"
+
+#include "JNIHelp.h"
+
+#include <android_runtime/AndroidRuntime.h>
+#include <utils/Log.h>
+#include <ui/Input.h>
+#include "android_view_MotionEvent.h"
+
+
+namespace android {
+
+// Special constant to request the velocity of the active pointer.
+static const int ACTIVE_POINTER_ID = -1;
+
+// --- VelocityTrackerState ---
+
+class VelocityTrackerState {
+public:
+ VelocityTrackerState();
+
+ void clear();
+ void addMovement(const MotionEvent* event);
+ void computeCurrentVelocity(int32_t units, float maxVelocity);
+ void getVelocity(int32_t id, float* outVx, float* outVy);
+
+private:
+ struct Velocity {
+ float vx, vy;
+ };
+
+ VelocityTracker mVelocityTracker;
+ int32_t mActivePointerId;
+ BitSet32 mCalculatedIdBits;
+ Velocity mCalculatedVelocity[MAX_POINTERS];
+};
+
+VelocityTrackerState::VelocityTrackerState() : mActivePointerId(-1) {
+}
+
+void VelocityTrackerState::clear() {
+ mVelocityTracker.clear();
+ mActivePointerId = -1;
+ mCalculatedIdBits.clear();
+}
+
+void VelocityTrackerState::addMovement(const MotionEvent* event) {
+ mVelocityTracker.addMovement(event);
+}
+
+void VelocityTrackerState::computeCurrentVelocity(int32_t units, float maxVelocity) {
+ BitSet32 idBits(mVelocityTracker.getCurrentPointerIdBits());
+ mCalculatedIdBits = idBits;
+
+ for (uint32_t index = 0; !idBits.isEmpty(); index++) {
+ uint32_t id = idBits.firstMarkedBit();
+ idBits.clearBit(id);
+
+ float vx, vy;
+ mVelocityTracker.getVelocity(id, &vx, &vy);
+
+ vx = vx * units / 1000;
+ vy = vy * units / 1000;
+
+ if (vx > maxVelocity) {
+ vx = maxVelocity;
+ } else if (vx < -maxVelocity) {
+ vx = -maxVelocity;
+ }
+ if (vy > maxVelocity) {
+ vy = maxVelocity;
+ } else if (vy < -maxVelocity) {
+ vy = -maxVelocity;
+ }
+
+ Velocity& velocity = mCalculatedVelocity[index];
+ velocity.vx = vx;
+ velocity.vy = vy;
+ }
+}
+
+void VelocityTrackerState::getVelocity(int32_t id, float* outVx, float* outVy) {
+ if (id == ACTIVE_POINTER_ID) {
+ id = mVelocityTracker.getActivePointerId();
+ }
+
+ float vx, vy;
+ if (id >= 0 && id <= MAX_POINTER_ID && mCalculatedIdBits.hasBit(id)) {
+ uint32_t index = mCalculatedIdBits.getIndexOfBit(id);
+ const Velocity& velocity = mCalculatedVelocity[index];
+ vx = velocity.vx;
+ vy = velocity.vy;
+ } else {
+ vx = 0;
+ vy = 0;
+ }
+
+ if (outVx) {
+ *outVx = vx;
+ }
+ if (outVy) {
+ *outVy = vy;
+ }
+}
+
+
+// --- JNI Methods ---
+
+static jint android_view_VelocityTracker_nativeInitialize(JNIEnv* env, jclass clazz) {
+ return reinterpret_cast<jint>(new VelocityTrackerState());
+}
+
+static void android_view_VelocityTracker_nativeDispose(JNIEnv* env, jclass clazz, jint ptr) {
+ VelocityTrackerState* state = reinterpret_cast<VelocityTrackerState*>(ptr);
+ delete state;
+}
+
+static void android_view_VelocityTracker_nativeClear(JNIEnv* env, jclass clazz, jint ptr) {
+ VelocityTrackerState* state = reinterpret_cast<VelocityTrackerState*>(ptr);
+ state->clear();
+}
+
+static void android_view_VelocityTracker_nativeAddMovement(JNIEnv* env, jclass clazz, jint ptr,
+ jobject eventObj) {
+ const MotionEvent* event = android_view_MotionEvent_getNativePtr(env, eventObj);
+ if (!event) {
+ LOGW("nativeAddMovement failed because MotionEvent was finalized.");
+ return;
+ }
+
+ VelocityTrackerState* state = reinterpret_cast<VelocityTrackerState*>(ptr);
+ state->addMovement(event);
+}
+
+static void android_view_VelocityTracker_nativeComputeCurrentVelocity(JNIEnv* env, jclass clazz,
+ jint ptr, jint units, jfloat maxVelocity) {
+ VelocityTrackerState* state = reinterpret_cast<VelocityTrackerState*>(ptr);
+ state->computeCurrentVelocity(units, maxVelocity);
+}
+
+static jfloat android_view_VelocityTracker_nativeGetXVelocity(JNIEnv* env, jclass clazz,
+ jint ptr, jint id) {
+ VelocityTrackerState* state = reinterpret_cast<VelocityTrackerState*>(ptr);
+ float vx;
+ state->getVelocity(id, &vx, NULL);
+ return vx;
+}
+
+static jfloat android_view_VelocityTracker_nativeGetYVelocity(JNIEnv* env, jclass clazz,
+ jint ptr, jint id) {
+ VelocityTrackerState* state = reinterpret_cast<VelocityTrackerState*>(ptr);
+ float vy;
+ state->getVelocity(id, NULL, &vy);
+ return vy;
+}
+
+
+// --- JNI Registration ---
+
+static JNINativeMethod gVelocityTrackerMethods[] = {
+ /* name, signature, funcPtr */
+ { "nativeInitialize",
+ "()I",
+ (void*)android_view_VelocityTracker_nativeInitialize },
+ { "nativeDispose",
+ "(I)V",
+ (void*)android_view_VelocityTracker_nativeDispose },
+ { "nativeClear",
+ "(I)V",
+ (void*)android_view_VelocityTracker_nativeClear },
+ { "nativeAddMovement",
+ "(ILandroid/view/MotionEvent;)V",
+ (void*)android_view_VelocityTracker_nativeAddMovement },
+ { "nativeComputeCurrentVelocity",
+ "(IIF)V",
+ (void*)android_view_VelocityTracker_nativeComputeCurrentVelocity },
+ { "nativeGetXVelocity",
+ "(II)F",
+ (void*)android_view_VelocityTracker_nativeGetXVelocity },
+ { "nativeGetYVelocity",
+ "(II)F",
+ (void*)android_view_VelocityTracker_nativeGetYVelocity },
+};
+
+int register_android_view_VelocityTracker(JNIEnv* env) {
+ int res = jniRegisterNativeMethods(env, "android/view/VelocityTracker",
+ gVelocityTrackerMethods, NELEM(gVelocityTrackerMethods));
+ LOG_FATAL_IF(res < 0, "Unable to register native methods.");
+ return 0;
+}
+
+} // namespace android
diff --git a/core/res/res/drawable-hdpi/stat_sys_adb.png b/core/res/res/drawable-hdpi/stat_sys_adb.png
index 877e731..9c56e24 100755
--- a/core/res/res/drawable-hdpi/stat_sys_adb.png
+++ b/core/res/res/drawable-hdpi/stat_sys_adb.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_adb.png b/core/res/res/drawable-mdpi/stat_sys_adb.png
index e259ca5..1400bb3 100644
--- a/core/res/res/drawable-mdpi/stat_sys_adb.png
+++ b/core/res/res/drawable-mdpi/stat_sys_adb.png
Binary files differ
diff --git a/core/res/res/drawable/expander_group_holo_dark.xml b/core/res/res/drawable/expander_group_holo_dark.xml
index 51a7290..2481dcb 100644
--- a/core/res/res/drawable/expander_group_holo_dark.xml
+++ b/core/res/res/drawable/expander_group_holo_dark.xml
@@ -17,7 +17,7 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_expanded="true"
- android:drawable="@drawable/expander_open_holo_dark" />
- <item
android:drawable="@drawable/expander_close_holo_dark" />
+ <item
+ android:drawable="@drawable/expander_open_holo_dark" />
</selector>
diff --git a/core/res/res/drawable/expander_group_holo_light.xml b/core/res/res/drawable/expander_group_holo_light.xml
index 0ce71a5..8006574 100644
--- a/core/res/res/drawable/expander_group_holo_light.xml
+++ b/core/res/res/drawable/expander_group_holo_light.xml
@@ -17,7 +17,7 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_expanded="true"
- android:drawable="@drawable/expander_open_holo_light" />
- <item
android:drawable="@drawable/expander_close_holo_light" />
+ <item
+ android:drawable="@drawable/expander_open_holo_light" />
</selector>
diff --git a/graphics/java/android/renderscript/Matrix2f.java b/graphics/java/android/renderscript/Matrix2f.java
index 78ff97b..acc5bd8 100644
--- a/graphics/java/android/renderscript/Matrix2f.java
+++ b/graphics/java/android/renderscript/Matrix2f.java
@@ -42,7 +42,7 @@
* floats long
*/
public Matrix2f(float[] dataArray) {
- mMat = new float[2];
+ mMat = new float[4];
System.arraycopy(dataArray, 0, mMat, 0, mMat.length);
}
diff --git a/include/ui/Input.h b/include/ui/Input.h
index e9cacf4..8e8b61b 100644
--- a/include/ui/Input.h
+++ b/include/ui/Input.h
@@ -311,6 +311,13 @@
inline int32_t getAction() const { return mAction; }
+ inline int32_t getActionMasked() const { return mAction & AMOTION_EVENT_ACTION_MASK; }
+
+ inline int32_t getActionIndex() const {
+ return (mAction & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK)
+ >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
+ }
+
inline void setAction(int32_t action) { mAction = action; }
inline int32_t getFlags() const { return mFlags; }
@@ -458,6 +465,8 @@
AMOTION_EVENT_AXIS_ORIENTATION, pointerIndex, historicalIndex);
}
+ ssize_t findPointerIndex(int32_t pointerId) const;
+
void initialize(
int32_t deviceId,
int32_t source,
@@ -551,8 +560,7 @@
};
/*
- * Calculates the velocity of pointer motions over time.
- * Uses essentially the same algorithm as android.view.VelocityTracker.
+ * Calculates the velocity of pointer movements over time.
*/
class VelocityTracker {
public:
@@ -565,6 +573,11 @@
// Resets the velocity tracker state.
void clear();
+ // Resets the velocity tracker state for specific pointers.
+ // Call this method when some pointers have changed and may be reusing
+ // an id that was assigned to a different pointer earlier.
+ void clearPointers(BitSet32 idBits);
+
// Adds movement information for a set of pointers.
// The idBits bitfield specifies the pointer ids of the pointers whose positions
// are included in the movement.
@@ -572,11 +585,20 @@
// increasing id. Its size should be equal to the number of one bits in idBits.
void addMovement(nsecs_t eventTime, BitSet32 idBits, const Position* positions);
+ // Adds movement information for all pointers in a MotionEvent, including historical samples.
+ void addMovement(const MotionEvent* event);
+
// Gets the velocity of the specified pointer id in position units per second.
// Returns false and sets the velocity components to zero if there is no movement
// information for the pointer.
bool getVelocity(uint32_t id, float* outVx, float* outVy) const;
+ // Gets the active pointer id, or -1 if none.
+ inline int32_t getActivePointerId() const { return mActivePointerId; }
+
+ // Gets a bitset containing all pointer ids from the most recent movement.
+ inline BitSet32 getCurrentPointerIdBits() const { return mMovements[mIndex].idBits; }
+
private:
// Number of samples to keep.
static const uint32_t HISTORY_SIZE = 10;
@@ -585,7 +607,7 @@
static const nsecs_t MAX_AGE = 200 * 1000000; // 200 ms
// The minimum duration between samples when estimating velocity.
- static const nsecs_t MIN_DURATION = 5 * 1000000; // 5 ms
+ static const nsecs_t MIN_DURATION = 10 * 1000000; // 10 ms
struct Movement {
nsecs_t eventTime;
@@ -595,6 +617,7 @@
uint32_t mIndex;
Movement mMovements[HISTORY_SIZE];
+ int32_t mActivePointerId;
};
/*
diff --git a/include/utils/BitSet.h b/include/utils/BitSet.h
index f03825a..de748b5 100644
--- a/include/utils/BitSet.h
+++ b/include/utils/BitSet.h
@@ -61,6 +61,10 @@
// Result is undefined if all bits are marked.
inline uint32_t firstUnmarkedBit() const { return __builtin_clz(~ value); }
+ // Finds the last marked bit in the set.
+ // Result is undefined if all bits are unmarked.
+ inline uint32_t lastMarkedBit() const { return 31 - __builtin_ctz(value); }
+
// Gets the index of the specified bit in the set, which is the number of
// marked bits that appear before the specified bit.
inline uint32_t getIndexOfBit(uint32_t n) const {
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index b1eb164..f92e20b 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -92,7 +92,11 @@
void LayerRenderer::generateMesh() {
#if RENDER_LAYERS_AS_REGIONS
+#if RENDER_LAYERS_RECT_AS_RECT
if (mLayer->region.isRect() || mLayer->region.isEmpty()) {
+#else
+ if (mLayer->region.isEmpty()) {
+#endif
if (mLayer->mesh) {
delete mLayer->mesh;
delete mLayer->meshIndices;
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index b8bd7d6..e01e072 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -636,11 +636,13 @@
void OpenGLRenderer::composeLayerRegion(Layer* layer, const Rect& rect) {
#if RENDER_LAYERS_AS_REGIONS
+#if RENDER_LAYERS_RECT_AS_RECT
if (layer->region.isRect()) {
composeLayerRect(layer, rect);
layer->region.clear();
return;
}
+#endif
if (!layer->region.isEmpty()) {
size_t count;
@@ -1646,10 +1648,14 @@
#if RENDER_LAYERS_AS_REGIONS
if (!layer->region.isEmpty()) {
+#if RENDER_LAYERS_RECT_AS_RECT
if (layer->region.isRect()) {
const Rect r(x, y, x + layer->layer.getWidth(), y + layer->layer.getHeight());
composeLayerRect(layer, r);
} else if (layer->mesh) {
+#else
+ if (layer->mesh) {
+#endif
const float a = alpha / 255.0f;
const Rect& rect = layer->layer;
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 2d8b6f3..1aef99b 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -27,6 +27,8 @@
// If turned on, layers drawn inside FBOs are optimized with regions
#define RENDER_LAYERS_AS_REGIONS 1
+// If turned on, layers that map to a single rect are drawn as a rect
+#define RENDER_LAYERS_RECT_AS_RECT 0
/**
* Debug level for app developers.
diff --git a/libs/ui/Input.cpp b/libs/ui/Input.cpp
index eaa8926..440ec00 100644
--- a/libs/ui/Input.cpp
+++ b/libs/ui/Input.cpp
@@ -469,6 +469,16 @@
return value;
}
+ssize_t MotionEvent::findPointerIndex(int32_t pointerId) const {
+ size_t pointerCount = mPointerIds.size();
+ for (size_t i = 0; i < pointerCount; i++) {
+ if (mPointerIds.itemAt(i) == pointerId) {
+ return i;
+ }
+ }
+ return -1;
+}
+
void MotionEvent::offsetLocation(float xOffset, float yOffset) {
mXOffset += xOffset;
mYOffset += yOffset;
@@ -668,12 +678,27 @@
void VelocityTracker::clear() {
mIndex = 0;
mMovements[0].idBits.clear();
+ mActivePointerId = -1;
+}
+
+void VelocityTracker::clearPointers(BitSet32 idBits) {
+ BitSet32 remainingIdBits(mMovements[mIndex].idBits.value & ~idBits.value);
+ mMovements[mIndex].idBits = remainingIdBits;
+
+ if (mActivePointerId >= 0 && idBits.hasBit(mActivePointerId)) {
+ mActivePointerId = !remainingIdBits.isEmpty() ? remainingIdBits.firstMarkedBit() : -1;
+ }
}
void VelocityTracker::addMovement(nsecs_t eventTime, BitSet32 idBits, const Position* positions) {
if (++mIndex == HISTORY_SIZE) {
mIndex = 0;
}
+
+ while (idBits.count() > MAX_POINTERS) {
+ idBits.clearBit(idBits.lastMarkedBit());
+ }
+
Movement& movement = mMovements[mIndex];
movement.eventTime = eventTime;
movement.idBits = idBits;
@@ -682,8 +707,13 @@
movement.positions[i] = positions[i];
}
+ if (mActivePointerId < 0 || !idBits.hasBit(mActivePointerId)) {
+ mActivePointerId = count != 0 ? idBits.firstMarkedBit() : -1;
+ }
+
#if DEBUG_VELOCITY
- LOGD("VelocityTracker: addMovement eventTime=%lld, idBits=0x%08x", eventTime, idBits.value);
+ LOGD("VelocityTracker: addMovement eventTime=%lld, idBits=0x%08x, activePointerId=%d",
+ eventTime, idBits.value, mActivePointerId);
for (BitSet32 iterBits(idBits); !iterBits.isEmpty(); ) {
uint32_t id = iterBits.firstMarkedBit();
uint32_t index = idBits.getIndexOfBit(id);
@@ -691,7 +721,7 @@
float vx, vy;
bool available = getVelocity(id, &vx, &vy);
if (available) {
- LOGD(" %d: position (%0.3f, %0.3f), velocity (%0.3f, %0.3f), speed %0.3f",
+ LOGD(" %d: position (%0.3f, %0.3f), vx=%0.3f, vy=%0.3f, speed=%0.3f",
id, positions[index].x, positions[index].y, vx, vy, sqrtf(vx * vx + vy * vy));
} else {
assert(vx == 0 && vy == 0);
@@ -702,6 +732,70 @@
#endif
}
+void VelocityTracker::addMovement(const MotionEvent* event) {
+ int32_t actionMasked = event->getActionMasked();
+
+ switch (actionMasked) {
+ case AMOTION_EVENT_ACTION_DOWN:
+ // Clear all pointers on down before adding the new movement.
+ clear();
+ break;
+ case AMOTION_EVENT_ACTION_POINTER_DOWN: {
+ // Start a new movement trace for a pointer that just went down.
+ // We do this on down instead of on up because the client may want to query the
+ // final velocity for a pointer that just went up.
+ BitSet32 downIdBits;
+ downIdBits.markBit(event->getActionIndex());
+ clearPointers(downIdBits);
+ break;
+ }
+ case AMOTION_EVENT_ACTION_OUTSIDE:
+ case AMOTION_EVENT_ACTION_CANCEL:
+ case AMOTION_EVENT_ACTION_SCROLL:
+ case AMOTION_EVENT_ACTION_UP:
+ case AMOTION_EVENT_ACTION_POINTER_UP:
+ // Ignore these actions because they do not convey any new information about
+ // pointer movement. We also want to preserve the last known velocity of the pointers.
+ // Note that ACTION_UP and ACTION_POINTER_UP always report the last known position
+ // of the pointers that went up. ACTION_POINTER_UP does include the new position of
+ // pointers that remained down but we will also receive an ACTION_MOVE with this
+ // information if any of them actually moved. Since we don't know how many pointers
+ // will be going up at once it makes sense to just wait for the following ACTION_MOVE
+ // before adding the movement.
+ return;
+ }
+
+ size_t pointerCount = event->getPointerCount();
+ if (pointerCount > MAX_POINTERS) {
+ pointerCount = MAX_POINTERS;
+ }
+
+ BitSet32 idBits;
+ for (size_t i = 0; i < pointerCount; i++) {
+ idBits.markBit(event->getPointerId(i));
+ }
+
+ nsecs_t eventTime;
+ Position positions[pointerCount];
+
+ size_t historySize = event->getHistorySize();
+ for (size_t h = 0; h < historySize; h++) {
+ eventTime = event->getHistoricalEventTime(h);
+ for (size_t i = 0; i < pointerCount; i++) {
+ positions[i].x = event->getHistoricalX(i, h);
+ positions[i].y = event->getHistoricalY(i, h);
+ }
+ addMovement(eventTime, idBits, positions);
+ }
+
+ eventTime = event->getEventTime();
+ for (size_t i = 0; i < pointerCount; i++) {
+ positions[i].x = event->getX(i);
+ positions[i].y = event->getY(i);
+ }
+ addMovement(eventTime, idBits, positions);
+}
+
bool VelocityTracker::getVelocity(uint32_t id, float* outVx, float* outVy) const {
const Movement& newestMovement = mMovements[mIndex];
if (newestMovement.idBits.hasBit(id)) {
@@ -719,36 +813,17 @@
oldestIndex = nextOldestIndex;
} while (++numTouches < HISTORY_SIZE);
- // If we have a lot of samples, skip the last received sample since it is
- // probably pretty noisy compared to the sum of all of the traces already acquired.
+ // Calculate an exponentially weighted moving average of the velocity estimate
+ // at different points in time measured relative to the oldest sample.
+ // This is essentially an IIR filter. Newer samples are weighted more heavily
+ // than older samples. Samples at equal time points are weighted more or less
+ // equally.
//
- // NOTE: This condition exists in the android.view.VelocityTracker and imposes a
- // bias against the most recent data.
- if (numTouches > 3) {
- numTouches -= 1;
- }
-
- // Calculate an exponentially weighted moving average of the velocity at different
- // points in time measured relative to the oldest samples. This is essentially
- // an IIR filter.
- //
- // One problem with this algorithm is that the sample data may be poorly conditioned.
+ // One tricky problem is that the sample data may be poorly conditioned.
// Sometimes samples arrive very close together in time which can cause us to
// overestimate the velocity at that time point. Most samples might be measured
- // 16ms apart but some consecutive samples could be only 0.5sm apart due to
- // the way they are reported by the hardware or driver (sometimes in bursts or with
- // significant jitter). The instantaneous velocity for those samples 0.5ms apart will
- // be calculated to be 32 times what it should have been.
- // To work around this effect, we impose a minimum duration on the samples.
- //
- // FIXME: Samples close together in time can have an disproportionately large
- // impact on the result because all samples are equally weighted. The average should
- // instead take the time factor into account.
- //
- // FIXME: The minimum duration condition does not exist in
- // android.view.VelocityTracker yet. It is less important there because sample times
- // are truncated to the millisecond so back to back samples will often appear to be
- // zero milliseconds apart and will be ignored if they are the oldest ones.
+ // 16ms apart but some consecutive samples could be only 0.5sm apart because
+ // the hardware or driver reports them irregularly or in bursts.
float accumVx = 0;
float accumVy = 0;
uint32_t index = oldestIndex;
@@ -756,19 +831,27 @@
const Movement& oldestMovement = mMovements[oldestIndex];
const Position& oldestPosition =
oldestMovement.positions[oldestMovement.idBits.getIndexOfBit(id)];
+ nsecs_t lastDuration = 0;
while (numTouches-- > 1) {
if (++index == HISTORY_SIZE) {
index = 0;
}
const Movement& movement = mMovements[index];
nsecs_t duration = movement.eventTime - oldestMovement.eventTime;
- if (duration > MIN_DURATION) {
+
+ // If the duration between samples is small, we may significantly overestimate
+ // the velocity. Consequently, we impose a minimum duration constraint on the
+ // samples that we include in the calculation.
+ if (duration >= MIN_DURATION) {
const Position& position = movement.positions[movement.idBits.getIndexOfBit(id)];
float scale = 1000000000.0f / duration; // one over time delta in seconds
float vx = (position.x - oldestPosition.x) * scale;
float vy = (position.y - oldestPosition.y) * scale;
- accumVx = accumVx == 0 ? vx : (accumVx + vx) * 0.5f;
- accumVy = accumVy == 0 ? vy : (accumVy + vy) * 0.5f;
+
+ accumVx = (accumVx * lastDuration + vx * duration) / (duration + lastDuration);
+ accumVy = (accumVy * lastDuration + vy * duration) / (duration + lastDuration);
+
+ lastDuration = duration;
samplesUsed += 1;
}
}
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_notification_dnd.png b/packages/SystemUI/res/drawable-hdpi/ic_notification_dnd.png
new file mode 100644
index 0000000..eb783df
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_notification_dnd.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_notification_dnd.png b/packages/SystemUI/res/drawable-mdpi/ic_notification_dnd.png
index 6d4da7f..3f88968 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_notification_dnd.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_notification_dnd.png
Binary files differ
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index b4eea6e..6306d2e 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -117,19 +117,19 @@
<string name="status_bar_use_physical_keyboard">Use physical keyboard</string>
<!-- Prompt for the USB device permission dialog [CHAR LIMIT=80] -->
- <string name="usb_device_permission_prompt">Allow the application %1$s to access the USB device?</string>
+ <string name="usb_device_permission_prompt">Allow the application <xliff:g id="application">%1$s</xliff:g> to access the USB device?</string>
<!-- Prompt for the USB accessory permission dialog [CHAR LIMIT=80] -->
- <string name="usb_accessory_permission_prompt">Allow the application %1$s to access the USB accessory?</string>
+ <string name="usb_accessory_permission_prompt">Allow the application <xliff:g id="application">%1$s</xliff:g> to access the USB accessory?</string>
<!-- Prompt for the USB device confirm dialog [CHAR LIMIT=80] -->
- <string name="usb_device_confirm_prompt">Open %1$s when this USB device is connected?</string>
+ <string name="usb_device_confirm_prompt">Open <xliff:g id="activity">%1$s</xliff:g> when this USB device is connected?</string>
<!-- Prompt for the USB accessory confirm dialog [CHAR LIMIT=80] -->
- <string name="usb_accessory_confirm_prompt">Open %1$s when this USB accessory is connected?</string>
+ <string name="usb_accessory_confirm_prompt">Open <xliff:g id="activity">%1$s</xliff:g> when this USB accessory is connected?</string>
<!-- Prompt for the USB accessory URI dialog [CHAR LIMIT=80] -->
- <string name="usb_accessory_uri_prompt">Additional information for this USB accessory may be found at: %1$s</string>
+ <string name="usb_accessory_uri_prompt">Additional information for this USB accessory may be found at: <xliff:g id="url">%1$s</xliff:g></string>
<!-- Title for USB accessory dialog. Used when the name of the accessory cannot be determined. [CHAR LIMIT=50] -->
<string name="title_usb_accessory">USB accessory</string>
diff --git a/services/audioflinger/AudioPolicyManagerBase.cpp b/services/audioflinger/AudioPolicyManagerBase.cpp
index 74be4e0..f653dc5 100644
--- a/services/audioflinger/AudioPolicyManagerBase.cpp
+++ b/services/audioflinger/AudioPolicyManagerBase.cpp
@@ -339,6 +339,7 @@
LOGW("setForceUse() invalid config %d for FOR_COMMUNICATION", config);
return;
}
+ forceVolumeReeval = true;
mForceUse[usage] = config;
break;
case AudioSystem::FOR_MEDIA:
@@ -384,7 +385,7 @@
updateDeviceForStrategy();
setOutputDevice(mHardwareOutput, newDevice);
if (forceVolumeReeval) {
- applyStreamVolumes(mHardwareOutput, newDevice);
+ applyStreamVolumes(mHardwareOutput, newDevice, 0, true);
}
audio_io_handle_t activeInput = getActiveInput();
@@ -1973,7 +1974,13 @@
// offset value to reflect actual hardware volume that never reaches 0
// 1% corresponds roughly to first step in VOICE_CALL stream volume setting (see AudioService.java)
volume = 0.01 + 0.99 * volume;
+ // Force VOICE_CALL to track BLUETOOTH_SCO stream volume when bluetooth audio is
+ // enabled
+ if (stream == AudioSystem::BLUETOOTH_SCO) {
+ mpClientInterface->setStreamVolume(AudioSystem::VOICE_CALL, volume, output, delayMs);
+ }
}
+
mpClientInterface->setStreamVolume((AudioSystem::stream_type)stream, volume, output, delayMs);
}
@@ -1986,6 +1993,7 @@
} else {
voiceVolume = 1.0;
}
+
if (voiceVolume != mLastVoiceVolume && output == mHardwareOutput) {
mpClientInterface->setVoiceVolume(voiceVolume, delayMs);
mLastVoiceVolume = voiceVolume;
@@ -1995,12 +2003,12 @@
return NO_ERROR;
}
-void AudioPolicyManagerBase::applyStreamVolumes(audio_io_handle_t output, uint32_t device, int delayMs)
+void AudioPolicyManagerBase::applyStreamVolumes(audio_io_handle_t output, uint32_t device, int delayMs, bool force)
{
LOGV("applyStreamVolumes() for output %d and device %x", output, device);
for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) {
- checkAndSetVolume(stream, mStreams[stream].mIndexCur, output, device, delayMs);
+ checkAndSetVolume(stream, mStreams[stream].mIndexCur, output, device, delayMs, force);
}
}
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 267c76a..50fffd0 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -6996,8 +6996,9 @@
addErrorToDropBox("wtf", r, null, null, tag, null, null, crashInfo);
- if (Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.WTF_IS_FATAL, 0) != 0) {
+ if (r != null && r.pid != Process.myPid() &&
+ Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.WTF_IS_FATAL, 0) != 0) {
crashApplication(r, crashInfo);
return true;
} else {
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index 31d0732..e7e1fa2 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -1018,15 +1018,14 @@
return gNativeInputManager->getInputManager()->getDispatcher()->injectInputEvent(
& keyEvent, injectorPid, injectorUid, syncMode, timeoutMillis);
} else if (env->IsInstanceOf(inputEventObj, gMotionEventClassInfo.clazz)) {
- MotionEvent motionEvent;
- status_t status = android_view_MotionEvent_toNative(env, inputEventObj, & motionEvent);
- if (status) {
+ const MotionEvent* motionEvent = android_view_MotionEvent_getNativePtr(env, inputEventObj);
+ if (!motionEvent) {
jniThrowRuntimeException(env, "Could not read contents of MotionEvent object.");
return INPUT_EVENT_INJECTION_FAILED;
}
return gNativeInputManager->getInputManager()->getDispatcher()->injectInputEvent(
- & motionEvent, injectorPid, injectorUid, syncMode, timeoutMillis);
+ motionEvent, injectorPid, injectorUid, syncMode, timeoutMillis);
} else {
jniThrowRuntimeException(env, "Invalid input event type.");
return INPUT_EVENT_INJECTION_FAILED;
diff --git a/tools/velocityplot/velocityplot.py b/tools/velocityplot/velocityplot.py
new file mode 100755
index 0000000..421bed4
--- /dev/null
+++ b/tools/velocityplot/velocityplot.py
@@ -0,0 +1,289 @@
+#!/usr/bin/env python2.6
+#
+# Copyright (C) 2011 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#
+# Plots debug log output from VelocityTracker.
+# Enable DEBUG_VELOCITY to print the output.
+#
+# This code supports side-by-side comparison of two algorithms.
+# The old algorithm should be modified to emit debug log messages containing
+# the word "OLD".
+#
+
+import numpy as np
+import matplotlib.pyplot as plot
+import subprocess
+import re
+import fcntl
+import os
+import errno
+import bisect
+from datetime import datetime, timedelta
+
+# Parameters.
+timespan = 15 # seconds total span shown
+scrolljump = 5 # seconds jump when scrolling
+timeticks = 1 # seconds between each time tick
+
+# Non-blocking stream wrapper.
+class NonBlockingStream:
+ def __init__(self, stream):
+ fcntl.fcntl(stream, fcntl.F_SETFL, os.O_NONBLOCK)
+ self.stream = stream
+ self.buffer = ''
+ self.pos = 0
+
+ def readline(self):
+ while True:
+ index = self.buffer.find('\n', self.pos)
+ if index != -1:
+ result = self.buffer[self.pos:index]
+ self.pos = index + 1
+ return result
+
+ self.buffer = self.buffer[self.pos:]
+ self.pos = 0
+ try:
+ chunk = os.read(self.stream.fileno(), 4096)
+ except OSError, e:
+ if e.errno == errno.EAGAIN:
+ return None
+ raise e
+ if len(chunk) == 0:
+ if len(self.buffer) == 0:
+ raise(EOFError)
+ else:
+ result = self.buffer
+ self.buffer = ''
+ self.pos = 0
+ return result
+ self.buffer += chunk
+
+# Plotter
+class Plotter:
+ def __init__(self, adbout):
+ self.adbout = adbout
+
+ self.fig = plot.figure(1)
+ self.fig.suptitle('Velocity Tracker', fontsize=12)
+ self.fig.set_dpi(96)
+ self.fig.set_size_inches(16, 12, forward=True)
+
+ self.velocity_x = self._make_timeseries()
+ self.velocity_y = self._make_timeseries()
+ self.velocity_magnitude = self._make_timeseries()
+ self.velocity_axes = self._add_timeseries_axes(
+ 1, 'Velocity', 'px/s', [-5000, 5000],
+ yticks=range(-5000, 5000, 1000))
+ self.velocity_line_x = self._add_timeseries_line(
+ self.velocity_axes, 'vx', 'red')
+ self.velocity_line_y = self._add_timeseries_line(
+ self.velocity_axes, 'vy', 'green')
+ self.velocity_line_magnitude = self._add_timeseries_line(
+ self.velocity_axes, 'magnitude', 'blue')
+ self._add_timeseries_legend(self.velocity_axes)
+
+ shared_axis = self.velocity_axes
+
+ self.old_velocity_x = self._make_timeseries()
+ self.old_velocity_y = self._make_timeseries()
+ self.old_velocity_magnitude = self._make_timeseries()
+ self.old_velocity_axes = self._add_timeseries_axes(
+ 2, 'Old Algorithm Velocity', 'px/s', [-5000, 5000],
+ sharex=shared_axis,
+ yticks=range(-5000, 5000, 1000))
+ self.old_velocity_line_x = self._add_timeseries_line(
+ self.old_velocity_axes, 'vx', 'red')
+ self.old_velocity_line_y = self._add_timeseries_line(
+ self.old_velocity_axes, 'vy', 'green')
+ self.old_velocity_line_magnitude = self._add_timeseries_line(
+ self.old_velocity_axes, 'magnitude', 'blue')
+ self._add_timeseries_legend(self.old_velocity_axes)
+
+ self.timer = self.fig.canvas.new_timer(interval=100)
+ self.timer.add_callback(lambda: self.update())
+ self.timer.start()
+
+ self.timebase = None
+ self._reset_parse_state()
+
+ # Initialize a time series.
+ def _make_timeseries(self):
+ return [[], []]
+
+ # Add a subplot to the figure for a time series.
+ def _add_timeseries_axes(self, index, title, ylabel, ylim, yticks, sharex=None):
+ num_graphs = 2
+ height = 0.9 / num_graphs
+ top = 0.95 - height * index
+ axes = self.fig.add_axes([0.1, top, 0.8, height],
+ xscale='linear',
+ xlim=[0, timespan],
+ ylabel=ylabel,
+ yscale='linear',
+ ylim=ylim,
+ sharex=sharex)
+ axes.text(0.02, 0.02, title, transform=axes.transAxes, fontsize=10, fontweight='bold')
+ axes.set_xlabel('time (s)', fontsize=10, fontweight='bold')
+ axes.set_ylabel(ylabel, fontsize=10, fontweight='bold')
+ axes.set_xticks(range(0, timespan + 1, timeticks))
+ axes.set_yticks(yticks)
+ axes.grid(True)
+
+ for label in axes.get_xticklabels():
+ label.set_fontsize(9)
+ for label in axes.get_yticklabels():
+ label.set_fontsize(9)
+
+ return axes
+
+ # Add a line to the axes for a time series.
+ def _add_timeseries_line(self, axes, label, color, linewidth=1):
+ return axes.plot([], label=label, color=color, linewidth=linewidth)[0]
+
+ # Add a legend to a time series.
+ def _add_timeseries_legend(self, axes):
+ axes.legend(
+ loc='upper left',
+ bbox_to_anchor=(1.01, 1),
+ borderpad=0.1,
+ borderaxespad=0.1,
+ prop={'size': 10})
+
+ # Resets the parse state.
+ def _reset_parse_state(self):
+ self.parse_velocity_x = None
+ self.parse_velocity_y = None
+ self.parse_velocity_magnitude = None
+ self.parse_old_velocity_x = None
+ self.parse_old_velocity_y = None
+ self.parse_old_velocity_magnitude = None
+
+ # Update samples.
+ def update(self):
+ timeindex = 0
+ while True:
+ try:
+ line = self.adbout.readline()
+ except EOFError:
+ plot.close()
+ return
+ if line is None:
+ break
+ print line
+
+ try:
+ timestamp = self._parse_timestamp(line)
+ except ValueError, e:
+ continue
+ if self.timebase is None:
+ self.timebase = timestamp
+ delta = timestamp - self.timebase
+ timeindex = delta.seconds + delta.microseconds * 0.000001
+
+ if line.find(': position') != -1:
+ self.parse_velocity_x = self._get_following_number(line, 'vx=')
+ self.parse_velocity_y = self._get_following_number(line, 'vy=')
+ self.parse_velocity_magnitude = self._get_following_number(line, 'speed=')
+ self._append(self.velocity_x, timeindex, self.parse_velocity_x)
+ self._append(self.velocity_y, timeindex, self.parse_velocity_y)
+ self._append(self.velocity_magnitude, timeindex, self.parse_velocity_magnitude)
+
+ if line.find(': OLD') != -1:
+ self.parse_old_velocity_x = self._get_following_number(line, 'vx=')
+ self.parse_old_velocity_y = self._get_following_number(line, 'vy=')
+ self.parse_old_velocity_magnitude = self._get_following_number(line, 'speed=')
+ self._append(self.old_velocity_x, timeindex, self.parse_old_velocity_x)
+ self._append(self.old_velocity_y, timeindex, self.parse_old_velocity_y)
+ self._append(self.old_velocity_magnitude, timeindex, self.parse_old_velocity_magnitude)
+
+ # Scroll the plots.
+ if timeindex > timespan:
+ bottom = int(timeindex) - timespan + scrolljump
+ self.timebase += timedelta(seconds=bottom)
+ self._scroll(self.velocity_x, bottom)
+ self._scroll(self.velocity_y, bottom)
+ self._scroll(self.velocity_magnitude, bottom)
+ self._scroll(self.old_velocity_x, bottom)
+ self._scroll(self.old_velocity_y, bottom)
+ self._scroll(self.old_velocity_magnitude, bottom)
+
+ # Redraw the plots.
+ self.velocity_line_x.set_data(self.velocity_x)
+ self.velocity_line_y.set_data(self.velocity_y)
+ self.velocity_line_magnitude.set_data(self.velocity_magnitude)
+ self.old_velocity_line_x.set_data(self.old_velocity_x)
+ self.old_velocity_line_y.set_data(self.old_velocity_y)
+ self.old_velocity_line_magnitude.set_data(self.old_velocity_magnitude)
+
+ self.fig.canvas.draw_idle()
+
+ # Scroll a time series.
+ def _scroll(self, timeseries, bottom):
+ bottom_index = bisect.bisect_left(timeseries[0], bottom)
+ del timeseries[0][:bottom_index]
+ del timeseries[1][:bottom_index]
+ for i, timeindex in enumerate(timeseries[0]):
+ timeseries[0][i] = timeindex - bottom
+
+ # Extract a word following the specified prefix.
+ def _get_following_word(self, line, prefix):
+ prefix_index = line.find(prefix)
+ if prefix_index == -1:
+ return None
+ start_index = prefix_index + len(prefix)
+ delim_index = line.find(',', start_index)
+ if delim_index == -1:
+ return line[start_index:]
+ else:
+ return line[start_index:delim_index]
+
+ # Extract a number following the specified prefix.
+ def _get_following_number(self, line, prefix):
+ word = self._get_following_word(line, prefix)
+ if word is None:
+ return None
+ return float(word)
+
+ # Add a value to a time series.
+ def _append(self, timeseries, timeindex, number):
+ timeseries[0].append(timeindex)
+ timeseries[1].append(number)
+
+ # Parse the logcat timestamp.
+ # Timestamp has the form '01-21 20:42:42.930'
+ def _parse_timestamp(self, line):
+ return datetime.strptime(line[0:18], '%m-%d %H:%M:%S.%f')
+
+# Notice
+print "Velocity Tracker plotting tool"
+print "-----------------------------------------\n"
+print "Please enable debug logging and recompile the code."
+
+# Start adb.
+print "Starting adb logcat.\n"
+
+adb = subprocess.Popen(['adb', 'logcat', '-s', '-v', 'time', 'Input:*', 'VelocityTracker:*'],
+ stdout=subprocess.PIPE)
+adbout = NonBlockingStream(adb.stdout)
+
+# Prepare plotter.
+plotter = Plotter(adbout)
+plotter.update()
+
+# Main loop.
+plot.show()